Skip to content
Published on

フロントエンドセキュリティ 2025 — XSS·CSRF·CSP·Trusted Types·JWT·OAuth·PKCE·Passkey·サプライチェーン·SRI (シーズン6 第9回)

Authors

プロローグ — ブラウザがランタイムになり、攻撃者が気付いた

フロントエンドセキュリティはかつて「ユーザー入力をサニタイズし、セキュアクッキーを設定する」という意味だった。2026年に脅威面は全ランタイム: Service Worker、拡張、サードパーティスクリプト、バンドル依存、自身のソース。セキュリティ基準のないフロントエンドエンジニアは脆弱性を出荷する。

本稿は実務プレイブック: 実際に重要な脅威、実際に機能する防御、全チームの最低ライン。


1章. 2026年脅威モデル

OWASP Top 10はあまり変わらない。変わったのはリスクが住む場所:

  • インジェクション (主にXSS) — 今も#1。
  • 認証の破綻 — セッションハイジャック、トークン盗難。
  • 機密データ漏洩 — バンドル、ログ、キャッシュレスポンスで。
  • サプライチェーン侵害 — 依存するnpm/CDN。
  • 設定ミス — 間違ったCSP、オープンCORS、漏洩env変数。

2章. XSS — クロスサイトスクリプティング

3タイプ:

  • 反射型 — 攻撃者がURLにスクリプトを組み込み、被害者がクリック。
  • 格納型 — スクリプトがDBに保存、全ユーザーに配信 (最も危険)。
  • DOMベース — クライアント側シンク (innerHTML, eval) が未信頼ソースを読む。

緩和 (層状)

  1. フレームワーク出力エンコーディング — React、Vue、Svelteがデフォルトでエスケープ。dangerouslySetInnerHTMLv-html{@html}が脱出ハッチ。
  2. Sanitizer API (2024ベースライン) — ブラウザネイティブサニタイズ: element.setHTML(untrustedHtml)
  3. DOMPurify — 古いブラウザ用フォールバック。
  4. CSP v3 + strict-dynamic — XSSが着弾してもnonce/hashなしでスクリプト実行不可。
  5. Trusted TypesinnerHTML = xTrustedHTML オブジェクトを要求するTS風ランタイムチェック。

CSPスターター (2026)

Content-Security-Policy:
  default-src 'self';
  script-src 'nonce-{nonce}' 'strict-dynamic';
  style-src 'self' 'nonce-{nonce}';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'none';
  object-src 'none';
  require-trusted-types-for 'script';
  trusted-types default dompurify;

strict-dynamicは「このnonceを持つスクリプトとそれがロードするスクリプトを信頼」 — ホワイトリスト管理地獄を排除。


3章. CSRF — クロスサイトリクエストフォージェリ

クッキー認証APIではCSRFは今もリスク。

緩和

  • SameSite=Lax (主要ブラウザでデフォルト化) がほとんどのCSRFをキル。
  • SameSite=Strict 可能な場所で。
  • ダブルサブミットトークン — CSRFトークンをクッキーにセット、ヘッダで要求。
  • Origin/Referer検証 をサーバで。
  • fetchのcredentialsを明示 — デフォルト credentials: include しない。

トークンベースAPI (AuthorizationヘッダJWT) ではCSRFは適用外 — だがXSS窃取は別。


4章. 認証 — OAuth、PKCE、Passkeys

OAuth 2.0 + PKCE

SPAとモバイルアプリは必ずPKCE (Proof Key for Code Exchange) を使用。Implicit Flowは廃止。

Client/authorize?code_challenge=S256(random)
IdP → redirects back with code
Client/token with code + code_verifier

トークン

  • アクセストークンは可能な限りメモリに。
  • リフレッシュトークンは httpOnly, Secure, SameSite=Strict クッキーに。
  • 短命アクセストークン (5–15分); リフレッシュがローテート。

Passkeys (WebAuthn)

2024年がパスキーブレイクスルー — Apple、Google、Microsoftが相互同期。新アプリには passkey先、password後 が現代デフォルト。

  • navigator.credentials.create({ publicKey: ... }) で登録。
  • navigator.credentials.get({ publicKey: ... }) でサインイン。
  • 設計上フィッシング耐性 (ドメインバインド)。

5章. クッキーとセッション

  • Secure — HTTPSでのみ送信。
  • HttpOnly — JSから読めない。
  • SameSite=Lax (デフォルト) または Strict
  • Path=/ または絞る。
  • Max-Age または Expires — Max-Age推奨。
  • 名前プレフィクス __Host- で最強セキュリティ (__Host-session=...)。

6章. JWTの落とし穴

JWTは正しく使えば問題ない — が、よくあるミス:

  1. セッションで十分な場所にJWT。有効期限までrevokeできないならセキュリティ問題。
  2. JWTをlocalStorageに保存 — XSSが盗む。httpOnlyクッキーを使う。
  3. 弱いシークレット — 短いシークレットのHS256は数分でクラック。非対称 (RS256/ES256) + ローテーション使用。
  4. audissを検証しない — サービス間でトークン混在しやすい。
  5. alg: none — 古代のライブラリバグ、今もチェック価値あり。

