メインコンテンツへスキップ
友田 陽大
Amazon GuardDuty 本番運用
セキュリティ
AWS
GuardDuty
Terraform
アーキテクチャ設計

Amazon GuardDuty で AWS の脅威検知を本番設計する:保護プラン・Extended Threat Detection・組織一括有効化・EventBridge 自動対応を実コードで

Amazon GuardDutyでAWSの脅威検知を本番構築する実装ガイド。エージェントレスな基盤検知(CloudTrail/VPC Flow Logs/DNS)、S3・EKS・Runtime・Malwareの保護プラン選定、追加費用ゼロのExtended Threat Detection、組織一括有効化、そしてEventBridge→冪等な自動対応までをTerraform/Pythonの実コードで解説します。GuardDutyは予防ではなく検知の一層——その前提から設計します。

公開日
読了時間
30分
著者
友田 陽大
シェア
目次

「GuardDuty って、有効化しておけば守ってくれるんですよね?」——AWS のセキュリティを相談される場で、最も多い誤解の一つです。

半分は正しく、半分は危険です。確かに GuardDuty は、有効化した瞬間からエージェント不要で AWS 全体の脅威を監視し始めます。けれど GuardDuty は「防御(prevention)」ではなく「検知(detection)」のサービスです。攻撃を止めるのは GuardDuty の仕事ではありません。GuardDuty は「いま危ないことが起きている」と知らせるだけ——その通知(finding)を受けて何をするかを設計しない限り、GuardDuty はダッシュボードを赤く光らせるだけの装置になります。

この記事は、Amazon GuardDuty で AWS の脅威検知を本番品質で設計・運用するための実装ガイドです。単一アカウントの有効化から、AWS Organizations での全社一括統制、資産に合わせた保護プランの選定、そして最も重要な 「finding を EventBridge で受けて、冪等な自動対応に変える」 ところまでを、Terraform と Python の実コードで通します。題材として、私がマルチアカウント AWS 上のサーバーレス決済プラットフォームで IAM・可観測性・DR を横断実装した経験——実際の金銭・カーボンクレジット・地域通貨を扱うため、認証情報の漏洩や異常な API 操作を「運用の注意深さ」ではなく仕組みで検知する必要があった——という視点も交えます。

この記事のルール:仕様・データソース・finding の重大度・料金体系は AWS 公式ドキュメント(2026年6月時点) に基づきます。保護プランの対応状況・finding の種類・料金は改定されるため、本番投入前に必ず公式の最新情報を確認してください。コードは実運用に近い形(Terraform / Python)に整えていますが、GuardDuty は検知サービスであり、WAF・最小権限の IAM・入力検証・暗号化を代替しません。そしてもう一つの鉄則——自動対応は「冪等・スコープを絞る・取り消せる」を満たすものから始め、破壊的な操作は人間のレビューを挟むことです。


0. メンタルモデル:GuardDuty は「ログを解析する脅威検知エンジン」

設計を始める前に、GuardDuty が何で、何でないかを一行で固定します。

GuardDuty = AWS のログ(CloudTrail・VPC Flow Logs・DNS ほか)を継続的に取り込み、脅威インテリジェンスと ML で「悪意ある/異常な振る舞い」を検知して finding を生成する、エージェントレスの検知エンジン。

ここから3つの帰結が出ます。これが設計判断のすべての土台になります。

  1. GuardDuty は「検知」であって「防御」ではない。 GuardDuty はリクエストをブロックしません(それは WAF の仕事)。攻撃を止めるのではなく、起きていることを知らせるサービスです。だから GuardDuty の価値は「検知の精度 × 検知後の対応速度」で決まります。finding を放置すれば価値はゼロです。
  2. GuardDuty はエージェントレスで、ログの配管を要求しない。 基盤データソース(CloudTrail 管理イベント/VPC Flow Logs/DNS ログ)について、GuardDuty は 「独立した複製ストリーム」 を直接消費します。公式の表現を借りれば "an independent and duplicated stream of events"。つまりあなたが CloudTrail 証跡を作る必要も、VPC Flow Logs を有効化する必要も、これらを保存・課金する必要もありません。GuardDuty はフィールドを抽出して profiling した後、ログ本体を破棄します(保存しません)。あなた側のログ設定を変えても GuardDuty には影響しません。逆も同様です。
  3. finding は「出発点」であって「終点」ではない。 GuardDuty が出すのは構造化された通知です。それを EventBridge で受け、トリアージ・自動対応・チケット化に繋ぐ「対応の配管」を作って初めて、検知は運用に載ります。**本記事の山場は 6 章(EventBridge → 自動対応)**です。

この3点を押さえると、「GuardDuty を入れる」という作業が、実は 「①どのログを見せるか(保護プラン選定)→ ②どう全社に広げるか(組織統制)→ ③finding をどう行動に変えるか(自動対応)」 の3つの設計だと分かります。順に作ります。


1. GuardDuty の全体地図:基盤検知 + 保護プラン + Extended Threat Detection

GuardDuty は「一枚岩のサービス」ではなく、常時オンの基盤任意で足す保護プラン無償で相関する上位レイヤー の3層構造です。

