Log::info($request->all()) という1行のおかげで、平文パスワードとカード番号、さらに保存してはいけない CVV までログに残ります。一方、認証失敗を記録していなければ、1アカウントに532回の総当たりが来ていても痕跡すら残りません。残ってほしくないものが残り、残ってほしいものが残らない。この非対称を、設計で解きます。
社内エンジニア向けセキュリティ勉強会(全6回)の第6回、最終回です。テーマはセキュアログ設計とインシデント追跡。ログを「書けば安全」から「目的から設計するもの」へ置き換え、消す・残す・守る・追うの4つに分解します。第1回から第5回まで「それはログで追う話、第6回で」と送ってきた伏線を、この回で全部回収します。Laravel 13.x(新規)と FuelPHP 1.x(レガシー)が混在する現場を前提にしています。
疑似デモは1本、before / after の対比です。ログインと決済を1回ずつ通して $request->all() をそのまま出力すると、CloudWatch Logs に password、card_number、cvv が生のまま並びます(値は説明用のダミーで、カード番号は業界で周知のテスト用 Visa 番号です)。これは CWE-532、ログ自体が新しい漏洩源になった状態です。リダクションを通した after では、password は全マスク、PAN は先頭6桁と末尾4桁だけ、cvv はそもそも出力せず、代わりに相関ID を付けます。第4回の「平文で機微データを置かない」を、DB からログにまで広げる話でもあります。
扱うトピック
- ログの目的は3つ(抑止 / 検証・解析 / 原因究明)。ロギング要件は「先に全部出す」ではなく目的から決める
- 責務分離:アプリは構造化ログを stdout に書くだけ、送出と保管はインフラ層(ECS FireLens・awslogs・CloudWatch Agent → CloudWatch Logs)。12-factor の logs
- 必ず残すもの:認証成功・失敗(authn_login_success / authn_login_fail)、認可失敗(authz_fail = CRITICAL)、入力検証失敗、管理者操作・機微アクセス・鍵の使用。イベント名は自己流にせず標準の語彙で、重大度は RFC 5424 の8段階(数字が小さいほど深刻)
- 絶対に書かないもの:パスワード・暗号鍵・カード会員データ(PAN)・PII・セッションID / トークン・DB 接続文字列・ソースコード。ログは本番データより保護が薄い=書けば「より無防備な漏洩経路」を増やす(CWE-532)
- 消す設計:出力前に除外リストで機械的にマスク。パスワードは値を一切残さない(OWASP 除外リスト)、PAN は先頭6+末尾4(PCI DSS の表示要件)。消しすぎると追えなくなるので、消すのは機微データだけでイベント自体は残す
- 残す設計:構造化ログ(JSON)+相関ID で1リクエストを縦断(Laravel は Log::withContext / shareContext、標準は W3C Trace Context の traceparent、AWS では X-Amzn-Trace-Id)+タイムスタンプは UTC・RFC 3339 / ISO 8601 に統一。時刻がずれていると複数ソースを1本の線につなげられない
- 記録しないことのコスト:認証失敗を残していなければ CWE-778(重要イベントの記録漏れ)。検知が困難になり、フォレンジックの証跡が残らず、攻撃元の特定が困難または不可能になる
- 守る設計:S3 Object Lock による WORM(Compliance モードなら root でも消せない)、CloudTrail ログファイル整合性検証(SHA-256+RSA 署名)、CloudWatch Logs の保持は1〜3653日(約10年)で既定は無期限 ── 期間も目的から決める
- 追跡:Logs Insights で authn_login_fail を userid ごとに集計して総当たりを1クエリで可視化 → 人がクエリを叩く偶然の後追いから、メトリクスフィルタ+アラームで仕組みが叩く形へ(OWASP Top 10 の A09。2021年版 Security Logging and Monitoring Failures、2025年版で Security Logging & Alerting Failures に改称されアラートが強調された)→ サブスクリプションフィルタと相関ID で点を線に束ね、第5回の WAF ログ・VPC フローログも同じ集約先で相関
- 検知はゴールではなく受け渡し:ログの役割は「誰が・何を・いつ」を対応チームに正確に渡すこと。追ったログはデジタル証拠にもなり、証拠に必要な完全性を「守る設計」が技術で支える
- シリーズ回収表:第1〜5回の脅威を「残すログイベント」と「追跡手段」に割り付け直す(総当たり→authn_login_fail の集計とアラート、SQLi / SSRF→入力検証失敗ログ、XSS・セッション異常・CSRF 失敗→セッション管理失敗ログ、認証失敗・鍵アクセス→認証・鍵操作ログ、境界を素通りした攻撃→WAF ログ・VPC フローログ)
持ち帰る3つの結論
1. ログは目的から設計する
2. 機微データ(パスワード・クレカ・PII)はログに残さない
3. 攻撃・認証失敗の痕跡は必ず残す ── 追跡できないと侵入に気づけない
レガシー保守(FuelPHP 1.x)でも「明日からできる」形にしています。FuelPHP 1.x の Log も Monolog ベースなので重大度は付けられ、除外リストも相関ID も書けます(既定の log_threshold は L_WARNING、log_date_format は UTC の ISO 8601 ではないので揃える余地がある)。Next action は3つ、$request->all() や password・card・token を grep で棚卸ししてリダクションを通す、authn_login_fail と閾値アラートを有効にする、ログの保持期間と改ざん防止を確認する。Appendix には CloudWatch Logs の保持値一覧と料金構造、送出方式の比較(stdout→インフラ集約 と Monolog+AWS SDK での直送)、Monolog 3 の processor によるリダクション実装全文、Logs Insights クエリ集、改ざん防止・時刻同期・正規化の詳細と出典、そして IR・フォレンジック概観を付けています。
全6回は、ぜんぶ「作る側の設計」で守る話でした。次の一歩である組織的なインシデント対応(NIST SP 800-61・RFC2350・X.1060・JPCERT/CC)は、別シリーズの CySec 復習ログに委ねています。
想定読者:「いざという時のために、リクエストは丸ごと出しておけばいい」と思っていた"ちょっと前の自分"。
分類:OWASP A09:2021 Security Logging and Monitoring Failures(2025年版では Security Logging & Alerting Failures)、CWE-532(ログへの機微情報の書き込み)、CWE-778(重要イベントの記録漏れ)