GuardDuty運用3ヶ月、毎日3000件アラートの地獄から脱出した話
GuardDutyで毎日5000件のアラート地獄に陥った実体験。False Positive対策とホワイトリスト設計で、実務レベルの運用設計を解説します。
GuardDutyを有効化したら地獄が始まった
うちのチーム、先月GuardDutyとSecurity Hubを全本番環境で有効化したんですよ。
最初の週だけで、アラートが毎日3000件以上来始めて。タスク作成されたり、Slackに流れてくるたびにチーム中がパニック状態。正直、導入を後悔しました。
「セキュリティはやっぱ大事だから、ちゃんと対応しよう」という建前で始まったんですが、実際に開けてみたら大半がfalse positiveなんです。ローカルDBへの接続試行、開発環境のRDP試行、Lambdaログの正常な例外。こういうのが「potentially malicious activity detected」と判定されるわけですよ。
3ヶ月本番で運用した結果、見えてきたのは「GuardDutyは導入じゃなく、運用設計で全てが決まる」ということでした。
最初の失敗:抑止のない有効化
実装初期、うちが踏んだ地雷を赤裸々に話します。
GuardDutyは有効化するだけで、即座に現在のAWS環境を分析し始めます。CloudTrail、VPCフローログ、DNS ログの3つのデータソースを組み合わせて、不正なアクティビティを検知するんですね。
ですが「どのアラートが本当か、ガセか判定する戦略なしに有効化したら、それはカオスでしかない」ということを身をもって学びました。
初期段階でやるべきだったことは以下の3つです。
1. Trusted IP Listの作成
開発環境、スキャン用インスタンス、定期バッチ処理といった「既知の正常なアクティビティ」をホワイトリスト化する。うちの場合、AWS内部のスキャンツール(Qualysとか)が毎日数百のポートスキャンを実行してたんですが、これをTrusted IPに登録することで劇的に減りました。
{
"TrustedIPList": {
"S3Bucket": "s3://my-guardduty-config/trusted-ips.txt",
"Format": "PLAINTEXT"
},
"IPs": [
"10.0.0.0/8", // Internal VPC
"203.0.113.45/32", // Qualys Scanner
"203.0.113.46/32" // Tenable Nessus
]
}
2. Threat Intel Feedの精査
GuardDutyが使用する脅威インテリジェンスフィードは、AWS独自のものと複数のパートナーから集約されてるんですが、企業によって「本当に脅威か」の定義は違うんですよね。
うちの場合、某大手SaaS企業のIPがfeed登録されてて、正当な業務用接続が全部flagされてた。こういう誤検知は、フィード選択で対応できます。
3. Suppression Rulesの事前構築
これが実装の肝ですね。GuardDutyのSuppression Rulesを使うと、特定の条件のアラートを自動で抑止できます。
{
"Name": "Suppress-Lambda-Exceptions",
"Criterion": {
"Type": "FINDING_TYPE",
"Value": "UnauthorizedAccess:IAMUser/PermissionDenied"
},
"ResourceFilters": {
"AccountId": ["123456789012"],
"Tags": {
"Environment": ["development"]
}
},
"Active": true
}
最初は「抑止したら脅威見落とすんじゃ?」って抵抗ありました。でも現実は逆。ノイズが多すぎると、本当の脅威が埋まるんです。人間の注意力はリソース制約があるんですよね。
AWS構成図:マルチアカウント下でのGuardDuty・Security Hub配置
実装した構成を図解します。
graph TB
subgraph "Organization (Central)"
ODH["🛡️ GuardDuty Delegated Admin<br/>Account"]
OSH["📊 Security Hub Delegated Admin<br/>Account"]
CVPC["VPC - Central Monitoring"]
CVPC -->|CloudTrail Events| ODH
CVPC -->|VPC Flow Logs| ODH
end
subgraph "Production Account"
PPVPC["VPC - Prod"]
PEC2["EC2 Instances"]
PS3["S3 Buckets"]
PPVPC -->|VPC Flow Logs| ODH
PEC2 -->|CloudTrail| ODH
PS3 -->|CloudTrail| ODH
end
subgraph "Development Account"
DPVPC["VPC - Dev"]
DEC2["EC2 Dev"]
DPVPC -->|VPC Flow Logs| ODH
DEC2 -->|CloudTrail| ODH
end
subgraph "Security Hub Aggregation"
SHCONSOLE["Security Hub Console<br/>Findings Aggregated"]
OSH -->|Findings| SHCONSOLE
end
ODH -->|Exports| S3EXPORT["S3 Export Bucket<br/>findings.jsonl"]
ODH -->|SNS Alerts| SNS["SNS Topic<br/>Critical Findings"]
SNS -->|Email/Slack| SOCEMAIL["SOC Team Notification"]
S3EXPORT -->|Athena Query| ATHENA["🔍 Athena Analysis<br/>Custom Queries"]
ATHENA -->|Grafana Dashboard| GRAFANA["📈 Grafana<br/>Visualization"]
SHCONSOLE -->|Remediation Rules| REMEDIATE["⚙️ Lambda Remediation<br/>Auto-Fix Logic"]
REMEDIATE -->|Update| PPVPC
style ODH fill:#ff9999
style OSH fill:#99ccff
style SHCONSOLE fill:#99ff99
style REMEDIATE fill:#ffcc99
この構成のポイントをまとめると:
- **中央アカウント(Delegated Admin)**にGuardDutyとSecurity Hubの管理を一元化することで、複数アカウントの統一的な運用が実現
- マルチアカウント配下の全EC2/S3から自動的にCloudTrail・VPCフローログを集約するので、見落としがない
- Finding exportをS3に流して、Athenaで長期分析できるから、トレンド把握が容易に
- 自動remediation LambdaでIAM権限エラーや不適切なセキュリティグループ設定を自動修正する仕組みを用意
アラート5000件の地獄から脱出した3つの工夫
1. 重大度別のしきい値調整
GuardDutyのFinding Type Filteringを活用して、本当に必要なものだけを検知対象にします。
import boto3
client = boto3.client('guardduty', region_name='ap-northeast-1')
# 重大度 HIGH 以上のみ抑止ルールから除外
finding_criteria = {
'Criterion': {
'severity': {
'Gte': 7 # 7以上 = HIGH/CRITICAL
},
'type': {
'Eq': ['UnauthorizedAccess:EC2/RDPBruteForce',
'CryptoCurrency:EC2/BitcoinTool',
'Trojan:EC2/PhishingDomain']
},
'region': {
'Eq': ['ap-northeast-1', 'us-east-1']
}
}
}
# Suppression Rule作成
response = client.create_filter(
DetectorId='detector-xxx',
Name='Critical-Findings-Only',
Description='Suppress non-critical findings',
Action='ARCHIVE',
FindingCriteria=finding_criteria
)
実装して2週間で、対応が必要なアラートは1日50件程度に落ち着きました。それまでは毎日3000件だったんで、圧倒的な改善ですね。
2. Security HubとGuardDutyの統合分析
Security Hubは複数のセキュリティサービスの結果を集約するんですが、GuardDutyとの相乗効果が地味に大きいんですよ。
うちが設定した統合フローはこんな感じです:
GuardDuty Findings
↓
Security Hub (Standards Compliance Check)
↓
Automation (EventBridge + Lambda)
↓
Auto-Remediation / Manual Review Queue
Security Hubの「Controls」機能で、CIS AWS Foundations Benchmarkのコンプライアンスを自動チェック。GuardDutyで脅威検知、Security Hubで「今のセキュリティ設定が適切か」を並行確認する感じです。
# Security Hub Findings を Athena で分析
import boto3
athena = boto3.client('athena')
query = """
SELECT
created_at,
finding_type,
severity,
resource_id,
COUNT(*) as incident_count
FROM security_hub_findings
WHERE created_at > date_format(current_date - interval '7' day, '%Y-%m-%d')
AND severity IN ('HIGH', 'CRITICAL')
GROUP BY created_at, finding_type, severity, resource_id
ORDER BY incident_count DESC
"""
response = athena.start_query_execution(
QueryString=query,
QueryExecutionContext={'Database': 'security_logs'},
ResultConfiguration={'OutputLocation': 's3://my-athena-results/'}
)
3ヶ月運用してわかったこと:GuardDuty単体では検知、Security Hubは可視化・コンプライアンス確認と役割を分けるのが正解です。
3. Slackへのインテリジェント通知設計
最初、全findingをSlackに流してました。結果、通知が多すぎてスルーされるようになった。まさに狼少年状態ですね。
そこで実装したのが、重大度・リソースタイプ・時間帯ごとに通知方法を変える仕組みです:
import json
import boto3
from datetime import datetime
slack_client = boto3.client('secretsmanager')
sns = boto3.client('sns')
def lambda_handler(event, context):
finding = json.loads(event['Records'][0]['Sns']['Message'])
severity = finding['detail']['severity']
finding_type = finding['detail']['type']
created_time = finding['detail']['updatedAt']
# 重大度に応じた通知戦略
if severity >= 8: # CRITICAL
# Slack + Email + PagerDuty
notify_to_slack(finding, channel='#security-critical', ping='@security-team')
notify_to_email('security-critical@company.com')
trigger_pagerduty_incident(finding)
elif severity >= 6: # HIGH
# Slack only (business hours)
if is_business_hours(created_time):
notify_to_slack(finding, channel='#security-alerts')
else:
# After hours: Queue for next business day review
queue_for_morning_review(finding)
else: # MEDIUM/LOW
# Weekly digest email
queue_for_weekly_digest(finding)
return {'statusCode': 200}
def is_business_hours(timestamp):
hour = datetime.fromisoformat(timestamp).hour
return 9 <= hour <= 18 # JST 9:00-18:00
def notify_to_slack(finding, channel, ping=''):
slack_token = slack_client.get_secret_value(
SecretId='slack-webhook-url'
)['SecretString']
message = f"""
{ping} **GuardDuty Alert - {finding['detail']['severity']}**
Type: {finding['detail']['type']}
Resource: {finding['detail']['resource']['instanceDetails']['instanceId']}
Region: {finding['detail']['region']}
Time: {finding['detail']['updatedAt']}
Description: {finding['detail']['description']}
"""
# Slack通知実装...
これで、本当に対応が必要なアラートはすぐに目に入り、バックグラウンドノイズは削減されました。朝1時間で確認できるレベルになってます。
1年運用で気づいた、実装の本当のコツ
Suppression Rule は「永遠」じゃない
3ヶ月ごとに見直しが必須です。開発環境のセキュリティグループが変わったり、新しいツールを入れたら、過去のSuppression Ruleが通用しなくなるんですよ。
うちは Confluence に「Suppression Rule 台帳」を作って、なぜそれを抑止してるのかをドキュメント化しました。理由が「もう不要」になったら削除。
| Rule Name | Reason | Created | Last Reviewed | Delete? |
|---|---|---|---|---|
| Suppress-Lambda-IAMDeny | Lambda logs permission errors as expected behavior | 2026-02 | 2026-05 | NO |
| Suppress-Dev-RDPScan | Qualys vulnerability scanning in dev env | 2026-02 | 2026-05 | YES - Moved Qualys to prod |
| Suppress-Batch-DynamoDB | Batch job occasional throttling logs | 2026-01 | 2026-05 | REVIEW - Batch optimized |
False Positive との付き合い方は「完全排除」ではなく「優先順位」
100%のfalse positiveを排除するのは不可能です。でも「本物の脅威を見落とさない」なら可能。
そのために有効なのが、custom findings export + Athena分析 です。
-- 過去3ヶ月のFinding分析
SELECT
finding_type,
COUNT(*) as count,
COUNT(DISTINCT resource_id) as unique_resources,
MAX(severity) as max_severity
FROM guardduty_findings
WHERE created_at >= date_format(current_date - interval '90' day, '%Y-%m-%d')
GROUP BY finding_type
HAVING COUNT(*) > 10
ORDER BY count DESC;
このクエリで「毎日来るけど対応不要」なFinding Typeが見える。そういうのはSuppression Ruleの候補になります。
Security Hub Standards の活用
GuardDutyは「脅威検知」ですが、Security Hubの出番は「セキュリティベースラインの維持」です。
有効化すべきStandardsは以下の3つ。
- CIS AWS Foundations Benchmark v1.4.0 — IAM、S3、CloudTrailの設定がベストプラクティスに従ってるかチェック
- PCI DSS — 決済情報を扱ってなくても、セキュリティベストプラクティスの参考になる
- AWS Foundational Security Best Practices — AWS推奨設定の確認に必須
これら3つを運用すると、GuardDutyの脅威検知 + Security Hubのコンプライアンス確認 で、脅威と基盤のセキュリティが両立します。
実運用で見えた、やらなくて良いこと
1. 全Finding自動修復
「セキュリティだからすべて自動修復しよう」って思いました。でも危険です。
Lambda Remediationで自動修復を試みた結果、こんなことが起きました:
- セキュリティグループが自動削除されて本番が落ちた
- IAMロールの権限が自動削除されてデプロイが失敗
- CloudTrail ログが自動無効化された
セキュリティと可用性のバランスが大事なんです。
うちの現在の方針はこれです:
| 重大度 | 対応方法 | 理由 |
|---|---|---|
| CRITICAL | 自動修復(ロールバック可能) | 即座の対応が必須、失敗時の復旧手段あり |
| HIGH | 管理者承認後に修復 | 本番への影響が大きい可能性 |
| MEDIUM以下 | 手動レビュー | 誤検知の可能性が高い |
2. リアルタイム通知の過度な期待
GuardDutyのFindingが検知されるまで、平均5〜10分のラグがあります。完全リアルタイムではないんですよ。
だから「Slackに即座に来たから即対応」という運用は不適切。むしろ日中1回、朝夕で確認するぐらいが現実的です。
3. GuardDuty + WAF + Network Firewallの3つ全部有効化
シグネチャ検知ツールをいっぱい有効化すると、組織全体でアラートが数倍になります。地味に厳しい。
うちの実装はこう役割分けしてます:
- Network Firewall → NW層の脅威検知(Intrusion Prevention)
- GuardDuty → CloudTrail・VPC Flow Logs分析(行動分析)
- WAF → L7アプリケーション層(SQLインジェクション等)
この3つをレイヤー分けすることで、各層の役割が明確になり、誤検知が減ります。
まとめ
1. GuardDutyはアラート数が全てではない
有効化直後は数千件のアラートが来ますが、大半はfalse positive。Suppression Rules と Trusted IP List を先に構築して、運用可能な量に絞ることが最優先です。
2. False Positive との付き合い方は「排除」じゃなく「優先順位」
100%のfalse positiveを排除するのは不可能。代わりに、本当の脅威を見落とさない設定と、重大度別の対応方針を決めることが大事。
3. Security Hubと併用することで、脅威検知 + コンプライアンス確認が可能に
GuardDuty単体では脅威検知だけですが、Security HubのStandards(CIS、PCI DSS等)と組み合わせることで、セキュリティベースラインも同時に維持できます。
4. 自動修復は「段階的」に
CRITICAL以外は手動レビューを挟むことで、セキュリティと可用性のバランスが取れます。
5. ツール選択はレイヤー分け
Network Firewall(NW層)、GuardDuty(行動分析)、WAF(L7層)を役割分けすることで、各々の検知精度が高まり、運用負荷も最適化できます。
正直、3ヶ月目時点ではまだ試行錯誤の途中です。でも「アラート数の多さ=セキュリティレベルの高さ」じゃなく、「適切な検知と対応の継続性こそが本当のセキュリティ」だってことに気づけたのは大きい。
皆さんが同じ地獄にハマらないよう、現実的な実装方法を共有できたら幸いです。