金融システム開発規約
ログ設計と監査証跡
決済システムにおけるログ要件、Audit Trail、マスキング
ログ設計と監査証跡
決済システムのログは「デバッグ用」ではなく「証拠」です。監査対応・障害調査・不正調査のすべてに使われるため、設計段階から計画的に実装します。
1. Audit Trail(監査証跡)
誰が、いつ、何を、どうしたか を追跡可能にする記録です。
必須記録項目
| 項目 | 説明 | 例 |
|---|---|---|
| Who | 操作者の識別子 | ユーザーID、システムID、IPアドレス |
| When | タイムスタンプ | ISO 8601 形式(タイムゾーン付き) |
| What | 操作対象 | 取引ID、口座番号(マスク済み) |
| Action | 操作内容 | 承認、取消、返金、参照 |
| Result | 結果 | 成功、失敗、エラーコード |
| Before/After | 変更前後の値 | ステータス変更(PENDING → AUTHORIZED) |
実装例
interface AuditLog {
timestamp: string; // "2026-03-08T10:30:00.000+09:00"
traceId: string; // リクエスト追跡ID
actor: {
type: 'USER' | 'SYSTEM' | 'BATCH';
id: string;
ip?: string;
};
action: string; // "PAYMENT_AUTHORIZE"
resource: {
type: string; // "TRANSACTION"
id: string; // "txn-abc-123"
};
result: 'SUCCESS' | 'FAILURE';
detail: {
before?: Record<string, unknown>;
after?: Record<string, unknown>;
reason?: string; // 失敗理由
};
}2. カード番号のマスキング
PCI DSS 要件3により、カード番号はログ・画面表示においてマスキングが必須です。
マスキングルール
完全なカード番号: 4123 4567 8901 2345
表示可能な範囲: 先頭6桁 + 末尾4桁まで
推奨マスキング: 412345******2345
最小限のマスキング: ************2345実装
function maskCardNumber(cardNumber: string): string {
const digits = cardNumber.replace(/\D/g, '');
if (digits.length < 13) return '****';
return digits.slice(0, 6) + '*'.repeat(digits.length - 10) + digits.slice(-4);
}
// ログ出力前に必ずマスキング
function logTransaction(tx: Transaction): void {
logger.info('Transaction processed', {
transactionId: tx.id,
cardNumber: maskCardNumber(tx.cardNumber), // マスキング必須
amount: tx.amount,
currency: tx.currency,
// CVV は絶対にログに出力しない
});
}ログに出力してはいけないデータ
| データ | 理由 |
|---|---|
| カード番号(平文) | PCI DSS 要件3 |
| CVV / CVC | PCI DSS(保存自体が禁止) |
| PIN(暗証番号) | PCI DSS(保存自体が禁止) |
| 磁気ストライプデータ | PCI DSS(保存自体が禁止) |
| パスワード(平文) | セキュリティの基本 |
3. ログレベルの使い分け
| レベル | 決済システムでの用途 | 例 |
|---|---|---|
| ERROR | 決済処理の失敗、外部システム連携エラー | オーソリ応答タイムアウト |
| WARN | 正常だが注意が必要な事象 | リトライ発生、閾値接近 |
| INFO | 正常な取引処理の記録 | 決済成功、ステータス変更 |
| DEBUG | 開発・調査用の詳細情報 | リクエスト/レスポンス詳細 |
ルール: 本番環境では INFO 以上を出力。DEBUG ログにも機密情報を含めない。
4. 構造化ログ
JSON形式の構造化ログを採用し、検索・分析を容易にします。
{
"timestamp": "2026-03-08T10:30:00.123+09:00",
"level": "INFO",
"traceId": "req-xyz-789",
"service": "payment-service",
"action": "PAYMENT_AUTHORIZE",
"transactionId": "txn-abc-123",
"cardNumber": "412345******2345",
"amount": 10000,
"currency": "JPY",
"result": "SUCCESS",
"responseTime": 234,
"acquirer": "acquirer-001",
"authCode": "A12345"
}5. ログの保存要件
| 規制・基準 | 保存期間 |
|---|---|
| PCI DSS 要件10 | 最低1年(直近3ヶ月は即時アクセス可能) |
| FISC 安全対策基準 | 1年以上 |
| 犯罪収益移転防止法 | 取引記録は7年間 |
| 税法(帳簿保存) | 7年間 |
ルール: 監査ログは 最低7年間 保存する設計とする。ログの改竄防止(Write-Once ストレージ、ハッシュチェーン等)も考慮する。
6. 障害調査のためのログ設計
トレースIDの一貫性
リクエストの開始から完了まで、同一の traceId を付与してログを横断的に追跡可能にします。
[req-xyz-789] → API Gateway → Payment Service → Acquirer API → Issuer外部連携のリクエスト/レスポンスログ
外部システム(国際ブランド、銀行ネットワーク等)との通信内容は、マスキングした上で記録します。
logger.info('External API call', {
traceId,
direction: 'REQUEST',
endpoint: 'acquirer/authorize',
payload: maskSensitiveFields(request),
timestamp: new Date().toISOString(),
});
logger.info('External API response', {
traceId,
direction: 'RESPONSE',
statusCode: response.status,
payload: maskSensitiveFields(response.data),
responseTime: elapsed,
timestamp: new Date().toISOString(),
});