概要
フロントエンドにおけるセキュリティとは、
「攻撃を防ぐこと」ではなく、「壊れない構造を設計すること」である。
XSS、CSRF、セッションハイジャック、トークン漏洩、悪意ある入力…
JavaScriptがユーザーのインターフェースを担う今、クライアント側の設計品質がそのまま防御力となる。
本稿では、JavaScriptにおける代表的な脅威とその対策、
およびUI/UXを壊さずにセキュリティを担保する構造的アプローチを紹介する。
1. XSS(クロスサイトスクリプティング)対策
// ❌ 危険なパターン:innerHTMLに直接出力
element.innerHTML = userInput;
✅ 解決策:
- innerText / textContent を使う
- DOM生成を明示的に構築
const text = document.createTextNode(userInput);
element.appendChild(text);
- HTMLテンプレートエンジンでは、サニタイズ処理の有無を常に確認
2. CSRF(クロスサイトリクエストフォージェリ)対策
- GETでは副作用のある操作を絶対に許可しない
- Cookieベースの認証にはSameSite=Strict or Lax を設定
- 非Cookieベースの認証ではCSRFトークンを埋め込み + サーバーで検証
<input type="hidden" name="csrf_token" value="xxxx">
- フロントエンドから送信:
fetch('/api/update', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
},
});
3. トークン管理:localStorage / sessionStorage / Cookie の選択戦略
保管場所 | 特性 |
---|---|
localStorage | 永続。XSSに弱い |
sessionStorage | タブごと。XSSに弱い |
Cookie | Secure / HttpOnly / SameSite 可。CSRF対策必須 |
✅ 原則:
- XSS対策が万全でなければ Cookie + HttpOnly が最も安全
- SPAでアクセストークンを扱う場合は、期限 + scope + リフレッシュ設計が必要
4. エスケープと検証の境界戦略
✅ サーバーサイド → サニタイズ済みを前提にしない
✅ クライアント側 → 出力前にescapeHTML
function escapeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
→ ✅ UI層で必ず**「この値は表示される」ことを想定して防御**する
5. ユーザー入力を介するイベントの設計
- イベント名 / クラス名 / data属性 にユーザー入力を直接使用しない
// ❌ 危険な例
const el = document.querySelector(`[data-user="${input}"]`);
→ ✅ Map
や id-index
でセーフな検索手段に分離
6. エラーとロギング:セキュリティ情報の漏洩防止
- ✅ APIの失敗時にスタックトレース・内部構造を出力しない
- ✅ ログにアクセストークンや個人情報を含めない
console.error('ユーザー取得に失敗しました'); // ✅ OK
console.error(error.stack); // ❌ 内部構造が露出
設計判断フロー
① 出力箇所にinnerHTMLを使っていないか? → textContentを選択
② トークンがlocalStorageにあり、XSSに晒されていないか? → Cookie + HttpOnlyを検討
③ イベント/属性でユーザー入力を扱っていないか? → DOM設計を再構成
④ トークンや個人情報をconsole.logで出力していないか? → ロギングポリシー設計へ
⑤ CSRF保護が不完全なままCookieを使っていないか? → SameSite/Tokenの導入
よくあるミスと対策
❌ すべての出力にinnerHTMLを使い、XSS攻撃を許容
→ ✅ 表示は textContent、構造は DOM構築で対応
❌ アクセストークンをlocalStorageに永続化し、別ページでも利用可能に
→ ✅ トークンは必要なタイミングでメモリ保持 + 期限管理 or HttpOnly Cookie
❌ fetch時にCSRFトークンを含め忘れ、保護バイパスが可能に
→ ✅ API設計時にトークン含有をデフォルト設計
結語
セキュリティとは「攻撃をブロックする」ことではない。
それは**「攻撃される前提で設計する」こと**である。
- 表示前に必ず疑い
- イベント前に経路を遮断し
- 情報が漏れることを想定して制御する
JavaScriptにおけるセキュリティ設計とは、
“フロントエンドが最後の砦であることを理解した構造の構築”である。