Amazon Web Services 한국 블로그

AWS Lambda와 WAF를 이용한 Rate-Based Blacklisting 기능 구현

여러분들 대다수는 원하지 않는 요청이나, crawl-delay directive의 기준을 무시하는 봇이나 크롤러 같은 스캐닝 도구로 인해 웹서버의 가용성에 문제가 발생하는 경우를 고민해 보셨을 것입니다. 흔히 HTTP flood 라고 불리는 분산 서비스 거부 공격(DDoS)의 목적은 시스템에 부담을 일으켜서 정상 사용자의 사용을 방해하기 위한(아래 그림 처럼) 것입니다.

이 글에서는 원하지 않는 트래픽을 요청량 기준으로 자동으로 탐지하고 AWS WAF (현재 WAF의 기능은 Amazon CloudFront 의 컨텐츠 배송 서비스 상에서 동작합니다)의 구성을 업데이트하여 악의의 사용자로부터의 요청을 차단하는 방법을 소개합니다.

이와 같은 기능을 AWS Lambda를 사용하여 CloudFront의 엑세스 로그에서 악성 요청을 식별하도록 구현하게 됩니다. 이 기능은 Amazon CloudWatch 상의 메트릭들을 활용하여 얼마나 많은 요청들이 처리되었고, 블락된 오리진의 수가 얼마인지를 모니터합니다. 또한 AWS WAF서비스는 수작업으로 잘 알려진 봇 네트웍 같이 금지하고 싶은 IP 대역을 추가할 수도 있습니다.

위 그림은 모든 요청을 처리하다가 웹서버의 모든 리소스를 소진하는 모양을 나타냅니다. 아래 그림은 본 포스팅에서 제시된 방법을 통해 블랙리스트 처리된 소스로부터의 요청을 차단하는 과정을 도식화 한 것입니다.

솔루션 개요

여러분의 리소스에 대한 부담을 방지하기 위한 방식은 요청률 기반 블랙리스팅(rate-based blacklisting)을 구현하는 것입니다. 이를 통해 웹 어플리케이션 환경이 얼마나 많은 요청들을 처리할 수 있는지에 대한 임계치를 세울 수 있습니다. 만약, 봇, 크롤러 혹은 임계치를 상회하는 공격이 들어온다면 AWS WAF를 통해 자동으로 차단될 것입니다.

위의 그림에서 회색 박스에 포함된 영역이 이 기능을 수행하는 AWS서비스들입니다. 시나리오를 위해 CloudFront와 Amazon S3, Amazon EC2, Amazon RDS를 사용하여 웹사이트의 정적, 동적 컨텐츠를 제공하도록 구성합니다. CloudFront가 웹 어플리케이션에 대한 요청을 받을 때마다, AWS WAF는 모든 요청들을 검사하고, 해당 요청을 허용할 지, 차단할지를 소스 IP기준으로 CloudFront에 설정합니다. 아래 그림은 전체적인 아키텍쳐와 동작 순서를 보여줍니다.

  1. CloudFront 가 웹어플리케이션 앞 단에서 요청들을 받고, 요청에 대한 구체적인 정보를 담아서 S3버킷으로 access logs를 보낸다.
  2. S3버킷에 신규 엑세스 로그들이 저장될 때 마다, 람다 펑션이 기동된다.
  3. 람다 펑션은 어떤 IP로부터 기준치 이상의 요청이 들어 왔는지를 분석하고, AWS WAF 블락 리스트에 추가한다. AWS WAF는 지정된 기간동안 해당 IP를 블락한다. 이 지정된 기간이 끝나면, AWS WAF는 다시 해당 IP로부터의 요청을 허용한다. 단, 해당 IP로부터의 트래픽을 모니터링하는 것은 계속된다.
  4. 람다 펑션은 분석된 요청들의 갯수나 블락된 IP주소의 갯수 같은 CloudWatch 메트릭들을 퍼블리싱 한다.