1.1 基盤データソース(有効化=即オン・追加費用なし)

GuardDuty を有効化すると、何も追加設定せずに以下の3つを解析し始めます。

基盤データソース何を見るか代表的な検知例
CloudTrail 管理イベント誰がどの AWS API を、どこから、いつ叩いたか(コントロールプレーン操作)漏洩した認証情報の悪用、異常な IAM 操作、権限昇格
VPC Flow LogsEC2 のネットワークインターフェース出入りの IP トラフィック既知の悪性 IP との通信、ポートスキャン、C&C 通信
Route 53 Resolver DNS クエリログEC2 が引いた DNS 名前解決悪性ドメインへの問い合わせ、暗号資産マイニング、DGA

運用上の注意(DNS):DNS 検知が効くのは AWS の DNS リゾルバ(Route 53 Resolver、EC2 のデフォルト) を使っている場合だけです。OpenDNS や Google DNS、自前のリゾルバを使うと、GuardDuty はこのデータソースを参照できません。

1.2 保護プラン(資産がある時だけ足す)

基盤の外側に、監視対象を追加で広げる任意の保護プランがあります。GuardDuty 公式はこれらをまとめて「Features(機能)」と呼びます。

保護プラン追加で見るログ/対象いつ有効化するか
S3 ProtectionCloudTrail の S3 データイベント機密データを S3 に置いている(ほぼ常に推奨)
EKS ProtectionEKS 監査ログ(コントロールプレーン)EKS クラスタを運用している
Runtime Monitoringコンテナ/ホスト内のプロセス・システムコール(EKS / EC2 / ECS-Fargate、軽量エージェント)ランタイムの侵害を見たい(最もコスト高)
Malware Protection for EC2EC2/コンテナにアタッチされた EBS ボリュームをエージェントレスでスキャンEC2 起因の finding 時にマルウェアを確認したい
Malware Protection for S3新規アップロードされた S3 オブジェクトをスキャンアップロードを受け付ける S3 バケットがある
RDS ProtectionAurora / RDS のログインアクティビティDB への異常なログイン試行を検知したい
Lambda ProtectionLambda の ネットワークアクティビティ(VPC Flow Logs)Lambda の外向き通信を監視したい

設計の勘所は 「資産に合わせて足す」(YAGNI) です。EKS を運用していないのに EKS Protection を有効化しても何も検知されません。逆に、S3 Protection は機密データを扱うほぼ全アカウントで推奨——後述の Extended Threat Detection が S3 への攻撃シーケンスを束ねるためにも効きます。コストは有効化したプラン × 監視量に比例するため、保護プランの選定は実質「セキュリティ予算の配分」です(8 章で料金を扱います)。

覚えておくと得する例外Malware Protection for S3 は GuardDuty 本体を有効化せず単独で使えます。「アップロードされたファイルだけスキャンしたい」というユースケースに、最小コストで応えられます。

1.3 Extended Threat Detection(無償・自動・最強の上位レイヤー)

ここが GuardDuty の真価です。Extended Threat Detection (ETD) は、GuardDuty を有効化すると自動で・追加費用ゼロでオンになり、個別の弱いシグナルを相関させて「多段攻撃(attack sequence)」を1つの finding に束ねます

公式の言葉でいうと、GuardDuty は API 操作や個別 finding を シグナル(Signals) として扱い、単体では脅威に見えない 弱いシグナル(weak signals) も含めて、24時間のローリングウィンドウで「攻撃の連鎖」として align するパターンを検出します。たとえば:

  • AWS 認証情報 + S3 データの侵害:コンピュートワークロードへの不正アクセス → 権限昇格・永続化 → S3 からのデータ持ち出し、という一連を 1つの finding にまとめる。
  • EKS クラスタの侵害:脆弱なコンテナの悪用 → 特権サービスアカウントトークンの取得 → pod identity 経由で Kubernetes secrets や AWS リソースへアクセス。

そして決定的な事実——攻撃シーケンス finding は、その性質上すべて重大度「Critical(9.0〜10.0)」 に分類されます。公式が定義する型は AttackSequence:IAM/CompromisedCredentialsAttackSequence:S3/CompromisedDataAttackSequence:EKS/CompromisedClusterAttackSequence:ECS/CompromisedClusterAttackSequence:EC2/CompromisedInstanceGroup の5つです(各型の詳しい読み解きはExtended Threat Detection の深掘り記事へ)。

ETD は基盤データソースだけでも動きますが、保護プランを足すほど相関できる範囲が広がります。EKS の攻撃シーケンスには EKS Protection か Runtime Monitoring が、S3 の攻撃シーケンスには S3 Protection が必要、という具合です。つまり 「S3 Protection を有効化する理由は、個別検知だけでなく ETD の相関を広げるため」 でもあるわけです。

設計への含意:ETD は「個別 finding を人間が点で追う」運用を「攻撃の文脈を線で受け取る」運用に変えます。後述の自動対応では、AttackSequence:*(=必ず Critical)を最優先のトリガーに据えるのが定石です。

