🐿️

【AWS】AWS Security Hubフィルタリング結果を管理者に届けるCloudFormationテンプレート

2023/06/28に公開


はじめに

ご覧いただきありがとうございます。阿河です。

AWSには便利なセキュリティサービスが用意されています。

例えばAmazon GuardDutyは、悪意のあるアクティビティや不正な行動を継続的にモニタリングする脅威検出サービスです。ただし有効化するだけで終わって、検知結果を踏まえて実際に対処や改善を行うまでは触手が伸びていないという方もいらっしゃるのではないのでしょうか。

今回は一元的なセキュリティチェックが可能なAWS Security Hubの検知結果を、重要度別にフィルタリングして定期的に管理者に届けることで、セキュリティ意識を高めて改善のステップに繋げる一助になればと思い、本記事を書かせていただきました。

対象者

  • AWSを運用中
  • 運用を自動化したい

概要

  1. 前提環境
  2. CloudFormation実行のための準備/実行
  3. 挙動結果の確認

1. 前提環境

  • Security Hubを有効化済

https://docs.aws.amazon.com/securityhub/latest/userguide/fsbp-standard.html 
セキュリティ基準は「AWS Foundational Security Best Practices (FSBP) standard」を有効化。こちらはドキュメントにあるように、AWSアカウントとリソースがセキュリティのベストプラクティスから逸脱した場合に検出する一連のコントロールです。

2. CloudFormation実行のための準備/実行

フィルタリング対象の選定

https://docs.aws.amazon.com/securityhub/1.0/APIReference/API_AwsSecurityFinding.html

マネジメントコンソールの「findings」のページで、フィルタリングを試すことができます。

今回のフィルタリング対象は

  • Severity Label(検出結果の重大度。CRITICAL/HIGH/MEDIUM/LOWそれぞれ確認)
  • Compliance Status(チェック結果。FAILEDのみを指定)
  • Record State(Findingの記録状態。ACTIVEを指定)
  • Product Name(結果を生成したプロダクトの名前。今回はSecurity Hubを指定)

の4つでフィルタリングを行います。

YAMLファイルを用意

YAMLファイルの中身は以下です。

AWSTemplateFormatVersion: "2010-09-09"

Parameters:

  AdminMailAdress:
    Type: String