다음은 AWS 서비스들이 어떤 방식으로 정해진 작업들을 수행하는지를 설명합니다.:

  • AWS WAF – 수정 가능한 웹 보안 규칙을 정의하고 이것을 통해 웹 어플리케이션으로의 트래픽을 허용하거나 차단하는 기능을 제공합니다. 이 서비스에서는 아래와 같이 3가지 규칙을 사용하게 됩니다.:
    1. 자동 블락킹 – 이 규칙은 악성 요청으로 식별된 IP주소를 추가합니다. 지금 시나리오에서는 이 규칙을 설정함과 동시에, 블락리스트된 IP주소들에서 들어오는 모든 신규 요청들이 드랍되기 시작하고, 람다가 지정된 폐기 기간(디폴트로 4 시간 설정됨)이 지나 해당 IP를 블락리스트에서 제거하기 전까지 유지됩니다.
    2. 수동 블락킹 – 이 규칙은 수작업으로 특정 IP 주소를 블락 리스트에 추가하는 데 사용됩니다. 해당 IP주소는 관리자가 수작업으로 리스트에서 제거하기 전까지 계속 블락됩니다.
    3. 자동 카운트 – 이것은 일종의 휴지통 개념입니다. 지정된 IP로부터의 요청이 바로 블락킹되지는 않고, 준 실시간성으로 요청 갯수가 트랙킹됩니다. 이를 통해 자동 블락 리스트에서 제거된 이후 해당 IP의 행동에 대한 가시성을 제공해 줄 수 있습니다.
  • Lambda – 서버를 준비할 필요없이 코드를 실행시켜 주는 서비스 입니다. 단지 여러분의 코드를 업로드하면 람다에서 알아서 실행에 필요한 모든 것을 담당합니다. 람다를 통해 다른 AWS서비스들이 행동기반으로 자동으로 기동되도록 작성될 수 있습니다. 이번 시나리오에서는 S3에 엑세스 로그 파일이 업로드 될때 마다 람다 펑션이 실행되도록 구성하였습니다. 이 기능은 해당 로그 파일내 공격자IP들을 식별하고 그것들을 AWS WAF로 블락킹 처리하도록 하는 과정을 담당합니다.
  • CloudFront – 컨텐츠 배포 웹 서비스로서, 다른 AWS서비스들을 통합하여 낮은 레이턴시와 높은 전송 속도로 컨텐츠를 배포할 수 있는 쉬운 방법을 제공합니다. CloudFront는 기본적으로 한시간 내로 엑세스 로그를 제공합니다. 그러나 어떤 로그는 지연될 수 있습니다. 이 시나리오에서 CloudFront는 웹 어플리케이션의 동적, 정적 컨텐츠를 제공하게 됩니다.
  • S3 – 높은 가용성, 확장성, 내구성과 낮은 레이턴시를 가진 저장소를 저렴한 비용으로 이용할 수 있도록 합니다.  이번 시나리오에서는 CloudFront의 엑세스 로그파일이 지정된 S3버킷에 저장되도록 구성됩니다.
  • CloudWatch – AWS상에서 운영되는 어플리케이션과 각종 리소스들에 대한 모니터링 서비스 입니다. 이번 시나리오에서는 람다 펑션에 의해 처리되는 요청의 갯수와 블락된 IP주소의 갯수에 대한 메트릭을 트랙킹하게 됩니다.
  • AWS CloudFormation – AWS인프라의 전개를 주기적으로 혹은 반복적으로 수행할 수 있도록 해주는 서비스입니다. CloudFormation을 통해 필요한 모든 리소스와 상관관계를 하나의 템플릿 파일로 정의할 수 있습니다. 이번 시나리오에서는 필요한 전체 솔루션 스택을 한번에 쉽게 구성할 수 있도록 준비하였습니다.

이번 시나리오에서는 S3버킷, 분당 요청수 제한기준, 블랙리스트에 IP주소를 담아두는 기간 등을 설정하게 됩니다.

AWS 관리 콘솔을 통해 진행하기

시작하기 앞서, 컨텐츠를 제공할 CloudFront 배포지점이 이미 있다고 가정하겠습니다. 만약 없다면, 다음 절차(Creating or Updating a Web Distribution Using the CloudFront Console)대로 만들어 보십시오. 본 시나리오에선 필요한 환경을 간단히 구축하기 위해 CloudFromation 템플릿을 활용해 보도록 하겠습니다. 관련 내용이 알고 싶으시면 이 내용(CloudFormation User Guide)을 참고하시기 바랍니다.

