ゼロトラストフロントエンドセキュリティアーキテクチャ:認証からAPI保護まで
前端安全高橋 翔
ゼロトラストセキュリティモデル:決して信頼せず、常に検証する
2026年のフロントエンドセキュリティ状況はかつてないほど厳しくなっています。従来の「城と堀」モデルは完全に失敗しました。ゼロトラストセキュリティの中核原則:
決して信頼せず、常に検証する(Never Trust, Always Verify)
すべてのリクエスト、すべてのインタラクションは、出所に関係なく厳格な認証と認可チェックを受ける必要があります。
従来の境界セキュリティ vs ゼロトラストアーキテクチャ
| 次元 | 境界セキュリティ | ゼロトラスト |
|---|---|---|
| 信頼モデル | 内部信頼、外部不信 | 全リクエスト不信 |
| 検証方式 | 一回限りのログイン | リクエストごとの検証 |
| ネットワーク境界 | ファイアウォール/VPN依存 | 境界なし、アイデンティティが境界 |
| 攻撃面 | 境界突破後の横移動可能 | 各ステップで再検証 |
| 適用対象 | モノリシックアプリ | クラウドネイティブ/マイクロサービス |
フロントエンドのゼロトラスト
フロントエンドはゼロトラストアーキテクチャで最も重要なコンポーネントです。フロントエンドコードはユーザーのブラウザで実行され、完全に制御不可能な環境だからです。
コア原則
- 暗黙の信頼なし:ログイン済みだからといって後続リクエストを信頼しない
- 最小権限:各リクエストは必要最小限の権限のみ取得
- 継続的検証:トークンは短期有効、頻繁にリフレッシュ
- 侵害を前提:攻撃者がフロントエンドを制御していると仮定して設計
リクエストレベル検証
class ZeroTrustHttpClient {
private tokenManager: TokenManager;
private proofManager: DPoPProofManager;
async request(config: RequestConfig): Promise<Response> {
const token = await this.tokenManager.getValidToken();
const proof = await this.proofManager.generateProof(
config.method, config.url
);
const response = await fetch(config.url, {
method: config.method,
headers: {
'Authorization': `DPoP ${token}`,
'DPoP': proof,
'X-Request-ID': crypto.randomUUID(),
'X-Timestamp': Date.now().toString(),
},
body: config.body,
});
if (response.status === 401) {
await this.tokenManager.refreshToken();
return this.request(config);
}
return response;
}
}
トークンセキュリティ:JWTベストプラクティス
| プラクティス | 説明 | リスクレベル |
|---|---|---|
| 短命Access Token | TTL ≤ 5分 | 🔴 クリティカル |
| HttpOnly Cookie保存 | XSS窃取防止 | 🔴 クリティカル |
| Refresh Tokenローテーション | リフレッシュごとに新しいRTを発行 | 🔴 クリティカル |
| トークンバインディング(DPoP) | トークンをキーペアにバインド | 🟡 重要 |
| Audience制限 | トークン使用対象を限定 | 🟡 重要 |
DPoP:証明所持デモンストレーション
DPoPはトークンをクライアントのキーペアにバインドします。攻撃者がトークンを盗んでも、対応する秘密鍵がなければ使用できません:
class DPoPProofManager {
private keyPair: CryptoKeyPair | null = null;
async init(): Promise<void> {
this.keyPair = await crypto.subtle.generateKey(
{ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']
);
}
async generateProof(method: string, url: string): Promise<string> {
const header = { typ: 'dpop+jwt', alg: 'ES256', jwk: await this.getPublicKey() };
const payload = {
htu: new URL(url).origin + new URL(url).pathname,
htm: method,
iat: Math.floor(Date.now() / 1000),
jti: crypto.randomUUID(),
};
return this.signJWT(header, payload);
}
}
ランタイム保護:CSP、Trusted Types、SRI
厳格なCSPポリシー
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
script-src 'strict-dynamic' 'nonce-abc123';
style-src 'self' 'nonce-abc123';
img-src 'self' https: data:;
connect-src 'self' https://api.example.com;
frame-src 'none';
object-src 'none';
require-trusted-types-for 'script';
report-uri /api/csp-report;
">
Trusted TypesによるXSS防止
if (window.trustedTypes) {
trustedTypes.createPolicy('default', {
createHTML: (input: string) => DOMPurify.sanitize(input, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
}),
createScript: () => { throw new Error('Blocked'); },
});
}
Subresource Integrity(SRI)
<script src="https://cdn.example.com/bundle.js"
integrity="sha384-oc5F5A5B7B6B8B9C0D1E2F3"
crossorigin="anonymous"></script>
サプライチェーンセキュリティ
# 既知の脆弱性を監査
npm audit --audit-level=high
# lockfile整合性を検証
npm ci --ignore-scripts
# SBOMを生成
npx @cyclonedx/cyclonedx-npm --output-format json -o sbom.json
ベストプラクティス:CIビルドごとにSBOMを生成し、脆弱性データベース(OSV、NVD)と自動比較する。
ゼロトラストフロントエンドアーキテクチャチェックリスト
認証と認可
- Access Token TTL ≤ 5分
- Refresh Tokenローテーション有効
- TokenはHttpOnly + Secure + SameSite=Strict Cookieに保存
- DPoPトークンバインディング有効
ランタイム保護
- CSPを
default-src 'none'ホワイトリストモードに設定 - Trusted Typesポリシーを設定
- すべてのCDNリソースにSRIを設定
-
eval()、new Function()を無効化
サプライチェーン
-
npm auditをCIパイプラインに統合 - lockfile整合性検証を有効化
- 各ビルドでSBOMを生成
ゼロトラストは製品ではなく、セキュリティの考え方である。 フロントエンドアプリケーションのすべての信頼仮定を見直し、「デフォルト信頼」から「デフォルト拒否、明示的許可」へ転換する。
ブラウザローカルツールを無料で試す →
#零信任#前端安全#Token验证#CSP#微前端安全