Site icon GRIP.News

AWS, CloudFormation 을 사용한 WAF 자동 구축 및 운영

완벽한 개발은 존재할 수 없다. 그리고, 나도 모르게 내가 사용중인 미들웨어 어딘가에서 보안 문제가 생기는 경우가 부지기수다. WAF(Web Application Firewall)은 Web 서비스를 제공함에 있어서 필수불가결한 존재라 생각한다. 특히나, 다양한 매개변수를 주고 받는 서비스라면 더욱이.

이미 Query String 의 헛점을 통한 Injection 공격을 수차례 당한지라, WAF 에 대한 관심이 상당히 많았다. 비용이 많이 들어가도 되는 서비스는 SK인포섹이나, 안랩의 와플같은 솔루션을 사용했었다. 문제는 Cloud 로 넘어오면서 부터다.

AWS 를 처음 사용했을 때가 2013년 경이다. 말도 많고 탈도 많았다. AWS 에서 Web 기반의 서비스나 API 를 개발할 때 가장 우려했던게 바로 보안이다. Security Group 말고 트래픽을 보호할 수 있는 방법이 딱히 없었던 것. 그래도 CloudFlare 를 알게 되면서 한시름 놨지만 CloudFlare 는 플랜을 올리지 않으면 룰에 대한 제약이 있다. 그리고 느리다. 난 그보다 더 이전에 Naxsi 를 사용해 봤고, 이걸 이미 다른 서비스에 적용해 본 경험이 있었다. Naxsi 덕분에 어느정도 대응이 가능했지만.

WAF(Web Application Firewall) Naxsi

 

AWS WAF Service 는 2015년 개시됐다. 처음엔 너무 빈약한 기능에 사용하려면 손이 많이 갔기에 쓰지 않았지만,”AWS WAF 보안 자동화 솔루션“이라는 쉽게 AWS WAF 를 사용할 수 있도록 도와주는 CloudFormation Template이 나온다음 부터 사용성이 급격히 좋아졌다.

이 Template 는 기본적인 AWS WAF 운영에 필요한 ACL, Lambda Function, API Gateway 등을 알아서 만들어준다. 덕분에 1.0 부터 잘 사용하고 있었는데 어느덧 2.3 버전이 릴리즈됐다. (2019년 5월 1일) 이와 더불어 작년 말(2018년 12월) AWS WAF를 위한 모니터링 대시보드를 제공하기 시작함으로서 활용성은 더 나아졌다. 전문적인 지식이 없어도 WAF 환경을 구축하고 운영하는데 문제가 없는 정도.

 

모니터링 대시보드 제공 및 템플릿 통합


AWS WAF의 가장 큰 단점은 현황을 알 수 없어 ‘답답하다’ 였다. 작년 말 추가된 CloudWatch 에선 AllowedRequestsBlockRequests 현황을 모니터링할 수 있게 되었다.

 

2.x 의 가장 큰 변화는 CloudFront 와 ALB를 위한 Template이 통합된 것. 정확히는 중첩 스택 형태로, 동일한 구성 요소를 선언하는 공통 패턴을 하나로 만들어 놨다. 중첩 스택 형태는 아래 Template를 참고하자. 이 URL 은 CloudFormation 에서 사용되니 복사해 놓을 것.

AlbStack:
  Type: 'AWS::CloudFormation::Stack'
  Condition: AlbEndpoint
  DependsOn: CheckRequirements
  Properties:
    TemplateURL: !Sub
      - 'https://s3.amazonaws.com/${S3Bucket}-${AWS::Region}/${KeyPrefix}/aws-waf-security-automations-alb.template'
      -
        S3Bucket: !FindInMap ["SourceCode", "General", "S3Bucket"]
        KeyPrefix: !FindInMap ["SourceCode", "General", "KeyPrefix"]
    Parameters:
      ActivateSqlInjectionProtectionParam: !Ref ActivateSqlInjectionProtectionParam
      ActivateCrossSiteScriptingProtectionParam: !Ref ActivateCrossSiteScriptingProtectionParam
      ActivateHttpFloodProtectionParam: !Ref ActivateHttpFloodProtectionParam
      ActivateScannersProbesProtectionParam: !Ref ActivateScannersProbesProtectionParam
      ActivateReputationListsProtectionParam: !Ref ActivateReputationListsProtectionParam
      ActivateBadBotProtectionParam: !Ref ActivateBadBotProtectionParam
      AppAccessLogBucket: !Ref AppAccessLogBucket
      WafApiType: 'waf-regional'
      WafArnPrefix: !Sub 'arn:aws:waf-regional:${AWS::Region}:'
      ParentStackName: !Ref 'AWS::StackName'
      WafLogBucket: !If [HttpFloodProtectionLogParserActivated, !Ref WafLogBucket, '']
      GlueAccessLogsDatabase: !If [AthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.GlueAccessLogsDatabase, '']
      GlueAppAccessLogsTable: !If [ScannersProbesAthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.ALBGlueAppAccessLogsTable, '']
      GlueWafAccessLogsTable: !If [HttpFloodAthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.GlueWafAccessLogsTable, '']

