概要
複数のAPIエンドポイントで認可(Authorization)チェックが欠如しており、認証済みユーザーがworkIdを指定することで他ユーザーの非公開データを閲覧・操作できる状態だった。
発生日
2026-03-05(MVP初回コミット a09374d)から存在。2026-03-30に発覚・修正。
根本原因
設計上のミス
MVPで21モジュール・41コントローラーを一括生成した際に、認証(Authentication: ログインしているか)と認可(Authorization: そのリソースにアクセスする権利があるか)を混同した。
具体的なパターン
// 危険: 認証はあるが認可がない
@UseGuards(JwtAuthGuard)
getWorkAnalytics(@Param('workId') workId: string) {
return this.service.getWorkAnalytics(workId); // 誰のworkIdでも返す
}
// 安全: 認証 + 認可
@UseGuards(JwtAuthGuard)
getWorkAnalytics(@Param('workId') workId: string, @CurrentUser('id') userId: string) {
await this.verifyWorkOwnership(workId, userId); // 所有者か確認
return this.service.getWorkAnalytics(workId);
}
なぜ見落としたか
- 一括生成の盲点: 各エンドポイントの認可要件を個別に検討する工程がなかった
- ネガティブテストの不在: 「他ユーザーのworkIdでアクセスしたら403を返す」テストが存在しなかった
- パターンの伝播: MVP後の機能追加が既存の「ガードなし」パターンを踏襲した
影響を受けたエンドポイント(修正済み)
CRITICAL(他人の非公開データ閲覧可能)
| コミット |
ファイル |
エンドポイント |
漏洩データ |
| 4a85c9e |
author-dashboard |
GET analytics/emotions/heatmap |
読者数、感情タグ、読了率 |
| 273b9a0 |
scoring |
GET history, GET analysis |
スコア履歴、改善提案 |
| 273b9a0 |
creation-wizard |
PUT/GET plan, GET originality/summary |
創作プラン、オリジナリティ、要約 |
| 273b9a0 |
story-structure |
GET story-context |
AI用内部コンテキスト |
| 62d4a0a |
scoring |
POST estimate/score/adopt, POST episode |
スコアリング実行(クレジット消費) |
| 62d4a0a |
ai-insights |
GET generic insights |
作品分析 |
| 62d4a0a |
episodes |
GET ?published=false |
未公開エピソード一覧 |
| 62d4a0a |
works |
GET emotion-profile/arc |
非公開作品の感情データ |
| dcc20f0 |
story-structure |
GET characters, GET story-arc |
キャラクター設計、ストーリーアーク |
再発防止策
即時対応(必須)
中期対応
長期対応
教訓
認証 ≠ 認可。 JwtAuthGuardは「ログインしているか」しか検証しない。「このユーザーがこのリソースにアクセスする権利があるか」は別途検証が必要。
概要
複数のAPIエンドポイントで認可(Authorization)チェックが欠如しており、認証済みユーザーがworkIdを指定することで他ユーザーの非公開データを閲覧・操作できる状態だった。
発生日
2026-03-05(MVP初回コミット
a09374d)から存在。2026-03-30に発覚・修正。根本原因
設計上のミス
MVPで21モジュール・41コントローラーを一括生成した際に、認証(Authentication: ログインしているか)と認可(Authorization: そのリソースにアクセスする権利があるか)を混同した。
具体的なパターン
なぜ見落としたか
影響を受けたエンドポイント(修正済み)
CRITICAL(他人の非公開データ閲覧可能)
再発防止策
即時対応(必須)
中期対応
verifyWorkOwnershipをガード(NestJS Guard)として共通化し、デコレータで宣言的に適用長期対応
教訓
認証 ≠ 認可。 JwtAuthGuardは「ログインしているか」しか検証しない。「このユーザーがこのリソースにアクセスする権利があるか」は別途検証が必要。