Skip to content

【重大】API認可チェック欠如による他ユーザーデータ閲覧の脆弱性 #5

@Workwrite-Niidome

Description

@Workwrite-Niidome

概要

複数の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);
}

なぜ見落としたか

  1. 一括生成の盲点: 各エンドポイントの認可要件を個別に検討する工程がなかった
  2. ネガティブテストの不在: 「他ユーザーのworkIdでアクセスしたら403を返す」テストが存在しなかった
  3. パターンの伝播: 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 キャラクター設計、ストーリーアーク

再発防止策

即時対応(必須)

  • 全エンドポイントに対する認可テスト追加(他ユーザーのworkIdで403を返すことを検証)
  • PRレビューチェックリストに「resourceIdベースのエンドポイントに所有者チェックがあるか」を追加

中期対応

  • verifyWorkOwnership をガード(NestJS Guard)として共通化し、デコレータで宣言的に適用
    @UseGuards(JwtAuthGuard, WorkOwnerGuard)
    @WorkParam('workId')
  • 新規エンドポイント作成時のセキュリティチェックリストを文書化

長期対応

  • 定期的なセキュリティ監査の実施(月次)
  • E2Eテストで認可バイパスを自動検出する仕組み

教訓

認証 ≠ 認可。 JwtAuthGuardは「ログインしているか」しか検証しない。「このユーザーがこのリソースにアクセスする権利があるか」は別途検証が必要。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions