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 つにまとめるなら:

  1. Macie は完璧ではない デフォルト設定だと False Positive が多すぎるので、組織の特性に応じた調整が必須。カスタム識別子とブロックリスト戦略で初めて実用的になる。

  2. 自動対応と通知が命 検出結果を手動確認していては運用が回らない。EventBridge + Lambda で段階的な自動応答を構築することで、セキュリティとビジネス継続性のバランスが取れる。

  3. データドリブンな運用を心がける Athena で検出トレンドを分析すれば、False Positive のパターンが見える。これを基にルール調整することで、精度が急速に改善される。

  4. Organizations での統制を活用 複数アカウントを使ってるなら、セキュリティアカウントからの一元管理は必須。自動化によって人的運用コストが大幅に削減できる。

  5. コスト最適化は継続的な課題 スキャン対象やファイルサイズ制限で、毎月 $200-300 のコストダウンが実現できる。ROI を意識した調整が大事だ。

正直なところ、Macie だけで機密データを完全に保護することはできません。でも、組織全体のデータ流出リスクを大幅に削減するには十分な価値があります。

これから導入を検討してる方がいたら、まずはパイロット運用から始めて、上記みたいな段階的な改善を心がけることをお勧めします。何か質問あれば、同じような運用経験を持つ人は多いと思うので、情報交換できたら幸いです。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事