モジュール 05ターム2 · セキュリティ13 ポスター項目
アプリケーションセキュリティ: OWASP Top 10:2025
ログインすると、進捗を複数のデバイスで保存できます。ログインするまでは、このブラウザに保存されます。
なぜ重要なのか
AI が生成したコードが最も頻繁に、しかも最も高くつく形で失敗するのがセキュリティです。Veracode の 2026 年のデータでは、AI コードのセキュリティ合格率は約 56% にとどまります。本モジュールは 2 週間構成で、プログラム全体で最も厳しい関門です。
学習目標
- OWASP Top 10:2025 を自分のアプリケーションに適用できる。
- 認証と認可を正しく実装できる(OAuth/OIDC、セッション、JWT ローテーション、IAM、RLS)。
- シークレットがコード、フロントエンドのバンドル、ログに一切入り込まないよう管理できる。
- インジェクション(SQL、XSS、SSRF)とブラウザを狙う攻撃(CSRF、CORS の設定ミス)を防止できる。
- rate limiting、WAF、DDoS 対策で境界を防御できる。
- SAST、DAST、シークレットスキャン、依存関係スキャンを CI で実行できる。
OWASP Top 10:2025
| ID | リスク | AI コードによくある失敗 |
|---|---|---|
| A01 | Broken Access Control(アクセス制御の不備) | エンドポイントがログインは確認するが所有者は確認しない。RLS が無効。連番 ID による IDOR |
| A02 | Security Misconfiguration(セキュリティ設定ミス) | デバッグが有効、デフォルトの認証情報、緩すぎる CORS *、詳細すぎるエラー、公開されたストレージバケット |
| A03 | Software Supply Chain Failures(ソフトウェアサプライチェーンの不備) | 存在しない(ハルシネーションされた)パッケージやバージョン未固定のパッケージ、侵害された依存関係、署名のないビルド |
| A04 | Cryptographic Failures(暗号化の不備) | 自作の暗号、MD5/SHA1 によるパスワード、平文のシークレット |
| A05 | Injection(インジェクション) | 文字列連結による SQL、dangerouslySetInnerHTML、ユーザー入力を含むシェルコマンド |
| A06 | Insecure Design(安全でない設計) | 脅威モデルがない。ビジネスロジックの悪用(負の数量、クーポンの重ね掛け) |
| A07 | Authentication Failures(認証の不備) | ログインに rate limit がない、脆弱なリセットフロー、有効期間の長いトークン、MFA がない |
| A08 | Software or Data Integrity Failures(ソフトウェアとデータの整合性の不備) | 検証されない webhook、署名のないアップデート、安全でないデシリアライゼーション |
| A09 | Security Logging and Alerting Failures(セキュリティログとアラートの不備) | 監査ログがない。ブルートフォースにアラートが出ない。ログにシークレットが残る |
| A10 | Mishandling of Exceptional Conditions(例外的状況の不適切な処理) | エラー時に fail-open、握りつぶされた例外、ユーザーに表示されるスタックトレース |
2025 年版の新項目: Supply Chain が A03 として独立したカテゴリになり、Mishandling of Exceptional Conditions が A10 に加わりました。どちらも、まさに AI ツールが最も苦手とする領域です。
レッスン
5.1 アクセス制御(A01, 68)
- 認証(Authentication)は「あなたが誰か」、認可(Authorization)は「あなたが何をしてよいか」です。AI コードはたいてい前者だけ実装し、後者を忘れます。
- 認可は すべてのリクエストで、サーバー側で、すべてのオブジェクトに対して チェックします(
WHERE id = $1 AND org_id = $currentOrg)。 - デフォルトは拒否です。ロールと権限は一か所(ポリシーモジュールまたは RLS)にまとめ、
if文をあちこちに散らばらせないでください。 - インフラの IAM: サービスごとに最小権限の専用 ID を割り当てます。管理者キーを共有しない。dev/staging/prod のアカウントは分離します。
- service-role キーや管理者キーをクライアントに公開してはいけません。Supabase では anon キーは設計上公開されるものなので、RLS が唯一の防御壁です。
5.2 認証、OAuth、トークン(69, 70)
- 認証をゼロから作らないでください。実績のあるプロバイダー(Supabase Auth、Clerk、Auth0、エンタープライズ SSO なら WorkOS)を使います。
- OAuth 2.0 / OIDC: ブラウザとモバイルでは PKCE 付きの Authorization Code フローを使います。
state、nonce、issuer、audience を検証します。 - セッション:
HttpOnly、Secure、SameSite=LaxまたはStrictの Cookie を使います。トークンを localStorage に保存しないでください。 - JWT ローテーション: アクセストークンは短命(5〜15 分)にし、refresh token は再利用検知付きでローテーションします。署名鍵は鍵 ID(
kid)付きの JWKS でローテーションし、alg: noneは絶対に許可せず、署名、有効期限、audience を必ず検証します。 - エンタープライズの購買担当者が求めるもの: MFA、SSO(SAML/OIDC)、SCIM プロビジョニング、セッションの失効。
5.3 シークレット管理(67)
- シークレットはシークレットマネージャーかプラットフォームの環境変数に置きます。git には絶対に入れず、
NEXT_PUBLIC_*/VITE_*変数にも入れません(これらはブラウザに配信されます)。 - pre-commit フックと CI でのスキャン(gitleaks、プッシュ保護付きの GitHub secret scanning)を導入します。
- 漏えいしたシークレットは削除するだけでなく ローテーション します。git の履歴とフォークには永遠に残るからです。
- 環境ごとに鍵を分け、API キーのスコープは最小限にします(制限付き Stripe キー、スコープ付きトークン)。
- すべての有料 API キー(LLM、SMS、地図)に利用上限を設定します。設定しなければ、漏えいしたキーは白紙の小切手と同じです。
5.4 暗号と暗号化(72, A04)
- パスワード: 認証プロバイダー経由で argon2id または bcrypt を使います。SHA で自前ハッシュ化してはいけません。
- 保存時の暗号化: マネージド DB/ストレージの暗号化を有効にし、バックアップも暗号化します。機密性の高いデータ(国民 ID 番号、銀行口座情報)にはフィールドレベル暗号化を使い、鍵は KMS で管理します。
- ライブラリ(libsodium、プラットフォームの KMS)を使い、暗号を自作しないでください。
5.5 インジェクション(78, 79, 80)
- SQL インジェクション: パラメータ化クエリ、またはクエリビルダー/ORM を使います。SQL に文字列を埋め込まないでください。「raw」の抜け道でも同様です。
- XSS: フレームワークはデフォルトでエスケープします。危険地帯は
dangerouslySetInnerHTML、v-html、markdown や LLM の出力を HTML としてレンダリングする箇所です。DOMPurify でサニタイズし、Content-Security-Policy を設定します。 - SSRF: ユーザーが指定した URL を取得する機能(リンクプレビュー、webhook、「URL からインポート」、Web を閲覧する AI エージェント)は、
169.254.169.254(クラウドのメタデータ)や内部サービスに向けられる可能性があります。ドメインを allowlist で制限し、名前解決したうえでプライベート IP 範囲をブロックし、リダイレクトを無効化するか毎回再チェックし、フェッチ処理は隔離されたネットワークで実行します。
5.6 ブラウザのセキュリティ(76, 77)
- CORS はサーバーにとってのセキュリティ境界ではありません。ブラウザがどのオリジンにレスポンスの読み取りを許すかを制御するだけです。
Access-Control-Allow-Credentials: trueと組み合わせて任意のOriginをそのまま返してはいけません。オリジンは完全一致で allowlist にします。 - CSRF: Cookie 認証を使う場合、状態を変更するリクエストには
SameSiteCookie に加えて CSRF トークンまたはオリジンチェックを使います。 - セキュリティヘッダー: CSP、HSTS、
X-Content-Type-Options: nosniff、Referrer-Policy、frame-ancestors。
5.7 境界防御: rate limiting、WAF、DDoS(1, 74, 75)
- IP、ユーザー、API キーごとに rate limit をかけます。ログイン、サインアップ、パスワードリセット、OTP、そしてお金がかかるすべてのエンドポイント(LLM、SMS、メール)はさらに厳しくします。
- アルゴリズム: token bucket または sliding window を使い、Redis かエッジに保存します。
Retry-After付きで429を返します。 - マネージド OWASP ルール付きの WAF(Cloudflare、AWS WAF)を使い、サインアップにはボット対策を入れます。
- DDoS 対策の大部分は CDN が担います。オリジンに直接アクセスされて CDN を迂回されないようにしてください。
5.8 例外的状況(A10)
- Fail closed(安全側に倒す): 認可チェックが例外を投げたら拒否します。不正検知チェックがタイムアウトしたら、取引を承認せず保留にします。
- スタックトレースをユーザーに見せてはいけません。リクエスト ID とともにログに記録します。
- 特定のエラーを捕捉し、すべての例外を握りつぶさないでください。
5.9 脅威モデリング(A06)
機能ごとに 30 分で STRIDE を実施します: Spoofing(なりすまし)、Tampering(改ざん)、Repudiation(否認)、Information disclosure(情報漏えい)、Denial of service(サービス拒否)、Elevation of privilege(権限昇格)。信頼境界を図示し、悪用ケース(「数量が -1 だったら?」)を洗い出します。
5.10 パイプラインでのセキュリティ
| ツールの種類 | 例 | 検出できるもの |
|---|---|---|
| シークレットスキャン | gitleaks、GitHub push protection | コード内の鍵 |
| SAST | Semgrep、CodeQL | インジェクション、危険なパターン |
| SCA | Dependabot、Snyk、OSV-Scanner | 脆弱な依存関係 |
| DAST | OWASP ZAP | 実行時の設定ミス、ヘッダー、XSS |
| コンテナ/IaC | Trivy、Checkov | 問題のあるイメージとインフラ設定 |
Lab 5(2 週間): 自分のアプリを攻撃する
Week A、壊す: 2 つ目のアカウントとブラウザプロキシ(ZAP または Burp Community)を使います。
- ID を書き換えて、他のユーザーのレコードを読み取り/編集できるか試します(IDOR)。
- 公開されている anon キーでデータベースを直接呼び出します。
- すべてのテキストフィールドと markdown/LLM の出力に
<script>を注入します。 - URL を取得する機能を
http://169.254.169.254/とhttp://localhostに向けます。 - ログインとパスワードリセットをブルートフォースし、最もコストの高いエンドポイントに負荷をかけ続けます。
- 本番の JS バンドルからシークレットを検索します。
Week B、直して証明する: すべての指摘事項を修正し、指摘ごとに回帰テストを追加し、スキャナーを CI に組み込みます。
完了の定義
AI プロンプトパック
- 「ペネトレーションテスターとして振る舞ってください。このアプリの各ルートについて、認証済みユーザーが他のユーザーのデータにアクセスできる方法を列挙し、それぞれのテストを書いてください。」
- 「シークレットや環境変数を読み込んでいる箇所をすべて洗い出し、クライアントのバンドルに入り込む可能性があるものにフラグを付けてください。」
- 「ユーザー入力から生成した URL を取得している箇所をすべて洗い出してください。DNS 解決後にプライベート、ループバック、リンクローカルの範囲をブロックする SSRF 対策を追加してください。」
- 「すべての catch ブロックを洗い出し、fail-open になっているものやエラーを握りつぶしているものにフラグを付けてください。」