개요

금융권에서는 내부사용자 비밀번호 관리에 관한 항목을 전자금융감독규정 제32조로 규정하고 있다. 시스템에 로그인할 때 비밀번호 입력 시 5회 이내 범위에서 입력 오류가 연속 발생한 경우 로그인을 차단하도록 되어있다. 하지만 AWS 클라우드에서는 콘솔 로그인 실패에 대한 제한 기능이 따로 제공하지 않기 때문에 Brute Force를 이용한 계정 탈취 공격에 노출될 수 있다. 따라서 Lambda 함수를 이용해 해당 기능을 만들어보자.

 

Lambda 구성 방안

 1. 사용자 계정에 콘솔 로그인 실패 횟수를 입력하는 태그 생성

 2. 콘솔 로그인 실패 횟수를 카운트하는 서버리스 함수 생성

 3. 실시간 API 호출내역에서 콘솔 로그인 실패 이벤트가 발생할 경우 트리거

 4. 동일 계정으로 5분 이내 로그인 실패 횟수를 계산하여 사용자 계정 태그 값 변경

 5. 해당 태그 값이 5 이상인 경우 계정 비활성화(모든 리소스 접근 권한 Deny)

 

Lambda 생성 및 적용

Step 1) 계정 로그인 실패 TAG 생성

 - 경로: IAM > 사용자 > 태그 관리

 - IAM 계정에 LOGIN_FAIL 태그 생성 및 Value 초기화(0)

    

Step 2) 로그인 실패 잠금 정책 생성 및 적용

 - 경로: IAM > 정책 >

 - LOGIN_FAIL 태그 값이 5 이상인 경우, 모든 Action Deny 정책 적용

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NumericGreaterThanEquals": {
                    "aws:PrincipalTag/LOGIN_FAIL": "5"
                }
            }
        }
    ]
}

 

Step 3) Lambda 정책 생성

 - 경로: IAM > 정책 >

 - Lambda 동작에 필요한 권한(LogGroup, Logging, Tagging) 부여

 - lamda_login-fail_access 정책 생성

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowLogGroup",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:280038369955:*"
        },
        {
            "Sid": "AllowLogging",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:280038369955:log-group:/aws/lambda/us-east-1_login_fail_lambda:*"
            ]
        },
        {
            "Sid": "AllowTagging",
            "Effect": "Allow",
            "Action": [
                "iam:TagUser",
                "iam:ListUserTags"
            ],
            "Resource": "arn:aws:iam::280038369955:user/*"
        }

    ]
}

 

Step 4) Lambda 역할 생성

 - 경로: IAM > 정책 >

 - Lambda에 부여할 역할 생성 및 권한 정책 추가

  - Step 2)에서 작성한 권한 정책 추가

{
  "source": ["aws.signin"],
  "detail": {
    "eventName": ["ConsoleLogin"],
    "responseElements.ConsoleLogin": ["Failure"]
  }
}

 

Step 5) Lambda 함수 생성

 - 경로: Lambda > 함수 >

 - 로그인 실패 횟수를 체크할 새로운 람다 함수 생성 (us-east-1)

    * 런타임: 파이썬, 권한: Step4) 에서 생성한 람다 역할

 

Step 6) Lambda 트리거 생성

 - 경로: CloudWatch > 이벤트 > 규칙

 - 로그인 실패 시 Lambda 함수를 동작시킬 트리거 규칙 생성 (us-east-1)

 - ConsoleLogin 실패 이벤트 패턴 작성

{
  "source": ["aws.signin"],
  "detail": {
    "eventName": ["ConsoleLogin"],
    "responseElements.ConsoleLogin": ["Failure"]
  }
}

 

Step 6) Lambda 트리거 생성

 - 경로: CloudWatch > 이벤트 > 규칙

 - 이벤트 규칙을 생성한 Lambda 함수에 적용

 

Step 7) Lambda 코드 작성

 - 경로: Lambda > 함수 >

 - 계정 별 로그인 실패 이벤트 카운트 및 태그 변경 코드 작성

import time
import boto3

# 로그인이 실패한 계정 명이 저장될 전역 변수 초기화
failed_events = {}
timeout = 300  # 5분을 초로 환산