Resources:

  # Configuring SNS Topic
  SNSTopic:
    Type: "AWS::SNS::Topic"
    Properties:
      TopicName: "Topic-For-SecuriyHub-Report"

  # Configuring SNS Subscription
  Subscription:
    Type: "AWS::SNS::Subscription"
    Properties:
      TopicArn: !Ref SNSTopic
      Endpoint: !Ref AdminMailAdress
      Protocol: "email"
  

  # Role of Lambda Function
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonSNSFullAccess
        - arn:aws:iam::aws:policy/AWSSecurityHubFullAccess

  # Configuring Lambda Function
  Function1:
    Type: AWS::Lambda::Function
    DependsOn:
      - SNSTopic
    Properties:
      Environment:
        Variables:
          TOPIC: !Ref SNSTopic
      Code:
        ZipFile: |
          import json
          import boto3
          import os

          topic = os.environ['TOPIC']

          client = boto3.client('securityhub', region_name="ap-northeast-1")
          sns_client = boto3.client('sns')
          topic_arn = topic

          def calc(iterator):
              sum_list = []

              for page in iterator:
                  sum_list.append(len(page['Findings']))
              
              sum = 0
              for i in sum_list:
                  sum = sum + i
              return sum

          def lambda_handler(event, context):
              paginator = client.get_paginator('get_findings')
    
              page_iterator_critical = paginator.paginate(
        
                  Filters={
                  'SeverityLabel': [
                      {
                          'Value': 'CRITICAL',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ComplianceStatus': [
                      {
                          'Value': 'FAILED',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'RecordState': [
                      {
                          'Value': 'ACTIVE',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ProductName': [
                      {
                          'Value': 'Security Hub',
                          'Comparison': 'EQUALS'
                      },
                  ]
                  }
              )
    
              page_iterator_high = paginator.paginate(
        
                  Filters={
                  'SeverityLabel': [
                      {
                          'Value': 'HIGH',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ComplianceStatus': [
                      {
                          'Value': 'FAILED',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'RecordState': [
                      {
                          'Value': 'ACTIVE',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ProductName': [
                      {
                          'Value': 'Security Hub',
                          'Comparison': 'EQUALS'
                      },
                  ]
                  }
              )
    
              page_iterator_medium = paginator.paginate(
        
                  Filters={
                 'SeverityLabel': [
                     {
                         'Value': 'MEDIUM',
                         'Comparison': 'EQUALS'
                     },
                  ],
                  'ComplianceStatus': [
                      {
                          'Value': 'FAILED',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'RecordState': [
                      {
                          'Value': 'ACTIVE',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ProductName': [
                      {
                          'Value': 'Security Hub',
                          'Comparison': 'EQUALS'
                      },
                  ]
                  }
              )
    
              page_iterator_low = paginator.paginate(
        
                  Filters={
                  'SeverityLabel': [
                      {
                          'Value': 'LOW',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ComplianceStatus': [
                      {
                          'Value': 'FAILED',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'RecordState': [
                      {
                          'Value': 'ACTIVE',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ProductName': [
                      {
                          'Value': 'Security Hub',
                          'Comparison': 'EQUALS'
                      },
                  ]
                  }
              )
    
              critical = calc(page_iterator_critical)
              high = calc(page_iterator_high)
              medium = calc(page_iterator_medium)
              low = calc(page_iterator_low)
    
              mail_title = "Security Report"
              message = f"CRITICAL: {critical}\nHIGH: {high}\nMEDIUM: {medium}\nLOW: {low}\n\n\n\nhttps://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/fsbp-standard.html\nAWS Foundational Security Best Practices (FSBP)"
              mail = sns_client.publish(
        
                      TopicArn=topic_arn,
                      Subject=mail_title,    
                      Message=message
                    )     
              return 0

      FunctionName: "get-security-hub-report"
      Handler: "index.lambda_handler"
      MemorySize: 128
      Timeout: 20
      PackageType: Zip
      Runtime: "python3.9"
      Role: !GetAtt LambdaRole.Arn

  # Configuring EventBridge
  CFnEventRule:
    Type: AWS::Events::Rule
    DependsOn:
      - Function1
    Properties:
      ScheduleExpression: rate(1 hour)
      State: ENABLED
      Name: "security-hub-report"
      Targets: 
        - Arn: !GetAtt Function1.Arn
          Id: !Ref Function1

  # Lambda Permission
  LambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn:
      - CFnEventRule
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !GetAtt Function1.Arn
      Principal: 'events.amazonaws.com'
      SourceArn: !GetAtt CFnEventRule.Arn

何点か補足します。

  • Security Hubの検知結果のフィルタリングには、Lambdaを使用しております。
 page_iterator_critical = paginator.paginate(
        
                  Filters={
                  'SeverityLabel': [
                      {
                          'Value': 'CRITICAL',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ComplianceStatus': [
                      {
                          'Value': 'FAILED',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'RecordState': [
                      {
                          'Value': 'ACTIVE',
                          'Comparison': 'EQUALS'
                      },
                  ],
                  'ProductName': [
                      {
                          'Value': 'Security Hub',
                          'Comparison': 'EQUALS'
                      },
                  ]
                  }
              )
    

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/securityhub/client/get_findings.html
フィルタリングの内容は、環境に合わせて変更してください。

  • EventBridgeでは、1時間ごとにメールを送るようにセッティングしてあります。
 # Configuring EventBridge
  CFnEventRule:
    Type: AWS::Events::Rule
    DependsOn:
      - Function1
    Properties:
      ScheduleExpression: rate(1 hour)
      State: ENABLED
      Name: "security-hub-report"
      Targets: 
        - Arn: !GetAtt Function1.Arn
          Id: !Ref Function1

あくまで検証のために高頻度で送るように設定しておりますが、環境に合わせて「ScheduleExpression」を調整してください。

CloudFormationの実行

マネジメントコンソールの「CloudFormation」ページから、「Create Stack」に進みます。

「Upload a template file」で、YAMLファイルをアップロードします。

「Specify stack details」のページでは、スタック名と、メールアドレスを入力します。

「Configure stack options」のページは特に設定を変更せず、先に進みます。

最後のページでは、「Capabilities」内のチェックボックスにチェックを入れてください。
以上の設定で作成を行います。

サブスクリプションの承認

指定したメールアドレス宛に、承認のためのメールが届きます。
こちらの承認を行います。

3. 挙動結果の確認

重要度別に件数をカウントした結果が、指定したメールアドレスに届きます。

おわりに

以上、「EventBridge + Lambda + SNS」を利用して、管理者に定期的にレポート結果を届けることができました。

ご活用頂けると幸いです。
御覧いただき、ありがとうございました!

MEGAZONE株式会社 Tech Blog

Discussion