7章. サードパーティスクリプトとサプライチェーン

<script src=...> は信頼関係。2024年攻撃 (polyfill.io、以前のnode-ipc) がリスクを示した。

緩和

  • Subresource Integrity (SRI)<script src=... integrity="sha384-..." crossorigin="anonymous">
  • 信頼できないCDNスクリプトをバンドルしない — 可能ならセルフホスト。
  • 依存変更を監査 (Dependabot、Renovate、Snyk)。
  • npm provenance — パッケージ原産地検証。
  • SBOM + SLSA attestation (シーズン6 第12回参照)。
  • Lockfile整合性npm cipnpm install --frozen-lockfile

8章. CORS — 最も誤解されたヘッダ

CORSはセキュリティではない。ブラウザポリシーでユーザーを悪意ある第三者APIへの認証リクエストから保護。サーバは今も全リクエストを検証。

ルール

  • 任意の Origin ヘッダを Access-Control-Allow-Origin にエコーしない。
  • Allow-Credentials: true具体的なオリジンとペア、* 禁止。
  • 可能ならCORSなし (同一オリジン) を優先。

9章. クリックジャッキングとフレーミング

  • X-Frame-Options: DENY または SAMEORIGIN (レガシーだが広くサポート)。
  • CSP frame-ancestors 'none' (モダン、X-Frame-Options置換)。
  • 埋め込み可能ウィジェット (Stripe Checkoutなど) では特定オリジンを許可リストに。

10章. ヘッダチートシート (2026)

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: (2章参照)
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), geolocation=(), microphone=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-site

ツール: securityheaders.comMozilla Observatory、ブラウザDevToolsセキュリティパネル。


11章. よくある漏洩

  1. クライアントバンドルのAPIキーNEXT_PUBLIC_* は公開 — シークレットを置かない。
  2. 本番のソースマップがフルソース付き — hidden-source-map + Sentryへのみアップロード検討。
  3. PII付きコンソールログ — 本番で除去。
  4. スタックトレースを漏らすエラーメッセージ — ユーザーにはサニタイズ、ログにはフル。
  5. 認証ページのCache-ControlCache-Control: private または no-store 使用。
  6. 機密データのlocalStorage — XSS窃取。最大 sessionStorage、httpOnlyクッキー推奨。
  7. トークン付きURL — Refererヘッダ、アナリティクス、ログに漏洩。

12章. AI時代のセキュリティ

  1. LLMアプリのプロンプトインジェクション — LLM前のユーザーコンテンツを未信頼入力として扱う。プロンプトサニタイズ、system/user分離。
  2. ツール使用アクセス — LLMがツールを呼べるなら、できることを制限 (許可リスト)。
  3. AIコーディングアシスタントのシークレット漏洩 — コンテキストの .env = 漏洩リスク。
  4. 幻覚パッケージ — LLMが存在しないnpmパッケージを推奨、攻撃者がそれを登録。

13章. 全チーム最低ライン

  1. HTMLページでnonce付きCSP v3 (unsafe-inlineでなく)。
  2. セッションクッキーに SecureHttpOnlySameSite
  3. HSTS preload。
  4. 全サードパーティスクリプトにSubresource Integrity。
  5. Dependabot/Renovate + audit workflow。
  6. クライアントバンドルにシークレットなし。
  7. CORS明示設定 (* でない)。
  8. 定期依存監査 (npm audit、Snyk)。
  9. エラー追跡 (Sentry) — 攻撃を早期検知。
  10. 認証変更にセキュリティレビュー。

12項目チェックリスト

  1. strict-dynamic + nonceのCSP v3?
  2. HSTS preload有効?
  3. 全サードパーティスクリプトにSRI?
  4. 認証にPasskeyオプション?
  5. 短命アクセストークン + リフレッシュローテ?
  6. XSS耐性フレームワークデフォルト (dangerouslySetInnerHTMLなし)?
  7. 認証クッキーに SameSite=Lax (またはStrict)?
  8. 本番でソースマップ保護?
  9. 依存監査自動化?
  10. セキュリティヘッダツールでA+?
  11. エラー追跡がCSP違反キャプチャ?
  12. シークレット管理監査 (NEXT_PUBLIC_ 漏洩なし)?

10アンチパターン

  1. ユーザーコンテンツで dangerouslySetInnerHTML
  2. JWTを localStorage に。
  3. Access-Control-Allow-Origin: * + credentials。
  4. CSP違反レポートを無視。
  5. 環境横断でJWTシークレット再利用。
  6. フルソース付きソースマップをデプロイ。
  7. URLクエリ文字列で認証トークン渡し。
  8. クライアントコードでJWT/クッキーをログ。
  9. SRIなしでランダムCDNスクリプトをバンドル。
  10. 自前暗号 (またはJWT実装) を作る。

次回予告

シーズン6 第10回: 状態管理ルネサンス 2025 — Zustand、Jotai、Valtio、TanStack Query、Signals、XState、RSC。2026 Reactアプリで各状態がどこに住むか。

— フロントエンドセキュリティ編、完。