단계 1: CloudFromation 템플릿을 사용하여 환경 구축

  1. CloudFormation console로 갑니다.
  2. 각자 적합한 리전으로 변경합니다. CloudFormation을 사용하게 되면 모든 리소스들이 CloudFormation템플릿을 생성했던 리전에 생성됩니다. 이번 시나리오에서는 람다를 활용하기 때문에, 어느 리전이 람다 활용이 가능한지 확인하기 위해 AWS Global Infrastructure Region Table 을 체크합니다.
  3. 신규 스택 생성(Create New Stack) 버튼을 클릭합니다.
  4. 템플릿 선택화면에서, GitHub(this GitHub repository)에서 waf_template.json를 찾아서 업로드 합니다.
  5. 상세 내역 화면에서, (아래 화면 예시 처럼):
    1. Stack name란에, 적당한 스택이름을 줍니다. 스택이름을 너무 길게 주는 경우, 람다펑션을 실행 할때 에러가 날 수도 있습니다.
    2. Create CloudFront Access Log Bucket란에서 CloudFront Access Log를 위한 버킷을 만들려면 yes를 클릭하고, 기존 버킷을 활용하려면 no를 선택합니다.
    3. CloudFront Access Log Bucket Name란에 CloudFront가 엑세스 로그를 넣기 위한 S3버킷의 이름을 지정합니다. 만약 b 단계에서 no 를 선택했다면 공란으로 놔둡니다.
    4. Request Threshold란에 분당 기준으로 블락킹 없이 제공될 수 있는 최대 요청 갯수를 지정합니다.
    5. WAF Block Period란에 초단위로 임계치를 넘은 IP주소에 대해 얼마동안 블라킹을 할 것인지 설정합니다.
    6. WAF Quarantine Period란에 AWS WAF가 블락킹에서 해제된 IP를 얼마동안 모니터링 할 것인지 설정합니다.
    7. Options 페이지에서 Next를 클릭합니다.
    8. Review 페이지에서 ‘I acknowledge that this template might cause AWS CloudFormation to create IAM resources‘ 체크 박스를 체크하고 Create를 클릭합니다.

이 템플릿으로 이번 시나리오를 실행하는데 필요한 모든 요소들이 마련됩니다: 람다 펑션, 모든 필요한 규칙들이 설정되어 있는AWS WAF Web ACL ( Malicious Requesters라는 이름으로), CloudWatch 커스텀 메트릭, 그리고 만약 Create CloudFront Access Log Bucket란에서 yes를 선택했다면 CloudFront Access Log Bucket Name 란에 주어진 이름으로 버킷이 생성될 것입니다.

단계2: CloudFront 배포 설정 업데이트

AWS WAF를 활성화 시키고, 전 단계에서 생성했던 버킷을 가지고 로깅을 하기 위해 CloudFront 배포 설정을 업데이트 합니다. (만약 이미 CloudFront Access Log를 위한 S3버킷을 가지고 있다면 이번 단계를 생략합니다):

  1. CloudFront console을 열고 업데이트 대상 배포지점을 선택합니다.
  2. Distribution Settings  화면에서, General 탭을 선택하고 Edit를 클릭합니다.
  3. AWS WAF Web ACL 설정을 업데이트 합니다(아래 그림처럼). 옵션 메뉴에는 모든 활성화된 Web ACL들이 나타나는데, 여기서 단계 1에서 생성했던 것(Malicious Requesters)을 선택합니다.
  4. Logging란에 On을 선택합니다.
  5. Bucket for Logs란에 단계 1에서 지정했던 버킷을 선택합니다.
  6. 변경내용을 저장합니다.

만약 CloudFront Access Log를 위한 S3버킷을 이미 가지고 있다면, 신규 로그파일이 해당 버킷에 추가될 때마다 람다 펑션을 기동시키기 위해 S3 이벤트 통보 기능을 활성화 합니다(more details을 참조하세요). 이를 위해, S3 console 을 오픈하고 버킷 속성에서 다음 그림에서 표시된 부분처럼 설정해 줍니다. 만약 람다 펑션 설정 부분이 비 활성화 되어 있다면, 해당 버킷이 CloudFormation팀플릿을 실행했던 리전인지를 확인하기 바랍니다.

단계 3: [생략 가능] CloudFormation 파라메터 수정