なお 2026 年現在、AI が finding を分析して MITRE ATT&CK 分類・リスク評価・次アクションを返す GuardDuty Investigation(プレビュー) も登場しています。プレビュー段階のため本番フローの中核には据えませんが、トリアージ補助として把握しておく価値があります。

1.4 クラスタ・ロードマップ:12の深掘り記事への地図

このピラーは GuardDuty 本番運用の全体像です。各テーマは独立した深掘り記事に分けています。目的から選んでジャンプしてください(検知のライフサイクル順)。

目的 / 段階深掘り記事こんな人へ
検知を相関するExtended Threat Detection と攻撃シーケンス個別 finding を「攻撃の線」で捉えたい
検知を行動に変えるEventBridge 自動対応(SOAR)finding を冪等に自動対応し MTTR を縮めたい
全社へ広げるマルチアカウント / Organizations 統制複数アカウント・全リージョンを一括統制したい
コンテナ:監査ログEKS Protection(Kubernetes監査ログ)RBAC 改変・匿名アクセスを検知したい
コンテナ:ランタイムRuntime Monitoring(EKS/ECS/EC2)コンテナ内部の侵害(プロセス/通信)を見たい
データ:S3Malware Protection for S3(単独運用)アップロードを自動スキャン・検疫したい
データ:DB / サーバーレスRDS / Lambda ProtectionDB ログイン異常・Lambda 脅威を検知したい
調査するAmazon Detective 調査ワークフロー根本原因・影響範囲(ブラストラディウス)を調べたい
長期集約するSecurity Lake 集約・OCSF 分析長期保管・横断 SQL・SIEM 連携したい
誤検知を抑えるSuppression / 信頼 IP / 脅威リスト チューニング攻撃を消さずにノイズだけ下げたい
コストを最適化するコスト最適化・料金(FinOps)請求を分解してムダを削りたい
技術選定するGuardDuty vs Security Hub / Detective / Inspector / Macieどのサービスをどう組み合わせるか整理したい

2. まず1アカウントで有効化する(Terraform)

最小構成は驚くほど単純です。GuardDuty を有効化した瞬間に、基盤検知も Extended Threat Detection も動き始めます。新規有効化したリージョンには 30日間の無料トライアルが付き、その間に各データソースの想定使用量を確認できます。

# detector = そのアカウント・そのリージョンの GuardDuty 本体。
# これ1つで「基盤検知 + Extended Threat Detection」が即オンになる。
resource "aws_guardduty_detector" "this" {
  enable = true

  # finding の「更新」を EventBridge / S3 へ流す頻度。
  # FIFTEEN_MINUTES | ONE_HOUR | SIX_HOURS(デフォルト)。
  # 自動対応を組むなら 15 分を強く推奨(対応の遅延を縮める。理由は 6 章)。
  finding_publishing_frequency = "FIFTEEN_MINUTES"

  tags = { ManagedBy = "terraform", Purpose = "threat-detection" }
}

保護プランは、aws_guardduty_detector とは別リソースaws_guardduty_detector_feature)で「資産に合わせて」足します。たとえば S3 Protection だけを足すなら:

# S3 Protection を有効化(CloudTrail の S3 データイベントを監視)。
# ETD の S3 攻撃シーケンス相関も、これで初めて有効になる。
resource "aws_guardduty_detector_feature" "s3_data_events" {
  detector_id = aws_guardduty_detector.this.id
  name        = "S3_DATA_EVENTS"
  status      = "ENABLED"
}

リージョン戦略:GuardDuty はリージョン単位のサービスです。公式は 使っていないリージョンも含めて全リージョンで有効化することを推奨します。理由は、IAM・STS・CloudFront などのグローバルサービスイベントが各リージョンに複製・処理され、攻撃者が「手薄なリージョン」で不正リソースを作る経路を塞げるからです。1アカウントを手で全リージョン有効化するのは現実的でないため、実運用では次章の「組織一括有効化」で一気に広げます


3. 組織で一括統制する:委任管理者 + 自動有効化(マルチアカウント)

アカウントが2つ以上あるなら、AWS Organizations 経由の集中管理が公式の推奨です。管理アカウントから直接運用するのではなく、セキュリティ専用アカウントを「委任管理者(delegated administrator)」に指定し、そこから全メンバーアカウントの finding を集約・統制します(管理アカウントに権限を集中させない最小権限の原則)。

# ── 管理(payer)アカウントで実行:GuardDuty の委任管理者を指名 ──
# security-tooling アカウントに GuardDuty 管理を委譲する。
resource "aws_guardduty_organization_admin_account" "delegate" {
  admin_account_id = var.security_account_id # 例: 専用の security-tooling アカウント
}

次に、委任管理者アカウント側で「組織に入っている/今後入る全アカウントを自動的に GuardDuty 対象にする」設定を入れます。

# ── 委任管理者(security-tooling)アカウントで実行 ──
resource "aws_guardduty_detector" "security" {
  enable                       = true
  finding_publishing_frequency = "FIFTEEN_MINUTES"
}

