AWS CDKでSecurity Hubのイベント駆動型アーキテクチャを構築してみた

AWS CDKでSecurity Hubのイベント駆動型アーキテクチャを構築してみた

Clock Icon2025.04.07

こんにちは!製造ビジネステクノロジー部の小林です。

前回の記事ではAWS CDKを利用して、Security Hubを構築しました。
今回はその続編として、Security Hubで検出された脆弱性を重要度レベルに応じて自動的にメール通知する仕組みを作っていきます。EventBridge、Lambda、SNSを組み合わせたサーバーレスアーキテクチャで、セキュリティ監視を効率化する方法をご紹介します!

やりたいこと図

vscode-drop-1743871760870-l1c67ktmksp.jpeg

全体のワークフロー

  1. Security Hubで検出結果が生成される
  2. EventBridgeルールで、重要度が CRITICAL、HIGH、MEDIUM の検出結果をキャプチャ
  3. ルールに一致すると、Lambda関数が自動的に呼び出される
  4. Lambda関数は検出結果の詳細を処理し、SNSトピックにメッセージを発行
  5. SNSトピックは、登録されたメールアドレスに通知を送信

このアーキテクチャにより、重要なセキュリティ問題が検出された際に、自動的に担当者にメール通知が送られるため、迅速な対応が可能となります。

プロジェクト構造

プロジェクトの構造は以下のようになります。

.
├── bin
│   └── cdk-security-hub.ts # エントリーポイント
├── lambda
│   └── index.ts            # Lambdaソース
├── lib
│   ├── cdk-security-hub.ts # Security Hub設定
│   └── security-action.ts  # EventBridge,SNS,Lambda関数定義
...

やってみる

AWS CDKを使って通知システムのインフラストラクチャを定義します。このコードでは、Security Hubからの検出結果を処理するためのEventBridgeルール、Lambda関数、SNSトピックを作成します。

security-acution.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as path from 'path';
import * as lambda from 'aws-cdk-lib/aws-lambda'; 

export class SecurityAction extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // SNSトピック
    const topic = new sns.Topic(this, 'SecurityTopic');

    // メール購読
    topic.addSubscription(  // トピックにメール購読を追加
      new subscriptions.EmailSubscription('<メールアドレスを指定>') // 指定したメールアドレスに通知を送信
    );

    // Lambda関数
    const handler = new NodejsFunction(this, 'SecurityHandler', {
      entry: path.join(__dirname, '../lambda/index.ts'),
      handler: 'handler',
      runtime: lambda.Runtime.NODEJS_20_X,
      bundling: {
        forceDockerBundling: false,
        externalModules: [
          'aws-sdk',
        ],
      },
      environment: {
        TOPIC_ARN: topic.topicArn,  // Lambda関数内でSNSトピックのARNを環境変数として使用できるようにする

      },
    });

    // Lambda関数にSNSトピックへのメッセージ発行権限を付与
    topic.grantPublish(handler);

    // EventBridgeルール
    const rule = new events.Rule(this, 'SecurityRule', {
      eventPattern: {  // どのイベントをキャプチャするかを定義
        source: ['aws.securityhub'],  // Security Hub からのイベントをフィルタリング
        detailType: ['Security Hub Findings - Imported'],  // インポートされた検出結果のみを対象
        detail: {
          findings: {
            Severity: {
              Label: ['CRITICAL', 'HIGH', 'MEDIUM'],  // 重要度が CRITICAL、HIGH、MEDIUM の検出結果のみをフィルタリング

            }
          }
        }
      }
    });

    // ルールのターゲットとしてLambda関数を追加
    rule.addTarget(new targets.LambdaFunction(handler));
  }
}

イベントを処理するLambda関数を実装します。この関数は、検出された脆弱性の情報を受け取り、SNSを通じてメール通知を送信します。

index.ts
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';

const snsClient = new SNSClient({});

export const handler = async (event: any): Promise<void> => {
  const findings = event.detail.findings;
  if (!findings || findings.length === 0) return;

  const topicArn = process.env.TOPIC_ARN;

  for (const finding of findings) {
    await snsClient.send(new PublishCommand({
      TopicArn: topicArn, // このARNで指定されたSNSトピックに通知が送信されます
      Subject: `Security Alert:開発環境 ${finding.Title.substring(0, 99)}`, // SNSのSubjectには最大100文字の制限があるため、substring(0, 99)で99文字に制限している
      Message: `Severity: ${finding.Severity.Label}\nResource: ${finding.Resources[0]?.Id || 'Unknown'}` // SNSメッセージの本文を設定。検出結果の重要度(CRITICAL、HIGH、MEDIUM)を表示
    }));
  }
};

