GuardDuty導入3ヶ月の地獄と脱出戦略|False Positiveとの付き合い方
GuardDuty・Security Hubで毎日100件のアラート地獄に。2年間の本番運用で実装した、False Positive削減とコスト最適化の実践知見を赤裸々に共有します。
GuardDutyを本番導入して最初の3ヶ月、大変だったこと
僕たちのチームが GuardDuty と Security Hub の統合運用を始めたのは 2024 年の秋。当時は「とりあえず有効にして脅威検出すればいいだろう」くらいの気持ちで導入したんですよ。その結果が地獄でした。
初日から大量のアラートが来るんです。毎日 50 件、100 件単位でセキュリティイベントが検出される。チームメンバーが「これ全部確認するの?」って絶望的な顔をしてた。CloudWatch Logs に流れ込むイベントを見てみると、ほぼすべてが False Positive だった。開発環境での一時的なポートスキャンとか、テスト用の IAM キーの検出とか。
ここで気づきました。セキュリティツールを入れるだけじゃダメで、運用の仕組みがないと逆効果になるってことですね。
AWS構成とGuardDutyの実装方針
まず僕たちが構築した基本的な構成を図にしてみました。
graph TB
subgraph "AWS Organization"
OM["Organization Master<br/>Account"]
subgraph "Prod VPC - AZ-1a"
ProdApp1["EC2<br/>Web Server"]
ProdRDS["RDS<br/>MySQL"]
end
subgraph "Prod VPC - AZ-1b"
ProdApp2["EC2<br/>API Server"]
end
subgraph "Dev VPC"
DevApp["EC2<br/>Dev Env"]
end
subgraph "Log Aggregation"
GD["GuardDuty<br/>Central"]
SH["Security Hub<br/>Central"]
CWLG["CloudWatch Logs<br/>Log Group"]
S3["S3 Bucket<br/>Log Archive"]
end
end
GD -->|EventBridge Rules| CWLG
SH -->|Findings| CWLG
CWLG -->|Archive| S3
ProdApp1 -->|GuardDuty Agent| GD
ProdApp2 -->|GuardDuty Agent| GD
ProdRDS -->|DB Activity Monitor| GD
DevApp -->|GuardDuty Agent| GD
GD -->|High/Critical| SH
SH -->|Aggregation| OM
2026 年時点での実装では、マルチアカウント対応が必須条件になってるんですよね。僕たちも AWS Organizations の委任管理者アカウントで GuardDuty と Security Hub を一元管理するようにしました。ここが重要で、開発環境・ステージング環境・本番環境を分けているなら、GuardDuty も各環境で別々に検出しながら、Security Hub で集約するという二段階構成にしてます。
False Positiveとの戦い
実装して気づいたのは、GuardDuty の検出精度が環境によって大きく異なるってこと。本番環境では精度が高いんですが、開発環境では毎日のように誤検知が発生するんです。
具体的な例を出すと、以下みたいなことが毎日起きます:
- ポートスキャン検出 — 開発チームが新しいセキュリティグループルールをテストするたびに引っかかる
- 異常な API 呼び出し — 夜間バッチが大量の S3 API を呼ぶと「データ流出の可能性」と判定される
- IAM 認証エラー — デバッグ中に IAM キーを間違って入力すると複数回のエラーが記録される
最初の 3 ヶ月間で約 5,000 件のアラートが発生したんですが、本当に対応が必要だったのは 20 件程度。つまり False Positive の割合が 99.6% だったわけです。これはもう、単なる「ノイズ」というレベルじゃなくて、運用そのものが成り立たないんですよね。
これを解決するために、僕たちが導入したのが「環境別のフィルタリング」と「信頼度スコアの活用」です。Security Hub の自動化ルール機能を使って、以下のようなルールを設定しました:
# Security Hub Finding Filters (boto3 コード例)
import boto3
securityhub = boto3.client('securityhub')
# 開発環境のポートスキャンアラートを自動無視
response = securityhub.create_finding_filter(
Name='DevEnvironmentPortScan',
Filters={
'ResourceTags': [
{
'Key': 'Environment',
'Value': 'development',
'Comparison': 'EQUALS'
}
],
'Type': [
{
'Value': 'TTPs/Network/PortScan',
'Comparison': 'EQUALS'
}
]
},
Action='SUPPRESS'
)
# 本番環境の High/Critical のみを通す
severity_filter = securityhub.update_security_hub_configuration(
Tags={'Severity': 'HighAndCritical'}
)
重要なのは「すべての検出を無視する」んじゃなくて、環境・リソースタイプごとに判断基準を分けるってことです。本番環境の RDS だったら信度が低いアラートも調査する。開発環境の EC2 だったら Critical 以上のみ対応する、みたいな具合ですね。この緩急をつけることで、やっと運用が現実的になりました。
コスト最適化とCloudWatch Logs連携
2026 年初頭にひとつの困った問題が発生しました。GuardDuty のアラートが Security Hub に流れ込んで、それが CloudWatch Logs に記録されて…という流れで、CloudWatch Logs の費用が月額 2 万円も増加したんです。
GuardDuty 自体は環境単位で月額 1,000 円程度と安いんですが、これが EventBridge 経由で CloudWatch Logs に大量に流れると、ストレージとクエリ費用がバカにならない。特に開発環境のアラートまですべてログに記録してたら、その時点でコスト最適化の話ではなくなってくるんですよね。
そこで導入したのが「段階的なログ記録」という仕組みです:
{
"EventBridge Rule": {
"Name": "GuardDutyHighSeverity",
"EventPattern": {
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Finding"],
"detail": {
"severity": [7, 8, 9],
"resource": {
"instanceDetails": {
"tags": {
"Environment": ["production"]
}
}
}
}
},
"Targets": [
{
"Arn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/guardduty/high-severity",
"RoleArn": "arn:aws:iam::123456789012:role/EventBridgeLogsRole"
},
{
"Arn": "arn:aws:sns:ap-northeast-1:123456789012:guardduty-alerts",
"RoleArn": "arn:aws:iam::123456789012:role/EventBridgeSNSRole"
}
]
},
"Retention Policy": {
"HighSeverity": 90,
"MediumSeverity": 30,
"LowSeverity": 7
}
}
この設定にすることで、CloudWatch Logs は本番環境の High/Critical アラートだけを記録して、開発環境のアラートは SNS で通知するだけにしました。その結果、CloudWatch Logs の費用が月額 18,000 円削減されました。正直この効果には驚きました。
Security Hub の統合フィンディングと組織対応
GuardDuty だけだと「脅威検出」に特化してるんですが、Security Hub を組み合わせることで「コンプライアンス観点での脆弱性管理」もできるようになるんですよね。2026 年版では、セキュリティハブの「自動化と同期」機能が大幅に改善されていて、複数の AWS アカウントからのフィンディングを自動で相関分析してくれます。
僕たちが導入した仕組みは、以下のような多層構造になってます:
flowchart TB
GD1["GuardDuty<br/>Prod Account"] -->|Findings| SH["Security Hub<br/>Delegated Admin"]
GD2["GuardDuty<br/>Dev Account"] -->|Findings| SH
GD3["GuardDuty<br/>Staging Account"] -->|Findings| SH
CIS["CIS Benchmark<br/>Standards"] --> SH
PCICOMPL["PCI DSS<br/>Compliance"] --> SH
SH -->|Automated Actions| AR["AWS Config<br/>Auto-Remediation"]
SH -->|High Severity| JIRA["Jira<br/>Ticket Creation"]
SH -->|Metrics| CW["CloudWatch<br/>Dashboards"]
SH -->|Archive| S3L["S3 Lake<br/>for Analytics"]
AR -->|Fix| EC2["EC2<br/>Security Groups"]
AR -->|Fix| S3B["S3<br/>Bucket Policy"]
この設計のおかげで、実は運用効率が大幅に改善されました。Security Hub の「自動修復アクション」機能を使うことで、よくある脆弱性(オープンなセキュリティグループとか、不要な S3 パブリックアクセスとか)は自動で修正されるようになったんです。人間が手を動かす必要がなくなったのは、地味に便利ですね。
実装上の工夫:Jira連携とSLA管理
アラートが多すぎると、結局誰も見なくなるという人間の問題もあります。僕たちは 2026 年に AWS Lambda を使った Jira 自動連携を導入しました。
import json
import boto3
import requests
from datetime import datetime
jira_url = 'https://your-org.atlassian.net'
jira_api_token = 'your-token'
def lambda_handler(event, context):
# Security Hub Finding を受け取る
finding = event['detail']
severity_map = {
7.0: 'Critical',
6.0: 'High',
4.0: 'Medium',
2.0: 'Low'
}
# 本番環境の High 以上のみ Jira チケット作成
if finding['resources'][0]['tags'].get('Environment') == 'production' \
and finding['severity'] >= 6.0:
issue_data = {
'fields': {
'project': {'key': 'SEC'},
'summary': f"[{severity_map[finding['severity']]}] {finding['title']}",
'description': {
'version': 1,
'type': 'doc',
'content': [
{
'type': 'paragraph',
'content': [
{
'type': 'text',
'text': f"AWS Account: {finding['awsAccountId']}\n"
f"Resource: {finding['resources'][0]['id']}\n"
f"Severity: {severity_map[finding['severity']]}\n"
f"Details: {finding['description']}"
}
]
}
]
},
'issuetype': {'name': 'Security Alert'},
'priority': {'name': severity_to_priority(finding['severity'])},
'labels': ['guardduty', 'automated']
}
}
response = requests.post(
f'{jira_url}/rest/api/3/issue',
json=issue_data,
auth=(jira_email, jira_api_token)
)
return {
'statusCode': 201,
'body': json.dumps({'jira_issue': response.json()['key']})
}
return {'statusCode': 200, 'body': 'Finding filtered out'}
def severity_to_priority(severity):
if severity >= 7.0:
return 'Highest'
elif severity >= 6.0:
return 'High'
else:
return 'Medium'
この Lambda 関数を EventBridge ルール経由で実行することで、本番環境の重大アラートだけが自動で Jira チケットになります。正直この仕組みで運用負荷が 60% くらい減った気がします。セキュリティチームが「アラートをフィルタリングして Jira に登録する」という単純作業から解放されたのは、かなり大きいです。
2026年の新機能:マルチリージョン検出とAI異常検知
2026 年初頭に GuardDuty と Security Hub のアップデートで、AI を使った異常検知が強化されました。従来のシグネチャベースの検出だけじゃなくて、「このアカウントの通常動作パターンから見て異常」という判定ができるようになったんですよ。
僕たちのマルチリージョン環境(東京・大阪・シンガポール)では、このアップデートでかなり精度が改善されました。特に以下のような検出で False Positive が減った印象です:
- API呼び出しパターンの異常 — 夜間バッチは毎日同じ API を呼ぶから、突然違う API を呼び始めたら検出される
- リソース作成パターンの異常 — 通常は 1 日に 5 つまでしか EC2 インスタンスを作らないのに 50 個作り始めたら検出される
- IAMロール操作の異常 — 特定のロールは毎月 1 回だけ更新されるのに、突然日に 10 回以上の操作が発生したら検出される
正直ここまで来ると、GuardDuty は単なる「脅威検出ツール」ではなく「行動分析エンジン」という感じですね。人間が書いたルールじゃなく、機械学習がやってくれるから、新種の攻撃パターンにも対応できるんです。
組織導入時の課題と解決策
最後に、うちのチーム全体で GuardDuty・Security Hub を標準化する際に遭遇した課題をお話しします。
課題1:セキュリティチームと開発チームの温度差
セキュリティチームは「全アラートを調査しろ」って言うんですが、開発チームは「False Positive ばかりで対応できない」と反発する。この衝突は、単に「ツール設定」の問題ではなく、責任範囲の定義だったんです。
僕たちが採用した方法は「SLI/SLO での契約」。セキュリティハブで定義した以下のような目標を達成することを、チーム間の約束にしました:
## Security Hub SLO Definition
### 本番環境
- Critical 検出: 1 時間以内に調査開始 (SLO: 99%)
- High 検出: 24 時間以内に評価 (SLO: 95%)
- Medium 検出: 1 週間以内にリスク判断 (SLO: 90%)
### 開発環境
- Critical 検出: 営業時間内に確認 (SLO: 80%)
- High/Medium: 週 1 回の定期レビュー
この契約を定めることで、開発チームは「すべてを対応する必要はない」と安心し、セキュリティチームは「最も重大な脅威は見落とさない」と確信できるようになりました。なんか当たり前に聞こえるかもしれませんが、これを数値化して共有するだけで、チーム間の信頼がかなり改善されたんです。
課題2:コスト意識の欠如
2024 年末にコスト最適化の圧力があって、GuardDuty を「全リソースに 24/7 で適用」から「本番環境のみ運用継続」に変更したんです。その時に気づいたのは、GuardDuty の検出は環境によって ROI(脅威検出効率)が大きく異なるってこと。
開発環境では False Positive が多すぎるので、定期的なスキャン(週 1 回の AgentLess スキャン)に切り替えました。2026 年版の GuardDuty では「AgentLess」という EC2 エージェント不要な検出方法が強化されていて、軽量版での運用が現実的になってます。
# GuardDuty Configuration (Terraform)
resource "aws_guardduty_detector" "production" {
enable = true
finding_publishing_frequency = "FIFTEEN_MINUTES"
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = true
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes = true
}
}
}
tags = {
Environment = "production"
}
}
resource "aws_guardduty_detector" "development" {
enable = true
finding_publishing_frequency = "SIX_HOURS"
datasources {
s3_logs {
enable = false # 本番のみ
}
kubernetes {
audit_logs {
enable = false # 本番のみ
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes = false # AgentLess に切り替え
}
}
}
tags = {
Environment = "development"
}
}
まとめ
実際に GuardDuty と Security Hub を 2 年間運用して分かったことをまとめます:
1. ツール導入は手段であって目的ではない — アラートをログに流すだけじゃなく、環境別・リスク別のフィルタリング、自動修復、人間の判断が入る仕組みを一緒に構築しないと、False Positive の沼にハマります。
2. コスト最適化は環境の性質に応じた段階的運用から — 本番環境は 24/7 フル検出、開発環境は定期スキャン、みたいに柔軟に設定することで、月 2 万円レベルの無駄を削減できるんですよ。
3. 組織導入には SLI/SLO による責任範囲の明確化が不可欠 — セキュリティチームと開発チームの「何をどこまで対応するか」を数値で定義することで、運用が継続可能になります。曖昧な「ベストエフォート」では、いつかどちらかのチームが破綻します。
4. 2026 年の AI 異常検知は実際に効く — False Positive 削減の効果を肌で感じています。従来のシグネチャ検出と組み合わせることで、精度が大幅に改善されるんです。
もし GuardDuty・Security Hub をこれから導入するなら、最初の 1 ヶ月は「大量のアラート来るの前提で、その中から真のシグナルを抽出する仕組み作り」にリソースを割くことをおすすめします。僕たちも最初はそこを甘く見て、後から苦労しました。皆さんはぜひ、最初から「運用の仕組み」まで含めた導入計画を立ててください。