# 組織メンバーの自動有効化。
#   ALL  = 既存 + 新規すべてのアカウントを有効化(推奨:取りこぼしを作らない)
#   NEW  = 今後 join するアカウントのみ
#   NONE = 自動有効化しない
resource "aws_guardduty_organization_configuration" "this" {
  detector_id                      = aws_guardduty_detector.security.id
  auto_enable_organization_members = "ALL"
}

# 保護プランも「組織全体で自動有効化」できる。
# 例: S3 Protection を全アカウントで自動 ON(ETD の S3 相関を全社で効かせる)。
resource "aws_guardduty_organization_configuration_feature" "s3_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "S3_DATA_EVENTS"
  auto_enable = "ALL"
}

この3リソースで、「新しく作られたアカウントも、人手を介さず GuardDuty 配下に入る」 状態が作れます。決済プラットフォームのように 本番/ステージング/監査用にアカウントを分離している環境では、これが効きます——アカウント追加のたびに「GuardDuty 入れ忘れ」が起きる余地を、構造的に消せるからです。

auto_enable_organization_members は ALL を選ぶNEW だけにすると、設定時点で既に存在するアカウントが永久に対象外のまま残り得ます。「全社で漏れなく検知する」というセキュリティの目的に対して、ALL 以外はサイレントな穴になります。意図的に除外したいアカウントがある場合のみ、個別に管理してください。


4. 保護プランを「資産に合わせて」足す:何を・いつ・なぜ

保護プランは全部入れれば良いものではありません。検知されない対象を監視してもコストが増えるだけ(YAGNI 違反)。以下を「自分の資産」と照合して選びます。

有効化を検討する条件足すプラン補足
機密データを S3 に保管しているS3 Protectionほぼ常に推奨。ETD の S3 相関も有効化される
アップロードを受け付ける S3 バケットがあるMalware Protection for S3GuardDuty 本体なしでも単独利用可
EKS を運用しているEKS Protection(監査ログ)RBAC 改変・secrets アクセス等を検知
コンテナ/ホスト内部の侵害まで見たいRuntime Monitoring最もコスト高(vCPU 課金)。軽量エージェント
EC2 起因 finding 時にマルウェアを確認したいMalware Protection for EC2EBS をエージェントレスでスキャン
Aurora/RDS への異常ログインを検知したいRDS Protectionログインアクティビティを監視
Lambda の外向き通信を監視したいLambda ProtectionLambda の VPC Flow Logs を解析

Runtime Monitoring だけは別格です。コンテナ/ホスト内部のプロセスやシステムコールまで見るため検知精度は最高ですが、保護対象の vCPU 数に比例して課金され、軽量エージェント(EKS は add-on、ECS-Fargate はサイドカー、EC2 はエージェント)が必要です。GuardDuty にエージェント配置を自動管理させることもできます。

# Runtime Monitoring を有効化し、各環境のエージェント配置を GuardDuty に自動管理させる。
# コストが vCPU 比例で増えるため、「本当にランタイム可視性が要る環境」に絞って有効化する。
resource "aws_guardduty_detector_feature" "runtime_monitoring" {
  detector_id = aws_guardduty_detector.this.id
  name        = "RUNTIME_MONITORING"
  status      = "ENABLED"

  additional_configuration {
    name   = "EKS_ADDON_MANAGEMENT" # EKS のエージェントを自動デプロイ・更新
    status = "ENABLED"
  }
  additional_configuration {
    name   = "ECS_FARGATE_AGENT_MANAGEMENT" # Fargate タスクにサイドカーを自動注入
    status = "ENABLED"
  }
  additional_configuration {
    name   = "EC2_AGENT_MANAGEMENT" # EC2 のエージェントを SSM 経由で自動管理
    status = "ENABLED"
  }
}

トレードオフの言語化:保護プランの選定は「セキュリティ網羅性 vs コスト」の典型的な綱引きです。私は 「①S3 Protection は全社デフォルト ON → ②EKS/RDS/Lambda は該当資産のあるアカウントだけ → ③Runtime Monitoring は本番の重要ワークロードに限定」 という段階で広げることを薦めます。最初から全部 ON にして請求書に驚くより、ETD(無償)+ S3 Protection(安価で効果大)を土台に、必要な層だけ積むほうが、費用対効果で勝ります。


5. finding を読む:型・重大度・攻撃シーケンス

自動対応を設計するには、finding の構造を読める必要があります。GuardDuty の finding 型は、意味が型名にエンコードされているのが秀逸な点です。

5.1 finding 型のフォーマット

ThreatPurpose : ResourceTypeAffected / ThreatFamilyName . DetectionMechanism ! Artifact
        │              │                    │                    │              │
        │              │                    │                    │              └─ 攻撃ツールが使う具体リソース(例: DNS)。任意
        │              │                    │                    └─ 検知方法(.Custom=自前脅威リスト, .Reputation=評判スコア 等)
        │              │                    └─ 検知している脅威の中身(例: BitcoinTool, NetworkPortUnusual)
        │              └─ 標的となった AWS リソース種別(EC2 / S3 / IAMUser / EKS / RDS / Lambda ...)
        └─ 脅威の目的・攻撃段階(多くは MITRE ATT&CK の戦術に対応)