만약 단계 1에서 CloudFormation 스택을 생성한 뒤에 설정했던 파라메터 값을 변경하고자 한다면(예를 들어, IP 관련된 블락킹 기간 등을 바꾸고자 하는 경우), 새로 스택을 만들 필요없이 아래 절차대로 변경하면 반영됩니다:

  1. CloudFormation console에서 스택 목록 중 원하는 것을 선택합니다.
  2. Actions 을 클릭하고Update Stack.을 선택합니다.
  3. Select Template 페이지에서, Use the current template을 선택하고 Next를 클릭합니다.
  4. Specify Details 페이지에서, Rate-Based Blacklisting Parameters값을 원하는 값으로 변경합니다(예를 들자면 아래 그림처럼):
  5. Options 페이지에서, Next를 클릭합니다.
  6. Review 페이지에서, I acknowledge that this template might cause AWS CloudFormation to create IAM resources 체크박스를 클릭하고 Update를 클릭합니다.

CloudFormation 은 새로운 파라메터 값으로 스택을 업데이트 하게 됩니다.

AWS CLI 이용하여 스택을 생성하는 방법

이외에도, 여러분들은 AWS CLI를 써서 스택을 생성할 수도 있습니다. 아래 코드는 스택을 만드는 한가지 샘플입니다.(red로 표시되는 부분이 변경될 부분입니다).

aws cloudformation create-stack --stack-name <STACK_NAME> --template-body file:///<PATH_TO>/waf_template.json --capabilities CAPABILITY_IAM --parameters ParameterKey=CloudFrontCreateAccessLogBucket,ParameterValue=yes ParameterKey=CloudFrontAccessLogBucket,ParameterValue=<LOG_BUCKET_NAME> ParameterKey=RequestThreshold,ParameterValue=400 ParameterKey=WAFBlockPeriod,ParameterValue=1400 ParameterKey=WAFQuarantinePeriod,ParameterValue=14400

또한 this GitHub repository에서 waf_template.json 을 찾을 수 있습니다.

테스팅

이번 포스팅에서 제시된 내용을 테스트 해보기 위해서는 CloudFront 가 새 로그파일을 생성할 때 까지 기다려야 합니다. 이런 대기시간이 싫다면, this sample access log file 링크에 있는 샘플을 가지고 바로 S3 버킷에 올려서 테스트 할 수도 있습니다. 업로딩이 끝나고, IP 주소값들이 자동으로AWS WAF의 Auto Block Set 섹션에 들어가 있는지 확인해 보시기 바랍니다.

그리고 CloudWatch 메트릭이 업데이트 되어 있는지도 확인해 보시기 바랍니다. 또한 람다가 로그파일을 분석하는데 몇 초의 시간이 소요되고  CloudWatch가 신규 메트릭 정보를 보여주는 데 최대 2분 정도 소요된다는 점을 알고 계시기 바랍니다. 아래 그림은 Auto Block Set 섹션에 람다에서 처리했던 IP주소들이 어떻게 들어 있는 지를 보여줍니다.

CloudWatch가 어떻게 메트릭 정보를 표시하는지 보기 위해, CloudWatch dashboard를 참조하시면 좋을 것 같습니다.

요약

본 포스팅에서, 여러분들은 지정된 기준 값 이상으로 과다하게 요청하는 특정 IP주소를 자동으로 블락킹 하는 방법에 대해 살펴보았습니다. 여기에서 제시된 Lambda script 를 가지고 여러분들의 상황에 맞게 변경하여 활용하셔도 됩니다. 예를 들어 CloudFront 가 생성하는 데이터를 분석할 수도 있겠습니다(sc-bytes of access log file 같은 정보 등을 분석하여 관련 요청을 블락킹 하는 식으로)

만약 여러분들이 본 포스팅에 대해 질문이나 코멘트가 있다면, 아래 “Comments” 섹션이나 AWS WAF forum에 남겨주시기 바랍니다.

– Heitor

본 글은 AWS Security Blog의 How to Configure Rate-Based Blacklisting with AWS WAF and AWS Lambda에 대한 번역으로 AWS코리아의 보안 분야 솔루션 아키텍트로 일하시고 있는 임기성님께서 작성해 주셨습니다.