# 로그인 실패 이벤트 핸들러
def lambda_handler(event, context):
    global failed_events

    # 로그인 실패 이벤트로부터 계정 명 가져오기
    username = event['detail']['userIdentity']['userName']

    # failed_events 에 현재 이벤트를 발생시킨 계정이 없다면 초기화
    if username not in failed_events:
        failed_events[username] = []

    # 계정 명과 함께 이벤트 발생 시간을 전역변수 목록에 추가
    current_time = time.time()
    failed_events[username].append(current_time)

    # 현재 시점 기준으로 5분 이전의 로그인 실패 이벤트 제거
    failed_events[username] = [event_time for event_time in failed_events[username] if (current_time - event_time) <= timeout]

    # 이벤트 계정의 지난 5분 동안의 로그인 실패 이벤트 횟수 계산
    count = len(failed_events[username])

    # IAM 클라이언트 객체 생성
    iam_client = boto3.client('iam')
    
    # 현재 이벤트 계정의 태그 목록 가져오기
    user_tags_response = iam_client.list_user_tags(UserName=username)
    tags = user_tags_response['Tags']
    
    # IAM 계정의 'LOGIN_FAIL' 태그 변수 지정
    tag_key = "LOGIN_FAIL"
    
    # 현재 이벤트 계정의 'LOGIN_FAIL' 태그 Value를 저장할 변수 초기화
    iam_tag_count = 0
    
    # IAM 계정에 'LOGIN_FAIL' 태그 존재 여부 확인 및 값 확인
    for tag in tags:
        print(tag.get('Key'))
        if tag.get('Key') == tag_key:
            iam_tag_count = int(tag.get('Value'))

    # 사전 조건: IAM 계정의 'LOGIN_FAIL' 태그 값이 5 이상 일 경우 모든 액세스 권한 DENY 정책 적용
    # 'LOGIN_FAIL' 태그 값이 5 미만 일 경우 failed_events에서 확인한 COUNT를 IAM 계정의 태그 값으로 변경
    # 'LOGIN_FAIL' 태그 값이 5 이상 일 경우 IAM 계정의 태그 값에 1을 더한 값으로 변경
    if iam_tag_count >= 5:
        iam_client.tag_user(UserName=username, Tags=[{'Key': tag_key, 'Value': str(iam_tag_count+1)}])
        print(f"User: {username} 의 최근 5분 동안 로그인 실패 횟수가 5회 이상이므로 모든 액세스가 제한됩니다.")
    else:
        iam_client.tag_user(UserName=username, Tags=[{'Key': tag_key, 'Value': str(count)}])
        print(f"User: {username} 의 최근 5분 동안 로그인 실패 횟수는 {count}회 입니다.")

 

Step 8) Lambda 테스트 이벤트 생성

 - 경로: Lambda > 함수 > 테스트 이벤트

 - 테스트 수행 및 결과 확인

{
  "version": "0",
  "id": "",
  "detail-type": "AWS Console Sign In via CloudTrail",
  "source": "aws.signin",
  "account": "",
  "time": "2023-04-21T07:08:40Z",
  "region": "us-east-1",
  "resources": [],
  "detail": {
    "eventVersion": "1.08",
    "userIdentity": {
      "type": "IAMUser",
      "principalId": "",
      "accountId": "",
      "accessKeyId": "",
      "userName": "12345678"
    },
    "eventTime": "2023-04-21T07:08:40Z",
    "eventSource": "signin.amazonaws.com",
    "eventName": "ConsoleLogin",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "0.0.0.0",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
    "errorMessage": "Failed authentication",
    "requestParameters": null,
    "responseElements": {
      "ConsoleLogin": "Failure"
    },
    "additionalEventData": {
      "LoginTo": "https://console.aws.amazon.com/console/home?hashArgs=%23&isauthcode=true&state=hashArgsFromTB_us-east-2_07a754f6c8efb02b",
      "MobileVersion": "No",
      "MFAUsed": "No"
    },
    "eventID": "1b4eb2f7-bd36-429d-beb7-feb02fa47f70",
    "readOnly": false,
    "eventType": "AwsConsoleSignIn",
    "managementEvent": true,
    "recipientAccountId": "123456789999",
    "eventCategory": "Management",
    "tlsDetails": {
      "tlsVersion": "TLSv1.2",
      "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
      "clientProvidedHostHeader": "us-east-1.signin.aws.amazon.com"
    }
  }
}
복사했습니다!