例:CryptoCurrency:EC2/BitcoinTool.B!DNS は、「EC2 が、Bitcoin 関連の既知ドメイン(!DNS)と通信している」を一目で伝えます。UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS なら、「EC2 に紐づく一時認証情報が、発行元アカウントの外で使われた(=認証情報の持ち出し)」です。

先頭の ThreatPurpose は攻撃の段階を表し、多くが MITRE ATT&CK の戦術に対応します:InitialAccess / Execution / Persistence / PrivilegeEscalation / DefenseEvasion / CredentialAccess / Discovery / Exfiltration / Impact のほか、Backdoor / CryptoCurrency / Trojan / Recon / Stealth / Policy / UnauthorizedAccess / Pentest などがあります。型名を見るだけで「攻撃のどの段階か」が分かるので、自動対応のルーティングに直接使えます。

5.2 重大度(severity)の数値帯

finding には 1.0〜10.0 の数値が付き、4段階に区切られます。自動対応の閾値はこの数値で切るのが基本です。

重大度数値帯意味推奨対応
Critical9.0 – 10.0攻撃シーケンスが進行中/直近で発生。複数リソースが侵害された可能性最優先で即トリアージ・封じ込め
High7.0 – 8.9リソースが侵害され、不正利用が実際に進行中即時の封じ込め(隔離・鍵失効)
Medium4.0 – 6.9ベースラインから逸脱した不審な挙動。侵害の可能性あり早期に調査し、正規利用かを確認
Low1.0 – 3.9侵害には至らない試行(ポートスキャン・失敗した侵入)即時対応は不要だが記録・傾向把握

AttackSequence:*(Extended Threat Detection の攻撃シーケンス)は必ず Critical です。自動対応では「severity ≥ 7(High 以上)で封じ込め検討、Critical は即エスカレーション」という二段の閾値が扱いやすい設計です。

5.3 本番投入前に「対応経路」を検証する

GuardDuty には サンプル finding を意図的に生成する仕組みがあります。CreateSampleFindings API(または専用の tester スクリプト)で各型の finding を発火させ、EventBridge → 自動対応の経路が想定通り動くかを、実際の攻撃を待たずに検証できます。これは「検証経路を先に作る」原則そのものです。

# detector に対し、サンプル finding を生成して対応パイプラインを検証する。
# (type を指定すれば特定の finding 型だけを発火できる)
aws guardduty create-sample-findings \
  --detector-id "$DETECTOR_ID" \
  --finding-types "UnauthorizedAccess:EC2/MaliciousIPCaller.Custom" \
                  "CryptoCurrency:EC2/BitcoinTool.B!DNS"

6. 検知を行動に変える:EventBridge → 冪等な自動対応(最重要)

ここが本記事の山場です。GuardDuty の価値は「検知後の対応速度(MTTR)」で決まり、その自動化の土台が Amazon EventBridge です。すべての finding は、ほぼリアルタイムで EventBridge に流れます。

6.1 EventBridge に届く finding のかたち

{
  "source": "aws.guardduty",
  "detail-type": "GuardDuty Finding",
  "detail": {
    "id": "1ab23c...",
    "type": "UnauthorizedAccess:EC2/MaliciousIPCaller.Custom",
    "severity": 7.5,
    "accountId": "123456789012",
    "region": "ap-northeast-1",
    "title": "...",
    "description": "...",
    "resource": { "instanceDetails": { "instanceId": "i-0abc..." } }
  }
}

重要なのは detail.severity が数値だという点です。EventBridge の数値マッチで「High 以上だけ」を拾えます。

{
  "source": ["aws.guardduty"],
  "detail-type": ["GuardDuty Finding"],
  "detail": {
    "severity": [{ "numeric": [">=", 7] }]
  }
}

遅延の落とし穴(必読):GuardDuty は 新規 finding を約5分で発行しますが、同じ finding の「更新(再発)」を流す頻度は finding_publishing_frequency に従い、デフォルトは6時間です。つまり「最初の検知は速いが、続報は最大6時間遅れる」。自動対応の反応速度を上げたいなら、2 章で示したとおり FIFTEEN_MINUTES に設定してください。

6.2 ルーティングの設計:通知は全部、封じ込めは厳選

自動対応で最もやってはいけないのは、**「すべての finding に破壊的アクション(インスタンス停止・鍵削除)を自動で打つ」**ことです。誤検知一発で本番を落とします。設計の原則は:

  • 通知(notify)は広く:High 以上はすべて Slack/SNS に enrich して通知(人間のトリアージを速くする)。
  • 封じ込め(contain)は狭く「型の許可リスト」に載った高信頼の finding だけ、冪等・取り消し可能な封じ込め(隔離 SG のアタッチ等)を自動実行。
  • 破壊的操作は人間を挟む:鍵削除・インスタンス終了は、自動ではチケット化と承認待ちにとどめる。

Terraform でルールと配線を作ります。

