セキュリティの世界には、残酷な事実があります。「攻撃は、必ずしも防げない」。ゼロデイ脆弱性、内部不正、巧妙なフィッシング——どれだけ防御を固めても、侵害の可能性をゼロにはできません。だからこそ、防御と同じくらい重要なのが——**「気づけること」**です。
侵害の調査で最も多い悲劇は、「いつ・どこから・何が起きたのか、ログが無くて分からない」というものです。実際、OWASP Top 10でも**「セキュリティログとアラートの不備(A09)」は繰り返しランクインしています。本記事は、「検知できる状態」を作る**ための、ログ設計と検知エンジニアリングを、公式情報(OWASP Logging Cheat Sheet、Sigma、MITRE ATT&CK)に忠実な実コードで解説します。
この記事の位置づけ: NIST CSF 2.0の**「検知(Detect)」**を担う中核技能です。職種全体はセキュリティエンジニアになるには、検知の前段の防御はセキュアコーディング実践、検知の後段の対応はインシデント対応を参照してください。
0. 何を記録し、何を記録しないか
ログ設計は、「とにかく全部出す」では失敗します。ノイズに埋もれて重要な異変が見えなくなり、かつ機密情報を撒き散らすからです。OWASP Logging Cheat Sheetに沿って、記録すべきものと、絶対に記録してはいけないものを分けます。
記録すべきセキュリティイベント
| カテゴリ | 具体例 |
|---|---|
| 認証 | ログイン成功・失敗、ログアウト、MFAの成否、パスワード変更 |
| 認可 | アクセス拒否(権限不足)、ロール変更、特権操作 |
| 重要操作 | データの作成・更新・削除、設定変更、エクスポート |
| 入力検証の失敗 | 不正な入力の拒否(攻撃の予兆) |
| 異常 | 例外、レート制限の発動、想定外の状態遷移 |
絶対に記録してはいけないもの
ログそのものが新たな情報漏えい源になる——これを忘れてはいけません。
- パスワード・トークン・APIキー・セッションID(平文はもちろんハッシュ前も)
- クレジットカード番号・CVV(PCI DSS違反)
- 個人情報(PII)——氏名・住所・電話番号などは最小化、必要ならマスキング
- 機密性の高い業務データ
「デバッグのために一時的に出した秘密情報」が本番ログに残り、それが漏えいする——典型的な事故です。秘密を“うっかり書けない”構造を、ロガー側で持つのが正解です。
1. 型安全な構造化ロガー — 秘密を構造的に書けなくする
人間が読むためのテキストログ("user alice logged in from 1.2.3.4")は、機械が検索・相関するのに向きません。現代の標準は 構造化ログ(JSON) です。フィールドで検索でき、相関ID(リクエストID)で一連の流れを追え、SIEMにそのまま取り込めます。
さらに、秘密情報を自動でマスキングするロガーにすれば、「うっかり漏らす」事故を構造的に防げます。
// security-logger.ts — 構造化セキュリティログを出し、秘密情報を自動マスキングする。
import { z } from "zod";
// ① 記録してよいイベント種別を列挙(型で縛る=勝手な自由文字列を防ぐ)。
const SecurityEvent = z.enum([
"auth.login.success",
"auth.login.failure",
"authz.access.denied",
"data.export",
"ratelimit.triggered",
]);
type SecurityEvent = z.infer<typeof SecurityEvent>;
// ② ログに含めてはいけないキー。値を見ずにキー名で機械的に伏せる。
const REDACT_KEYS = new Set([
"password", "token", "secret", "authorization",
"apikey", "api_key", "sessionid", "session_id", "cookie", "creditcard",
]);
function redact(meta: Record<string, unknown>): Record<string, unknown> {
const safe: Record<string, unknown> = {};
for (const [key, value] of Object.entries(meta)) {
safe[key] = REDACT_KEYS.has(key.toLowerCase()) ? "[REDACTED]" : value;
}
return safe;
}
interface LogContext {
readonly requestId: string; // 相関ID:一連のリクエストを横断で追える
readonly userId?: string; // 主体(無ければ匿名)
readonly ip?: string;
}
/** セキュリティイベントを構造化JSONで出力する。秘密は自動で伏せられる。 */
export function logSecurityEvent(
event: SecurityEvent,
ctx: LogContext,
meta: Record<string, unknown> = {},
): void {
const entry = {
ts: new Date().toISOString(),
level: "security",
event,
...ctx,
...redact(meta), // ③ 秘密はここで構造的に伏せられる
};
// 1行1JSON(JSON Lines)。SIEM/ログ基盤がそのまま取り込める。
console.log(JSON.stringify(entry));
}
// 使用例:ログイン失敗を記録する。password を渡しても自動で伏せられる。
logSecurityEvent("auth.login.failure", { requestId, ip }, {
email: "alice@example.com",
password: "hunter2", // → "[REDACTED]" として出力される(漏れない)
reason: "invalid_credentials",
});
// 出力: {"ts":"2026-06-28T...","level":"security","event":"auth.login.failure",
// "requestId":"...","ip":"...","email":"alice@example.com",
// "password":"[REDACTED]","reason":"invalid_credentials"}
設計の要点は3つです。(1) イベント種別を型で列挙して野放図な文字列を防ぐ、(2) 相関IDで横断追跡を可能にする、(3) キー名ベースのマスキングで秘密を構造的に伏せる。これで「検索でき、相関でき、漏らさない」ログの土台ができます。
2. 検知エンジニアリング — Sigma で「異常」を定義する
ログを貯めるだけでは、誰も見ません。「この条件が起きたらアラートを出す」という検知ルールが要ります。ここで業界標準になりつつあるのが、Sigma です。
Sigmaは、ベンダー中立なYAML形式の検知ルールです。一度書けば、Splunk・Microsoft Sentinel・Elastic・QRadar など各SIEMのクエリに変換できます。「SIEMごとにクエリ言語を覚え直す」地獄から解放されるのが最大の利点です。
下は、**短時間に連続したログイン失敗(総当たり攻撃の予兆)**を検知するSigmaルールです。MITRE ATT&CKの技術ID(T1110: Brute Force)に紐づけることで、「自分たちはATT&CKのどの攻撃技術をカバーできているか」が可視化されます。
# rules/brute_force_login.yml — 総当たりログインの検知(Sigma形式)
title: 短時間における連続ログイン失敗(総当たりの疑い)
id: 8f2e1a3c-0b4d-4e5f-9a1b-2c3d4e5f6a7b
status: stable
description: 同一IPからの連続したログイン失敗を検知し、総当たり攻撃を早期に捉える
references:
- https://attack.mitre.org/techniques/T1110/
author: friend yota
date: 2026/06/28
logsource:
product: application
service: auth
detection:
selection:
event: "auth.login.failure" # 構造化ログの event フィールドに一致
timeframe: 5m # 5分の時間枠で…
condition: selection | count() by ip > 10 # 同一IPから10回超の失敗
fields:
- ip
- email
falsepositives:
- 正規ユーザーのパスワード失念(ロックアウト方針と併せて評価する)
level: high
tags:
- attack.credential_access
- attack.t1110 # MITRE ATT&CK: Brute Force
ルールの構造はシンプルです——logsource(どのログか)+ detection(何に一致するか)+ condition(アラート条件)+ メタ데이터(tags で ATT&CK 技術ID)。falsepositives(誤検知の可能性)を明記する文化が、後述のアラート疲れ対策に効きます。
3. MITRE ATT&CK で「カバレッジ」を測る
個々のルールを書くだけでは、「自分たちは何を検知できて、何を見落としているか」が分かりません。そこで使うのが MITRE ATT&CK です。ATT&CKは、実際の攻撃者の戦術(Tactics)と技術(Techniques)を体系化した、世界共通の“攻撃の地図”です。
各Sigmaルールを attack.tXXXX でATT&CK技術にマッピングしておくと、ATT&CK Navigator 上で「カバーできている技術」を塗り分けられます。これにより、検知の議論が「なんとなく不安」から——
- 「初期アクセス(Initial Access)の検知は手薄だ」
- 「権限昇格(Privilege Escalation)はカバーできている」
——という具体的なカバレッジの議論に変わります。脅威モデリング(STRIDE)で“守るべき脅威”を洗い出し、ATT&CKで“検知のカバレッジ”を測る——攻めの地図と守りの地図が噛み合います。
4. Detection as Code — 検知を“コード”として運用する
検知ルールも、アプリのコードと同じ規律で運用します(Detection as Code)。
- Gitで版管理する。 誰が・いつ・なぜそのルールを足したかが追える。
- テストする。 既知の攻撃ログ(サンプル)でルールが発火し、正常ログで発火しないことを検証する。
- CIで配備する。 マージされたら自動でSIEMにルールを反映する。
- チューニングし続ける。 誤検知(false positive)を継続的に削る。
最後の「チューニング」が、検知の品質を決定づけます。アラート疲れ(alert fatigue)——誤検知が多すぎて、本物のアラートまで無視されるようになる状態——は、検知体制の最大の敵です。だから各ルールに falsepositives を明記し、発火率を観測し、「狼少年にならないルール」だけを残す運用が要ります。「ルールの数」ではなく「信頼できるアラートの質」を指標にしてください。
検知が発火したあと、誰が・どう動くのか——その先はインシデント対応の領域です。検知と対応はセットで設計して初めて意味を持ちます。
5. ログの完全性 — 改ざんされたら証拠にならない
最後に見落とされがちな点を。ログ自体が改ざんできては、否認防止(STRIDEのRepudiation対策)になりません。 攻撃者が侵入後に証拠隠滅でログを消す、というのは典型的な手口です。
- **追記専用(append-only)**にする。アプリの実行権限ではログを書き換え・削除できないようにする。
- 集約する。 ログを生成元のサーバーから切り離し、別の集約基盤(SIEM/ログ専用ストレージ)へ即座に転送する。侵害されたサーバー上のログだけに頼らない。
- 保持期間を定める。 法令・契約・調査の必要性に応じて、改ざん不能な形で一定期間保持する。
「ログを取っている」と「ログが証拠になる」の間には、この完全性の差があります。
6. よくある質問(FAQ)
Q. 小規模なアプリでも、ここまで必要ですか? A. 規模に応じて“軽く”始めれば十分です。まずは構造化ログ+秘密のマスキング(§1)から。SIEMが無くても、認証成否・認可拒否・重要操作をJSONで出しておくだけで、いざという時の調査力が段違いになります。
Q. 既存のロガー(pino/winston等)でもできますか?
A. できます。本記事のロガーは原理を示すための最小実装です。pinoなどには redact 機能が標準であり、それを使えば本番品質の構造化ログ+マスキングが手早く実現できます。
Q. SIEMは高価では?無料で始められますか? A. クラウドのマネージドSIEMは従量課金で小さく始められます。OSSなら Elastic Stack や OpenSearch、Wazuh などがあります。まずはログを「構造化してどこかに集約する」ことが先で、SIEMの選定は後からでも間に合います。
Q. Sigmaルールはどこから手に入る?
A. SigmaHQ公式リポジトリに数千の公開ルールがあります。まずはそこから自分の環境に合うものを取り込み、sigma-cli で各SIEMのクエリに変換するのが早道です。
Q. ログにどこまでPIIを含めてよい? A. 原則は最小化です。調査に不可欠なら、識別子(ユーザーID)に留め、氏名・住所などはマスキングか参照に。個人情報保護法やGDPR等の観点でも、ログのPIIは“持たない”のが最も安全です。
7. まとめ
「守る」ことと同じくらい、「気づける」ことが重要です。検知は、防御をすり抜けた攻撃に対する最後の、そして決定的な防衛線です。
- 何を記録し、何を記録しないかを決める。 認証・認可・重要操作は記録、秘密とPIIは絶対に書かない。
- 構造化ログ+相関ID+自動マスキング。 検索でき、相関でき、漏らさない土台を、型安全なロガーで作る。
- Sigmaで検知を書く。 ベンダー中立なYAMLで、各SIEMへ変換。MITRE ATT&CKで技術IDに紐づける。
- ATT&CKでカバレッジを測る。 「なんとなく不安」を「どの戦術が手薄か」に変える。
- Detection as Codeで運用する。 Git・テスト・CI・継続チューニング。アラート疲れを避け、質を指標にする。
- ログの完全性を守る。 追記専用・別基盤への集約・適切な保持。
「検知できないものは守れない」——この一文を、設計の最初に置いてください。あなたのプロダクトのログ設計や検知体制を一度見直したい、SIEM導入や検知ルールの整備を伴走してほしい場合は、お気軽にご相談ください。