CloudFrontStack:
  Type: 'AWS::CloudFormation::Stack'
  Condition: CloudFrontEndpoint
  DependsOn: CheckRequirements
  Properties:
    TemplateURL: !Sub
      - 'https://s3.amazonaws.com/${S3Bucket}-${AWS::Region}/${KeyPrefix}/aws-waf-security-automations-cloudfront.template'
      -
        S3Bucket: !FindInMap ["SourceCode", "General", "S3Bucket"]
        KeyPrefix: !FindInMap ["SourceCode", "General", "KeyPrefix"]
    Parameters:
      ActivateSqlInjectionProtectionParam: !Ref ActivateSqlInjectionProtectionParam
      ActivateCrossSiteScriptingProtectionParam: !Ref ActivateCrossSiteScriptingProtectionParam
      ActivateHttpFloodProtectionParam: !Ref ActivateHttpFloodProtectionParam
      ActivateScannersProbesProtectionParam: !Ref ActivateScannersProbesProtectionParam
      ActivateReputationListsProtectionParam: !Ref ActivateReputationListsProtectionParam
      ActivateBadBotProtectionParam: !Ref ActivateBadBotProtectionParam
      AppAccessLogBucket: !Ref AppAccessLogBucket
      WafApiType: 'waf'
      WafArnPrefix: 'arn:aws:waf::'
      ParentStackName: !Ref 'AWS::StackName'
      WafLogBucket: !If [HttpFloodProtectionLogParserActivated, !Ref WafLogBucket, '']
      GlueAccessLogsDatabase: !If [AthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.GlueAccessLogsDatabase, '']
      GlueAppAccessLogsTable: !If [ScannersProbesAthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.CloudFrontGlueAppAccessLogsTable, '']
      GlueWafAccessLogsTable: !If [HttpFloodAthenaLogParser, !GetAtt FirehoseAthenaStack.Outputs.GlueWafAccessLogsTable, '']

 

CloudFormation 스택 생성


이미 만들어진 Template 가 있기 때문에 CloudFormation 에서 URL 추가만으로 쉽게 조건을 형성할 수 있다.

 

EndPoint 형식은 ALB 와 CloudFront 를 지원한다. 그리고 log 를 저장할 S3 Bucket 이름만 입력하면 스택 생성이 완료 된다. 다른거 건드릴 필요가 없다.

 

Lambda 부터 API Gateway 까지 만들었다면 생성 완료된 상태.

 

AWS WAF Service 내 WebACLs 를 클릭해 보면, 위에서 생성한 리소스를 확인할 수 있다.

 

그리고 사용중인 ALB 에 반영하면 끝.

 

인젝션 테스트를 해 보면, 알아서 잘 막아준다.

 

WebACL


Base Rules
Whitelist Rule
Blacklist Rune
HTTP Flood Rule
Scanners & Probes Rule
WAF IP Reputation Lists Rule
SQL Injection
XSS Rule

기본적으로 8개의 규칙을 갖고있다. WebACL 은 10개의 규칙만 사용할 수 있기 때문에 2개의 추가적인 규칙 추가가 가능해 졌다.

 

마무리하며


언제나 내가 개발한 프로그램을 대중에게 공개할 때 가장 걱정되는건, 예기치 못한 부분에서 주요 정보가 새어 나가는것이었다. (서비스 품질이야 기획단계의 문제가 아니겠는가! ㅎㅎ) DevOps 구조로 가면서 시스템의 보안 역시 개발자 영역이 되었다 생각하기 때문에 방화벽, 보안 부분은 공부해 둘 필요가 있다.

기존 AWS WAF Service를 제대로 사용하려면 부가적인 서비스/모듈을 사용해야 하기 때문에 거부담이 있었다면, CloudFormation + 자동화 Template 는 기본 ‘판’을 만들어 줄 것이기 때문에 기초를 몰라도 WAF 를 쉽게 사용할 수 있도록 도와줄 거라 생각한다.

Exit mobile version