# High 以上(severity >= 7) の GuardDuty finding を捕捉するルール。
resource "aws_cloudwatch_event_rule" "guardduty_high" {
  name        = "guardduty-high-severity"
  description = "Route GuardDuty findings (severity >= 7) to the responder"
  event_pattern = jsonencode({
    source        = ["aws.guardduty"]
    "detail-type" = ["GuardDuty Finding"]
    detail        = { severity = [{ numeric = [">=", 7] }] }
  })
}

# ① 人間向け:必ず SNS(→ Slack/メール)へ通知。
resource "aws_cloudwatch_event_target" "to_sns" {
  rule      = aws_cloudwatch_event_rule.guardduty_high.name
  target_id = "notify-sns"
  arn       = aws_sns_topic.security_alerts.arn
}

# ② 機械向け:封じ込めを判断する Lambda へ。
resource "aws_cloudwatch_event_target" "to_responder" {
  rule      = aws_cloudwatch_event_rule.guardduty_high.name
  target_id = "auto-responder"
  arn       = aws_lambda_function.responder.arn

  # 一過性の失敗に備えてリトライ&DLQ(取りこぼさない)。
  retry_policy {
    maximum_event_age_in_seconds = 3600
    maximum_retry_attempts       = 4
  }
  dead_letter_config { arn = aws_sqs_queue.responder_dlq.arn }
}

6.3 冪等な自動対応 Lambda(Python)

EventBridge は at-least-once 配信です。同じ finding で Lambda が2回起動し得ます。決済基盤で二重課金を防ぐのと同じ発想で、自動対応も冪等でなければなりません——「2回隔離されても、状態は1回と同じ」を保証します。

"""GuardDuty finding に応答する自動対応 Lambda。

設計原則:
  - 冪等: EventBridge は at-least-once。同じ finding を2回受けても副作用は1回分。
  - スコープを絞る: 封じ込めは ALLOWLIST に載った型 + High 以上のみ。
  - 取り消し可能: 「隔離SGのアタッチ + タグ付け」だけ。終了や鍵削除はしない。
  - 可観測: 構造化ログ。機密値は出さない。
"""
from __future__ import annotations

import json
import logging
import os
from typing import Any, Final

import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ec2 = boto3.client("ec2")
sns = boto3.client("sns")

# 自動封じ込めを許す finding 型(高信頼・誤検知の少ないものに限定)。
CONTAIN_ALLOWLIST: Final[frozenset[str]] = frozenset(
    {
        "UnauthorizedAccess:EC2/MaliciousIPCaller.Custom",
        "CryptoCurrency:EC2/BitcoinTool.B!DNS",
        "Backdoor:EC2/C&CActivity.B!DNS",
    }
)
QUARANTINE_SG_ID: Final[str] = os.environ["QUARANTINE_SG_ID"]
ALERT_TOPIC_ARN: Final[str] = os.environ["ALERT_TOPIC_ARN"]
# 破壊的操作を避け、まず観察したい段階では DRY_RUN=true で隔離を抑止。
DRY_RUN: Final[bool] = os.environ.get("DRY_RUN", "false").lower() == "true"


def handler(event: dict[str, Any], _context: object) -> dict[str, str]:
    detail = event["detail"]
    finding_type: str = detail["type"]
    severity: float = float(detail["severity"])
    finding_id: str = detail["id"]

    log = {"finding_id": finding_id, "type": finding_type, "severity": severity}

    # 封じ込めの条件: 型が許可リストにあり、かつ High 以上。
    instance_id = (
        detail.get("resource", {}).get("instanceDetails", {}).get("instanceId")
    )
    should_contain = (
        finding_type in CONTAIN_ALLOWLIST and severity >= 7.0 and bool(instance_id)
    )

    if should_contain and not DRY_RUN:
        action = _quarantine_instance(instance_id)  # 冪等
    else:
        action = "dry-run" if (should_contain and DRY_RUN) else "notify-only"

    _notify(finding_type, severity, finding_id, instance_id, action)
    logger.info(json.dumps({**log, "action": action}))
    return {"action": action}


def _quarantine_instance(instance_id: str) -> str:
    """インスタンスを隔離SGだけにする。冪等: 既に隔離済みなら何もしない。"""
    reservations = ec2.describe_instances(InstanceIds=[instance_id])["Reservations"]
    instance = reservations[0]["Instances"][0]

    # 冪等ガード: 隔離タグが既にあればスキップ(2回目の起動は no-op)。
    tags = {t["Key"]: t["Value"] for t in instance.get("Tags", [])}
    if tags.get("guardduty:quarantined") == "true":
        return "already-quarantined"

    # 全 ENI を隔離SGのみに付け替える(egress 遮断は SG 側で定義)。
    for eni in instance["NetworkInterfaces"]:
        ec2.modify_network_interface_attribute(
            NetworkInterfaceId=eni["NetworkInterfaceId"],
            Groups=[QUARANTINE_SG_ID],
        )
    ec2.create_tags(
        Resources=[instance_id],
        Tags=[{"Key": "guardduty:quarantined", "Value": "true"}],
    )
    return "quarantined"


