CloudFormationでAWS Security Hubからの新規検出通知をSlackで受ける環境を（ほぼ）一発で準備する

# はじめに
AWSでは作成されたリソースのセキュリティチェックをSecurity Hubで行うことができます。
Webコンソール上で確認することはできますが、デフォルトだと作ったリソースがセキュリティ基準に準拠してなかった場合に検知することはできず、定期的に確認する必要があります。
そこで今回は（ほぼ）一発でSecurity Hubで新規検知した際にSlackに通知できるCloudFormationテンプレートを作成しましたので、その内容についてご紹介します。

# 事前準備
事前に通知したいSlack WorkSpaceと該当AWSアカウントのChatbotとの接続が必要となります。
接続方法については簡易的になりますが過去に同様の記事を書いておりますのでこちらもご覧ください！
https://qiita.com/mt-kage/items/7b2464fc17d81d99f72b#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99

# CloudFormationテンプレート
実際に作成したテンプレートは以下になります。

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'AWS Security Hub to Slack Notice Resources'
Parameters:
  SlackWorkSpaceID:
    Type: 'String'
    Description: 'Slack WorkSpace ID'
  SlackChannelID:
    Type: 'String'
    Description: 'Slack Channel ID'
Resources:
  IAMRoleChatbot:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub '${AWS::StackName}-iam-role-for-chatbot'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service: 'chatbot.amazonaws.com'
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: !Sub '${AWS::StackName}-iam-policy-for-chatbot'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'cloudwatch:Describe*'
                  - 'cloudwatch:Get*'
                  - 'cloudwatch:List*'
                Resource:
                  - '*'
  IAMRoleStepFunctions:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub '${AWS::StackName}-iam-role-for-step-functions'
      AssumeRolePolicyDocument:
        Statement:
          - Effect: 'Allow'
            Action: 'sts:AssumeRole'
            Principal:
              Service: 'states.amazonaws.com'
      Policies:
        - PolicyName: !Sub '${AWS::StackName}-iam-policy-for-step-functions'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'securityhub:BatchUpdateFindings'
                Resource: '*'
  IAMRoleEventRule:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub '${AWS::StackName}-iam-role-for-event-rule'
      AssumeRolePolicyDocument:
        Statement:
          - Effect: 'Allow'
            Action: 'sts:AssumeRole'
            Principal:
              Service: 'events.amazonaws.com'
      Policies:
        - PolicyName: !Sub '${AWS::StackName}-iam-policy-for-event-rule'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'states:StartExecution'
                Resource: '*'
  SNSTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName: !Sub '${AWS::StackName}-sns-topic'
  ChatbotSlackChannelConfiguration:
    Type: 'AWS::Chatbot::SlackChannelConfiguration'
    Properties:
      ConfigurationName: !Sub '${AWS::StackName}-chatbot-slack-channel-configuration'
      IamRoleArn: !GetAtt IAMRoleChatbot.Arn
      LoggingLevel: 'INFO'
      SlackWorkspaceId: !Ref SlackWorkSpaceID
      SlackChannelId: !Ref SlackChannelID
      SnsTopicArns:
        - !Ref SNSTopic
  StepFunctionsStateMachine:
    Type: 'AWS::StepFunctions::StateMachine'
    Properties:
      StateMachineName: !Sub '${AWS::StackName}-step-functions-state-machine'
      RoleArn: !GetAtt IAMRoleStepFunctions.Arn
      DefinitionString: |
        {
          "Comment": "Security Hub finding handler",
          "StartAt": "Check finding workflow status",
          "States": {
            "Check finding workflow status": {
              "Type": "Choice",
              "Choices": [
                {
                  "Not": {
                    "Variable": "$.detail.findings[0].Workflow.Status",
                    "StringEquals": "NOTIFIED"
                  },
                  "Comment": "not NOTIFIED",
                  "Next": "Update finding workflow status to NOTIFIED"
                }
              ],
              "Default": "End"
            },
            "End": {
              "Type": "Pass",
              "End": true
            },
            "Update finding workflow status to NOTIFIED": {
              "Type": "Task",
              "Parameters": {
                "FindingIdentifiers": [
                  {
                    "Id.$": "$.detail.findings[0].Id",
                    "ProductArn.$": "$.detail.findings[0].ProductArn"
                  }
                ],
                "Workflow": {
                  "Status": "NOTIFIED"
                }
              },
              "Resource": "arn:aws:states:::aws-sdk:securityhub:batchUpdateFindings",
              "Next": "End"
            }
          }
        }
  EventsRule:
    Type: 'AWS::Events::Rule'
    Properties:
      Name: !Sub '${AWS::StackName}-events-rule'
      Description: 'Security Hub to Slack Notice'
      State: 'ENABLED'
      EventPattern:
        source:
          - 'aws.securityhub'
        detail-type:
          - 'Security Hub Findings - Imported'
        detail:
          findings:
            Compliance:
              Status:
                - anything-but: 'PASSED'
            RecordState:
              - 'ACTIVE'
            Severity:
              Label:
                - 'CRITICAL'
                - 'HIGH'
            Workflow:
              Status:
                - 'NEW'
      Targets:
        - Id: !Sub '${AWS::StackName}-events-to-sns'
          Arn: !Ref SNSTopic
        - Id: !Sub '${AWS::StackName}-events-to-step-functions'
          Arn: !GetAtt StepFunctionsStateMachine.Arn
          RoleArn: !GetAtt IAMRoleEventRule.Arn
```

内容としてはEventBridgeでSecurity HubのCRITICALもしくはHIGHの項目で非準拠項目が検出された場合にSNS、StepFunctionsにイベントを発火します。

SNSではそのままChatbotを経由し、指定されたSlackチャンネルへ通知します。

またSecurity Hubの検出処理は定期的に行われる仕様であるため、そのままだとすでに検知済みの項目も定期的にSlackへ通知され新規検出分を探すことが難しくなってしまいます。
そのためStepFunctionsを使って、新規検出時はワークフローのステータスを通知済み（NOTIFIED）へ変更しています。これによって検出済みの非準拠項目が何度も通知されることを防いでいます。

StepFunctionsのワークフロー定義についてはこちらの記事を参考にさせていただきました。
https://dev.classmethod.jp/articles/set-securityhub-finding-as-notified/

スタック作成後しばらく待つと以下のように非準拠項目がSlackに通知されてきます。
![Screenshot 2023-12-08 15.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146076/8ff2323a-c9af-02bd-e366-b55b9a5213d1.png)

# おわりに

以上、今回はAWS Security Hubの新規検出通知をSlackで受ける環境をCloudFromationで準備しました。
Webコンソール上で都度Security Hubの検出を確認されている方の参考になれば幸いです。