デプロイ

以下のコマンドでデプロイします。このコマンドは、先ほど定義したSecurityActionスタックをAWS CloudFormationを通じてプロビジョニングします。

# 特定のスタックをデプロイする場合
cdk deploy SecurityAction

# 複数のスタックがある場合、すべてをデプロイする場合は
cdk deploy --all

デプロイ後にメールを確認する

CDKのデプロイが完了したら、SNSトピックのサブスクリプション確認メールが登録したメールアドレス宛に届きます。これはAWSのセキュリティ対策で、意図しないメールアドレスへの通知を防ぐためのものです。

  1. 受信ボックスを確認し、「AWS Notification - Subscription Confirmation」というメールを探します
  2. メール内の「Confirm subscription」リンクをクリックして承認します
    vscode-drop-1743876438635-na4s96a5g7g.png

SNSコンソールで確認

サブスクリプションが正常に確認されたかどうかをAWSコンソールで確認できます。

  1. AWSマネジメントコンソールにログイン
  2. SNSサービスに移動
  3. 左側のナビゲーションから「Subscriptions」(サブスクリプション)を選択
  4. 登録したメールアドレスのステータスが「Confirmed」(確認済み)になっていることを確認
    vscode-drop-1743876306164-xzp6phs7i7q.png

実際にアラートを発報させてメールを受信してみる

システムが正しく動作するかテストするために、Security Hubで手動でアラートを発報させてみましょう!

AWS Security Hubコンソールにアクセスし、左側のナビゲーションから「Findings」(検出結果)を選択。
任意の検出結果を選択(チェックボックスをオン)し、画面上部の「Actions」(アクション)ボタンをクリック。
右画面の「ワークフローのステータス」のプルダウンで「通知済み」を選択。
スクリーンショット 2025-04-07 23.55.42
スクリーンショット 2025-04-07 23.57.34
これにより、EventBridgeルールの条件に一致するイベントが生成され、Lambda関数が起動し、SNS通知が送信されます。

受信ボックスを確認

数分以内に、設定したメールアドレスに「Security Alert:開発環境」で始まるメールが届きます。このメールには、検出結果の重要度とリソースIDが含まれています。
スクリーンショット 2025-04-08 0.09.55
届きました!中身も確認してみます。
スクリーンショット 2025-04-08 0.11.30
ちょっと内容を見てみました。
件名
Security Alert:開発環境 VPC flow logging should be enabled in all VPCs
これはVPC Flow Logsが有効になっていないという警告です

重要度
Severity: MEDIUM
中程度の重要度のセキュリティ問題です。

影響を受けるリソース
Resource: arn:aws:ec2:ap-northeast-1:×××××××××××:vpc/vpc-0f49868e25f93db82
東京リージョン(ap-northeast-1)のAWSアカウント内のVPC(vpc-0f49868e25f93db82)が対象です

問題の内容
VPC Flow Loggingが有効化されていないことを示しています。VPC Flow Logsはネットワークトラフィックを記録するための機能で、セキュリティ監査やトラブルシューティングに重要です。

メールの下部
購読解除のリンクとAWSサポートへの問い合わせ方法が記載されています。

トラブルシューティング

もし、通知が届かない場合は、以下を確認してみてください。

  1. SNSサブスクリプションが確認済みになっているか
  2. Lambda関数のログ(CloudWatch Logs)でエラーが発生していないか
  3. EventBridgeルールが正しく設定されているか(CloudWatch Logsのメトリクスで確認可能)
  4. Security Hubが有効で、検出結果が生成されているか

おわりに

今回は、AWS CDKを使ってSecurity Hubの検出結果を自動通知するシステムを構築してみました。EventBridge、Lambda、SNSを組み合わせることで、セキュリティアラートを即座にメールで受け取れるようになり、インシデント対応の迅速化が図れます。

セキュリティ対策は「やりっぱなし」ではなく、検知から対応までの一連の流れを自動化することが重要です。
今回のような仕組みを土台に、さらにSlack通知や自動修復の仕組みなども追加していきたいと思います!

余談ですが、筆者はイベント駆動型のアーキテクチャは、ピラゴラスイッチみたいで好きです。
それではまた次回の記事でお会いしましょう!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.