def _notify(
    finding_type: str, severity: float, finding_id: str, instance_id: str | None, action: str
) -> None:
    region = os.environ.get("AWS_REGION", "ap-northeast-1")
    console = (
        f"https://{region}.console.aws.amazon.com/guardduty/home"
        f"?region={region}#/findings?fId={finding_id}"
    )
    sns.publish(
        TopicArn=ALERT_TOPIC_ARN,
        Subject=f"[GuardDuty][{severity}] {finding_type}",
        Message="\n".join(
            [
                f"type: {finding_type}",
                f"severity: {severity}",
                f"instance: {instance_id or 'n/a'}",
                f"action: {action}",
                f"console: {console}",
            ]
        ),
    )

このコードの設計判断を明示します。

  • 冪等:隔離タグでガードし、再配信されても2回目は already-quarantined で no-op。これが無いと、at-least-once 配信で「同じ封じ込めを2回」走らせ、無駄な API 呼び出しや競合を生みます。
  • スコープを絞るCONTAIN_ALLOWLIST × severity >= 7.0 の二重条件。誤検知の多い型や Low/Medium では封じ込めを発火させません。
  • 取り消し可能:やるのは「隔離 SG への付け替え + タグ付け」だけ。インスタンス終了や鍵削除はしません(後で正規利用と判明したら戻せる)。
  • 段階導入DRY_RUN=true で「判断はするが手は出さない」モードを持たせ、本番投入前に意思決定ロジックだけを実トラフィックで検証できます。
  • 最小権限:この Lambda の実行ロールは ec2:ModifyNetworkInterfaceAttribute / ec2:CreateTags / ec2:DescribeInstances / sns:Publish に限定し、Resource も可能な限り絞ります。

テスト容易性:判断ロジック(should_contain の算出)は副作用を持たない純関数に切り出せます。boto3 クライアントを差し替えれば、サンプル finding の JSON を入力にユニットテストで意思決定を網羅できます。5.3 の create-sample-findings と組み合わせれば、コードとインフラの両方で対応経路を検証できます。


7. 誤検知とノイズを抑える:Suppression Rule / 信頼 IP / 脅威リスト

GuardDuty の運用で最初に直面するのは「正規の運用が finding を生む」ノイズです。脆弱性スキャナの定期実行、特定 IP からの管理アクセスなどです。チューニングの道具は3つ。

  • Suppression Rule(抑制ルール):フィルタ条件に一致する finding を自動でアーカイブし、ダッシュボードと通知から外す。「自社の脆弱性スキャナによる Recon:*」のような既知ノイズに使います。
  • 信頼 IP リスト(Trusted IP list):ここに載せた IP からのトラフィックは finding を生成しません。自社の固定 IP・VPN・監視サービスなどに。
  • 脅威リスト(Threat list):自前で「これは悪性」と判断した IP/ドメインを登録。一致すると .Custom の検知メカニズムで finding が出ます(5.1 の !Custom)。
# 信頼 IP リスト(S3 上の txt)を登録し、即アクティベートする例。
aws guardduty create-ip-set \
  --detector-id "$DETECTOR_ID" \
  --name "corporate-egress" \
  --format TXT \
  --location "s3://my-sec-config/trusted-ips.txt" \
  --activate

Suppression Rule の罠(Extended Threat Detection との相互作用):ETD は アーカイブされた finding(抑制ルールで自動アーカイブされたものを含む)を相関の対象にしません。つまり**「単体ではノイズに見える弱いシグナルを抑制で消すと、それが本来束ねるはずだった攻撃シーケンス(Critical)まで検知されなくなる」**可能性があります。抑制は「明確に無害と確信できるノイズ」に限り、攻撃の文脈になり得るシグナルは、抑制ではなく通知側のフィルタ(6 章の severity 閾値や型ルーティング)で静かにする——この使い分けが本番では効きます。


8. 本番運用:可観測性・コスト・過信しないこと

8.1 finding を一箇所に集約する

複数アカウント・複数リージョンの finding は、委任管理者アカウントに自動集約されます。さらに AWS Security Hub CSPM に流せば、Macie やサードパーティの検知と並べて横断的に優先順位づけできます。深掘り調査が必要なら Amazon Detective(GuardDuty 連携でグラフ可視化)、長期保管・分析なら S3 への finding エクスポートを設定します。「GuardDuty で検知 → Security Hub で集約 → Detective で深掘り」が王道の三段構えです。

8.2 コストモデル(資産に比例する設計を)

GuardDuty の料金は有効化したものに対する従量課金で、リージョンごとに異なります。概念を押さえると予算が読めます。

課金対象課金単位
基盤:CloudTrail 管理イベント100万イベントあたり
基盤:VPC Flow Logs / DNS ログGB あたり(量で逓減)
S3 ProtectionS3 データイベント 100万あたり
EKS Protection監査ログ 100万あたり
Runtime Monitoring保護 vCPU 時間あたり(最も高くなりやすい)
Malware Protection for EC2スキャンした GB あたり
Malware Protection for S3スキャン GB + オブジェクト評価数
RDS ProtectionvCPU(Aurora Serverless は ACU)あたり
Lambda ProtectionVPC Flow Logs 解析として GB あたり

