ストーリー
高橋アーキテクトが問いかけました。
監査ログとは
監査ログ(Audit Log)は、システム上で行われた重要な操作を記録するログです。「いつ、誰が、何を、どこから、どうしたか」を追跡可能にします。
監査ログに記録すべきイベント
| カテゴリ | イベント例 |
|---|---|
| 認証 | ログイン成功/失敗、ログアウト、MFA検証、パスワード変更 |
| 認可 | アクセス拒否、権限変更、ロール変更 |
| データアクセス | 機密データの参照、ダウンロード、エクスポート |
| データ変更 | 作成、更新、削除(特に機密データ) |
| 管理操作 | ユーザー作成/削除、設定変更、デプロイ |
| セキュリティイベント | レートリミット超過、不正なトークン使用、IP ブロック |
監査ログの設計
// 監査ログのスキーマ
interface AuditLogEntry {
// 識別情報
id: string; // UUID
timestamp: string; // ISO 8601
traceId: string; // リクエスト追跡ID
// アクター情報
actor: {
type: "user" | "service" | "system";
id: string;
role: string;
tenantId?: string;
};
// アクション情報
action: {
type: string; // "user.login", "order.create" 等
category: "authentication" | "authorization" | "data_access" |
"data_mutation" | "admin" | "security";
result: "success" | "failure" | "denied";
};
// リソース情報
resource: {
type: string; // "user", "order" 等
id: string;
attributes?: Record<string, unknown>;
};
// コンテキスト情報
context: {
ipAddress: string;
userAgent: string;
requestId: string;
sessionId?: string;
geoLocation?: string;
};
// 変更内容(データ変更の場合)
changes?: {
before: Record<string, unknown>;
after: Record<string, unknown>;
};
}
// 監査ログサービス
class AuditLogger {
constructor(private store: AuditStore) {}
async log(entry: Omit<AuditLogEntry, "id" | "timestamp">): Promise<void> {
const fullEntry: AuditLogEntry = {
...entry,
id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
};
// 書き込み専用ストレージに保存(削除不可)
await this.store.append(fullEntry);
// Critical イベントはリアルタイムアラート
if (this.isCritical(fullEntry)) {
await this.sendAlert(fullEntry);
}
}
private isCritical(entry: AuditLogEntry): boolean {
return (
entry.action.category === "security" ||
(entry.action.category === "authentication" && entry.action.result === "failure") ||
entry.action.type.includes("delete") ||
entry.action.type.includes("role.change")
);
}
private async sendAlert(entry: AuditLogEntry): Promise<void> {
await alertService.notify({
severity: "high",
message: `Security event: ${entry.action.type} by ${entry.actor.id}`,
details: entry,
});
}
}
ログに記録してはいけない情報
| 記録禁止 | 理由 |
|---|---|
| パスワード | 平文のパスワードは絶対にログに残さない |
| クレジットカード番号 | PCI DSS違反 |
| アクセストークン | トークン漏洩のリスク |
| 個人情報の生データ | GDPR/個人情報保護法違反 |
| シークレット | APIキー等の機密情報 |
// ログサニタイザー
const sanitizeForLog = (data: Record<string, unknown>): Record<string, unknown> => {
const sensitiveFields = [
"password", "token", "secret", "apiKey", "creditCard",
"ssn", "accessToken", "refreshToken",
];
const sanitized = { ...data };
for (const field of sensitiveFields) {
if (sanitized[field]) {
sanitized[field] = "[REDACTED]";
}
}
// メールアドレスはマスキング
if (typeof sanitized.email === "string") {
const [local, domain] = (sanitized.email as string).split("@");
sanitized.email = `${local[0]}***@${domain}`;
}
return sanitized;
};
ログの保存と保護
// ログの保存設計
interface LogStorageConfig {
// 書き込み専用(WORM: Write Once Read Many)
immutable: true;
// 暗号化
encryptionAtRest: true;
encryptionKey: "AWS KMS";
// 保存期間
retention: {
hot: "30日", // すぐに検索可能(CloudWatch Logs)
warm: "90日", // 低コストストレージ(S3 Standard-IA)
cold: "3年", // アーカイブ(S3 Glacier)
};
// アクセス制御
access: {
write: ["application-service"],
read: ["security-team", "compliance-team"],
delete: [], // 誰も削除できない
};
}
不正検知のためのログ分析
// 不審なパターンの検出ルール
const detectionRules = [
{
name: "ブルートフォース検知",
condition: "同一IPから5分間に10回以上のログイン失敗",
action: "IPブロック + アラート",
},
{
name: "異常な時間帯のアクセス",
condition: "深夜2-5時の管理者操作",
action: "追加認証要求 + アラート",
},
{
name: "大量データダウンロード",
condition: "1時間に1000件以上のデータエクスポート",
action: "アカウント一時停止 + アラート",
},
{
name: "権限昇格の試行",
condition: "認可拒否が連続5回以上",
action: "セッション無効化 + アラート",
},
];
まとめ
| ポイント | 内容 |
|---|---|
| 監査ログ | いつ・誰が・何を・どこから・どうしたかを記録 |
| 記録対象 | 認証、認可、データアクセス、管理操作 |
| 記録禁止 | パスワード、トークン、カード番号等 |
| 保護 | 書き込み専用、暗号化、アクセス制限 |
チェックリスト
- 監査ログに記録すべきイベントを把握した
- 監査ログのスキーマ設計を理解した
- ログに記録してはいけない情報を把握した
- 不正検知のためのログ分析パターンを理解した
次のステップへ
次は演習です。ここまで学んだDevSecOpsの知識を使って、セキュリティパイプラインを設計しましょう。
推定読了時間: 30分