Macie を本番環境で 6 ヶ月運用して気づいたこと|False Positive との付き合い方
S3 監査で Macie を導入したら最初は False Positive 地獄。6ヶ月の試行錯誤を経て、ようやくまともに使いこなせるようになった話と実装パターン。
本番運用で気づいた Macie の実像
去年、うちのチームで大規模な S3 監査プロジェクトが始まったんですよ。当初は「Macie があれば PII も個人情報も自動検出できるだろう」という楽観的な考えだったんですが、実際に導入してみるとそんなに甘くなかった。正直、最初の 3 ヶ月はアラートの嵐で、False Positive 対応に疲弊していました。
でも 6 ヶ月運用してみて、ようやく「ああ、こういう使い方をするツールなんだ」という実感が湧きました。今日はその試行錯誤の過程で得た知見を、同じような状況にいるエンジニアと共有したいと思います。
Macie の基本動作──実際に何ができるのか
まず、Macie 2026 年版の全体像を整理しておきましょう。つい最近アップデートがあって、生成 AI を使った検出精度が向上しているんです。
Macie の主な役割は大きく 3 つある。
1. 機密データの自動検出
S3 バケット内のオブジェクトをスキャンして、PII(氏名、メールアドレス、クレジットカード番号)、PHI(健康情報)、API キーなどを検出します。正規表現ベースのカスタムマネージドデータ識別子も定義できるので、社内独自の機密情報フォーマットにも対応可能だ。
2. リスク評価とスコアリング
バケットごとにリスクスコアが自動算出されます。暗号化の有無、公開設定、アクセスパターンなどを総合判定するので、「どのバケットから優先的に対応すべきか」を一目で判断できるんですよね。
3. 自動分類とコンプライアンスレポート
検出結果は Organizations レベルで統一管理され、定期レポートが自動生成されます。うちはこれで監査対応の工数が 60% 削減されたんですよ。
実際に運用してみると、この 3 つは密接に連動しているということに気づきます。検出精度が高いだけでは意味がなくて、それをいかに組織全体で運用できるかが重要だ。
実装パターン──うちのチームが採用した構成
graph TB
subgraph "Data Sources"
S3_Prod["S3 Production<br/>Buckets"]
S3_Staging["S3 Staging<br/>Buckets"]
end
subgraph "AWS Account (セキュリティアカウント)"
Macie["AWS Macie<br/>Central Monitoring"]
EventBridge["EventBridge<br/>Rules"]
Lambda["Lambda<br/>Autoresponse"]
SNS["SNS Topics"]
end
subgraph "Logging & Analysis"
CloudWatch["CloudWatch Logs<br/>& Metrics"]
S3_Macie_Findings["S3 Findings Bucket<br/>JSON Export"]
Athena["Athena<br/>Query Engine"]
end
subgraph "Notifications"
Slack["Slack Webhook"]
SecurityTeam["Security Team<br/>Console"]
end
S3_Prod -->|"スキャン"| Macie
S3_Staging -->|"スキャン"| Macie
Macie -->|"CRITICAL 検出"| EventBridge
Macie -->|"定期エクスポート"| S3_Macie_Findings
EventBridge -->|"トリガー"| Lambda
EventBridge -->|"通知"| SNS
Lambda -->|"自動対応"| S3_Prod
Lambda -->|"ログ記録"| CloudWatch
SNS -->|"POST"| Slack
SNS -->|"コンソール"| SecurityTeam
S3_Macie_Findings -->|"分析"| Athena
Athena -->|"可視化"| CloudWatch
この構成は、実は段階的に進化していったものなんです。最初は Macie の検出結果をコンソールで手動確認していたんですが、あまりにも運用効率が悪くて。
段階 1(初期) Macie 有効化 → コンソール確認のみ → セキュリティチームが手動レビュー
段階 2(1 ヶ月目) EventBridge で CRITICAL/HIGH の検出を SNS 通知 → Slack で即座に把握
段階 3(3 ヶ月目) Lambda で自動対応ロジック実装 → 低リスクの検出は自動削除、高リスクは自動隔離
段階 4(6 ヶ月目) Athena で JSON ベースの検出履歴分析 → トレンド把握と False Positive パターン抽出
こういった進化の過程を経ることで、初めて「Macie を使いこなしている」という感覚が出てきました。
False Positive 削減の工夫
正直なところ、Macie の初期設定だと False Positive がかなり出ます。特に大量のログファイルやテスト データベースダンプがある環境では、検出の 70% が誤検知だったりします。
最初のうち、セキュリティチームは「これ全部確認しろ」って言ってきたんですが、さすがに現実的じゃないので、段階的な除外戦略を導入しました。
1. マネージドデータ識別子の調整
Macie には 200+ の組み込み検出パターンがあるんですが、これをすべて有効にするのは得策ではありません。うちのチームでは、組織の特性に応じて選別しました。
{
"enabled_identifiers": [
"PII - AWS Credentials (exposed access keys)",
"PII - US Social Security Number",
"PII - Credit Card Number",
"PHI - HIPAA covered entity data",
"PII - Email Address (enhanced detection)"
],
"disabled_for_staging": [
"Generic credentials patterns",
"Database password strings"
]
}
ステージング環境では「Generic credentials patterns」を無効化することで、テストユーザーの dummy password がアラートになるという無駄を排除しました。
2. カスタムマネージドデータ識別子の作成
デフォルト識別子では捉えきれない、社内独自の機密データ形式があったんです。例えば、内部システムで使ってるユーザーID の形式(EMP-XXXXX-ZZZ みたいな)が個人情報に該当するケースですね。
(?i)EMP-[0-9]{5}-[A-Z]{3}
こういった正規表現をカスタム識別子として登録することで、本当に検出が必要なデータに絞り込めました。
3. S3 ブロックリスト戦略
Macie はバケット単位でスキャン対象を絞り込めます。うちは以下のような戦略で False Positive を削減しました。
| カテゴリ | スキャン対象 | 理由 |
|---|---|---|
| 本番アプリケーション DB バックアップ | ✓ | 実際に機密データを含む可能性が高い |
| ログアーカイブ | ✗ | 構造化ログは検出率が低く、アラート疲れの主因 |
| テストデータ | ✗ | ダミーデータばかりで False Positive の塊 |
| ML トレーニングデータセット | ◇ | カスタム識別子のみ有効化 |
| 監査証跡(CloudTrail) | ✓ | JSON 形式で検出精度が高い |
この選別だけで、初期状態に比べて True Positive の割合が 30% → 85% に改善しました。
EventBridge + Lambda による自動対応パターン
アラートを出すだけじゃ意味がないので、自動対応ロジックを組みました。優先度に応じて異なるアクションを実行するようにしたんです。
import json
import boto3
import logging
from datetime import datetime
s3_client = boto3.client('s3')
macie_client = boto3.client('macie2')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""Macie 検出結果に基づいて自動対応を実行"""
detail = event['detail']
severity = detail['severity']['description']
findings = detail['findings']
logger.info(f"Processing {len(findings)} findings with severity: {severity}")
for finding in findings:
bucket_name = finding['resourcesAffected']['s3Bucket']['name']
object_key = finding['resourcesAffected']['s3Object']['key']
finding_type = finding['type']
# CRITICAL: AWS Credentials の場合、即座にオブジェクトをロック
if severity == 'CRITICAL' and 'Policy:IAMUser/ManagedPolicy' in finding_type:
try:
s3_client.put_object_legal_hold(
Bucket=bucket_name,
Key=object_key,
LegalHold={'Status': 'ON'}
)
logger.warning(f"Legal hold applied to {bucket_name}/{object_key}")
send_critical_alert(bucket_name, object_key, finding_type)
except Exception as e:
logger.error(f"Failed to apply legal hold: {str(e)}")
send_critical_alert(bucket_name, object_key, finding_type)
# HIGH: PII 検出の場合、セキュリティアカウントに移動
elif severity == 'HIGH' and 'PII' in finding_type:
quarantine_object(bucket_name, object_key)
logger.info(f"Quarantined {bucket_name}/{object_key}")
# MEDIUM: アクセス権限を制限
elif severity == 'MEDIUM':
restrict_bucket_access(bucket_name)
logger.info(f"Restricted access to {bucket_name}")
return {
'statusCode': 200,
'body': json.dumps(f'Processed {len(findings)} findings')
}
def quarantine_object(bucket_name, object_key):
"""疑わしいオブジェクトを隔離用バケットに移動"""
quarantine_bucket = 'security-quarantine-bucket'
copy_source = {'Bucket': bucket_name, 'Key': object_key}
try:
s3_client.copy_object(
CopySource=copy_source,
Bucket=quarantine_bucket,
Key=f"quarantine/{datetime.now().isoformat()}/{object_key}",
ServerSideEncryption='AES256'
)
# 元のオブジェクトを削除
s3_client.delete_object(Bucket=bucket_name, Key=object_key)
except Exception as e:
logger.error(f"Quarantine failed: {str(e)}")
raise
def send_critical_alert(bucket, key, finding_type):
"""CRITICAL な検出結果をセキュリティチームに即座に通知"""
sns = boto3.client('sns')
message = f"""
🚨 CRITICAL Macie Finding 🚨
Bucket: {bucket}
Object: {key}
Type: {finding_type}
Timestamp: {datetime.now().isoformat()}
即座のレビューが必要です。
"""
sns.publish(
TopicArn='arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:security-critical',
Subject='[CRITICAL] AWS Macie Detection',
Message=message
)
def restrict_bucket_access(bucket_name):
"""バケットの public アクセスを自動ブロック"""
s3_client.put_public_access_block(
Bucket=bucket_name,
PublicAccessBlockConfiguration={
'BlockPublicAcls': True,
'IgnorePublicAcls': True,
'BlockPublicPolicy': True,
'RestrictPublicBuckets': True
}
)
このコードは 6 ヶ月運用で何度も改善されたもので、今はかなり堅牢になってます。特に大事なのが、CRITICAL と HIGH、MEDIUM の処理を分けている部分。権限の強さに応じた段階的な対応ができるので、セキュリティとビジネス継続性のバランスが取れるんです。
最初は「全部自動削除しろ」みたいな要件だったんですが、さすがにそれは危険だということで、段階的なアプローチに落ち着きました。
Athena + QuickSight で検出トレンドを可視化
運用していく中で「本当に Macie の検出は有効なのか」「False Positive はどのくらいあるのか」という問いが上がってきたんです。そこで、JSON エクスポートされた検出結果を Athena で分析することにしました。
-- 週ごとの検出数トレンド
SELECT
DATE_TRUNC('week', FROM_ISO8601_TIMESTAMP(createdAt)) as week,
severity,
COUNT(*) as detection_count,
COUNT(CASE WHEN confidence >= 90 THEN 1 END) as high_confidence_count
FROM macie_findings
WHERE createdAt >= DATE_FORMAT(CURRENT_TIMESTAMP - INTERVAL '12' WEEK, '%Y-%m-%dT%H:%i:%sZ')
GROUP BY 1, 2
ORDER BY 1 DESC, 2;
-- バケット別のリスクスコア推移
SELECT
s3_bucket_name,
severity,
COUNT(*) as total_findings,
AVG(confidenceScore) as avg_confidence,
MAX(confidenceScore) as max_confidence
FROM macie_findings
WHERE createdAt >= DATE_FORMAT(CURRENT_TIMESTAMP - INTERVAL '1' MONTH, '%Y-%m-%dT%H:%i:%sZ')
GROUP BY 1, 2
ORDER BY 3 DESC;
-- False Positive の可能性が高いパターン抽出
SELECT
type,
COUNT(*) as detection_count,
COUNT(CASE WHEN archived = true THEN 1 END) as archived_count,
ROUND(100.0 * COUNT(CASE WHEN archived = true THEN 1 END) / COUNT(*), 2) as archive_rate
FROM macie_findings
WHERE createdAt >= DATE_FORMAT(CURRENT_TIMESTAMP - INTERVAL '3' MONTH, '%Y-%m-%dT%H:%i:%sZ')
GROUP BY 1
HAVING COUNT(*) > 50
ORDER BY 4 DESC;
こういった分析結果を QuickSight でダッシュボード化したら、セキュリティチームと開発チームの認識がだいぶ揃いました。データで「False Positive が実は 15% しかない」ということを示せたのは大きかったです。
xychart-beta
title Macie 検出数とアーカイブ率の推移(直近 12 週)
x-axis [Week1, Week2, Week3, Week4, Week5, Week6, Week7, Week8, Week9, Week10, Week11, Week12]
y-axis "検出数 / アーカイブ率(%)" 0 --> 250
line [145, 167, 152, 198, 210, 189, 176, 201, 215, 198, 182, 156]
line [8, 12, 11, 15, 18, 22, 19, 25, 28, 24, 20, 16]
このグラフを見ると、運用が進むにつれて検出数は安定し、アーカイブ率(False Positive と判定して除外した割合)が低下していることがわかります。つまり、初期段階での過度なアラートが減り、真の脅威に集中できるようになったってわけですね。
Organizations での一元管理──運用スケーラビリティ
うちは複数の AWS アカウントを持ってるんですが、Macie の真価は Organizations での統制にあると感じてます。
セキュリティアカウントから全子アカウントの Macie を一元管理できるので、組織全体のリスク状況を一目で把握できるんです。
import boto3
from datetime import datetime, timedelta
macie_admin = boto3.client('macie2', region_name='ap-northeast-1')
def get_organization_findings_summary():
"""全子アカウントの検出結果をサマリー"""
response = macie_admin.describe_organization_configuration()
all_accounts_enabled = response['allAccountsEnabled']
# 全子アカウントのメンバーを取得
members_response = macie_admin.list_members(
onlyAssociated=True
)
summary = {
'total_accounts': len(members_response['members']),
'account_findings': []
}
# 各アカウントの検出サマリーを集計
for member in members_response['members']:
account_id = member['accountId']
findings_response = macie_admin.list_findings(
filterCriteria={
'criterion': {
'lastSeen': {
'gte': int((datetime.now() - timedelta(days=7)).timestamp() * 1000)
}
}
}
)
summary['account_findings'].append({
'account_id': account_id,
'email': member['email'],
'findings_count': len(findings_response['findings']),
'status': member['relationshipStatus']
})
return summary
# 実行例
summary = get_organization_findings_summary()
for account in summary['account_findings']:
print(f"Account: {account['account_id']}, Findings: {account['findings_count']}")
こういった一元管理ができると、「どのアカウントが最もリスクが高いのか」「新しく追加されたアカウントはちゃんと Macie が有効になっているか」みたいなチェックが自動化できます。
ただし、気をつけないといけないのは、Macie は region-specific なので、複数リージョンを使ってる場合は各リージョンで有効化する必要があるということ。うちはこれで初期段階で失敗しました。
コスト最適化の現実
ここが重要な話なんですが、Macie のコストはなかなかバカにならないんですよ。特に大量のオブジェクトがある環境では、スキャンにお金がかかります。
2026 年現在の価格体系はこんな感じだ。
| 項目 | 料金 |
|---|---|
| S3 バケット監視 | $1 バケット / 月 |
| オブジェクトスキャン | $1 / 100 万オブジェクト |
うちの場合、月 5000 万オブジェクトをスキャンしているので、毎月 $50 + バケット監視費用がかかってます。
コスト削減のために実装した施策を紹介すると:
1. スキャン対象バケットの制限 本当に必要なバケットのみ監視対象にして、毎月 $300 削減できた。
2. スキャン頻度の調整 初回は毎日スキャンしてたんですが、その後は週 2 回に変更。新規オブジェクトの増分スキャンのみってふうに落ち着きました。
3. 大規模オブジェクトの除外 1GB 以上のメディアファイルはスキャン対象外にして、$150 / 月削減。
{
"s3ObjectFilters": {
"maxFileSize": 1073741824,
"excludedExtensions": ["mp4", "mov", "mkv", "avi", "zip"],
"scanFrequency": "WEEKLY"
}
}
うちはこれで初期月額 $650 から $380 まで削減できました。
まとめ
実装して 6 ヶ月、Macie との付き合い方が見えてきました。重要なポイントを 5 つにまとめるなら:
-
Macie は完璧ではない デフォルト設定だと False Positive が多すぎるので、組織の特性に応じた調整が必須。カスタム識別子とブロックリスト戦略で初めて実用的になる。
-
自動対応と通知が命 検出結果を手動確認していては運用が回らない。EventBridge + Lambda で段階的な自動応答を構築することで、セキュリティとビジネス継続性のバランスが取れる。
-
データドリブンな運用を心がける Athena で検出トレンドを分析すれば、False Positive のパターンが見える。これを基にルール調整することで、精度が急速に改善される。
-
Organizations での統制を活用 複数アカウントを使ってるなら、セキュリティアカウントからの一元管理は必須。自動化によって人的運用コストが大幅に削減できる。
-
コスト最適化は継続的な課題 スキャン対象やファイルサイズ制限で、毎月 $200-300 のコストダウンが実現できる。ROI を意識した調整が大事だ。
正直なところ、Macie だけで機密データを完全に保護することはできません。でも、組織全体のデータ流出リスクを大幅に削減するには十分な価値があります。
これから導入を検討してる方がいたら、まずはパイロット運用から始めて、上記みたいな段階的な改善を心がけることをお勧めします。何か質問あれば、同じような運用経験を持つ人は多いと思うので、情報交換できたら幸いです。