新規有効化したリージョンには30日無料トライアルが付き、本番量での想定請求額を事前に把握できます。委任管理者アカウントからはメンバーアカウント別の想定使用量も見えます。コスト最適化の鉄則は4 章の繰り返し——ETD(無償)+ S3 Protection を土台に、Runtime Monitoring は重要ワークロードに限定。「とりあえず全部 ON」は、検知価値の薄い対象にまで vCPU 課金を払う結果になりがちです。

8.3 GuardDuty を過信しない——多層防御の再確認

最後に、最初のメンタルモデルへ戻ります。GuardDuty は検知であって防御ではありません。GuardDuty があっても:

  • 入口の防御は WAF が要る(L7 のフィルタ)。
  • 認証情報の漏洩そのものを減らすには、OIDC 鍵レス CI/CD最小権限の IAM が要る。
  • データ層の保護(暗号化・VPC エンドポイント・きめ細かいアクセス制御)は別途必要。

GuardDuty はこの多層防御の中で 「侵害の兆候をいち早く知らせ、対応を起動する層」 を担います。予防層をすり抜けた攻撃を早く見つけて早く封じ込める——それが GuardDuty に与えるべき正しい役割です。


9. まとめ:GuardDuty 本番チートシート

迷ったときの早見表です。

  • 何のサービスか:GuardDuty は検知(detection)であって防御(prevention)ではない。価値は「検知精度 × 検知後の対応速度(MTTR)」で決まる。
  • 基盤検知:有効化=即オン・追加費用なし。CloudTrail 管理イベント/VPC Flow Logs/DNS の独立した複製ストリームを解析(自分でログを有効化・保存・課金しない。エージェント不要)。DNS は AWS リゾルバ利用時のみ
  • 保護プラン資産に合わせて足す(YAGNI)。S3 Protection はほぼ常に推奨、Runtime Monitoring は vCPU 課金で最も高いので重要ワークロードに限定。Malware Protection for S3 は単独利用も可
  • Extended Threat Detection無償・自動。弱いシグナルを24時間窓で相関し、多段攻撃を 1つの AttackSequence:* finding(必ず Critical 9.0〜10.0) に束ねる。S3/EKS の相関には対応保護プランが必要。
  • 組織統制委任管理者 + auto_enable_organization_members = "ALL" で全アカウント・全リージョンを漏れなく有効化。グローバルイベントのため未使用リージョンも有効化
  • finding:型名 ThreatPurpose:Resource/Family.Mechanism!Artifact に意味がエンコードされる。severity は数値(Critical 9.0–10.0 / High 7.0–8.9 / Medium 4.0–6.9 / Low 1.0–3.9)。create-sample-findings で対応経路を事前検証。
  • 自動対応(最重要):EventBridge の detail.severity 数値マッチで High 以上を拾う。通知は広く・封じ込めは型の許可リストで狭く・破壊的操作は人間を挟む。EventBridge は at-least-once → 冪等必須。更新の遅延を縮めるため finding_publishing_frequency = FIFTEEN_MINUTES
  • チューニング:Suppression Rule は「無害と確信できるノイズ」だけ。攻撃シーケンスの材料になるシグナルは抑制で消さない(ETD はアーカイブ済みを見ない)。
  • 過信しない:GuardDuty は多層防御の一層。WAF・最小権限 IAM・鍵レス認証・暗号化を代替しない。

GuardDuty は「有効化すれば守ってくれる箱」ではなく、「検知 → 相関 → 行動」をどう設計するかで価値が10倍変わるサービスです。最大のレバレッジは検知そのものより、finding を冪等で安全な自動対応に変える配管にあります。

私はマルチアカウントのサーバーレス決済プラットフォームで、実際の金銭・カーボンクレジット・地域通貨を扱う基盤の IAM・可観測性・DR を横断実装し、「正しさ」を運用の注意深さではなくコードの構造と冪等性で担保してきました。GuardDuty の導入も同じ思想で設計します——①組織一括有効化で取りこぼしを構造的に消し、②資産に合わせて保護プランを最小コストで積み、③EventBridge → 冪等・スコープを絞った・取り消せる自動対応で MTTR を縮める。検知を「赤いダッシュボード」で終わらせず、運用に載る対応の仕組みまで作り切ります。

「自社の AWS に GuardDuty をどう設計し、どこまで自動対応を任せ、どうコストを抑えるか」——保護プランの選定から Terraform 実装、EventBridge 自動対応、組織統制、誤検知チューニングまで、一人 ×生成AI(Claude Code)で速く・安全に伴走できます。 要件の整理段階からでも、お気軽にご相談ください。


参考(公式ドキュメント)

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

GuardDuty 脅威検知基盤の導入・組織統制を、設計から実装まで

単一アカウントの有効化から、AWS Organizations による全社一括有効化・全リージョン・委任管理者まで。Terraform で再現可能な検知基盤を、最小権限・冪等・取りこぼしゼロで構築します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。