17
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【対策】XSS・CSRF・クライアントサイドセキュリティ完全対策ガイド

17
Posted at

⚠️ 注意:
日本語は勉強中のため、本記事は一部AIを使用して作成しています。
不自然な表現や誤りがあればご指摘いただけると嬉しいです 🙏

はじめに

Web開発において「自分はもうセキュリティ対策をしている」という思い込みが、最も危険な脆弱性になり得ます。
特にフロントエンドが中心のアプリケーション(React、Vue、SPAなど)では、XSS、CSRF、クライアントサイドのセキュリティ が軽視されがちです。

この記事では、実務でそのまま使える知識を提供します:

  • XSS(クロスサイトスクリプティング)
  • CSRF(クロスサイトリクエストフォージェリ)
  • クライアントサイドセキュリティ(LocalStorage、トークン管理など)

👉 「なぜ危険なのか」と「どう防ぐのか」を両方理解できる内容です。

1. XSS(クロスサイトスクリプティング)

XSSとは?

攻撃者が悪意あるスクリプト(JavaScript)をWebサイトに挿入し、被害者のブラウザ上でそのスクリプトが実行される攻撃です。

<input value="<script>alert('Hacked')</script>" />

💥 被害例

  • Cookieやログイントークンの盗難
  • セッションハイジャック
  • ユーザーの意図しない操作(投稿、送金など)の実行

🔥 絶対にやってはいけない例

❌ ユーザー入力値をそのまま innerHTML に代入

element.innerHTML = userInput;   // 非常に危険

👉 たったこれだけで、あなたのWebサイトは乗っ取られる可能性があります。

✅ XSSを防ぐための対策

1. エスケープ処理

専用ライブラリ DOMPurify を使用する:

import DOMPurify from 'dompurify';

const safeHTML = DOMPurify.sanitize(userInput);
element.innerHTML = safeHTML;

2. フレームワークの組み込み機能を使う

React はJSX内で自動エスケープします:

<div>{userInput}</div>   // 安全 – Reactがエスケープしてくれる

⚠️ ただし、次のようなコードは危険:

<div dangerouslySetInnerHTML={{ __html: userInput }} />

👉 これがいわゆる「XSSの裏口」です。リスクを完全に理解し、適切にサニタイズした場合のみ使ってください。

3. Content Security Policy (CSP)

CSPは最後の防御層です。ブラウザに対して「どこからスクリプトを読み込んで良いか」を指示します。

Content-Security-Policy: default-src 'self'; script-src 'self'

4. HttpOnly Cookie

Set-Cookie: token=abc123; HttpOnly; Secure

HttpOnly を設定すると、JavaScriptからCookieを読み取れなくなります → XSSが発生してもトークンを盗まれません。


2. CSRF(クロスサイトリクエストフォージェリ)

CSRFとは?

ユーザーが既にログインしている状態を悪用し、ユーザーの意図に反してリクエストを送信させる攻撃です。

💥 攻撃例

攻撃者が掲示板に次のような画像タグを埋め込みます:

<img src="https://bank.com/transfer?amount=10000&to=attacker" />

銀行にログインした状態でその掲示板を開くだけで、勝手に送金されてしまいます。

✅ CSRFを防ぐための対策

1. CSRFトークン

サーバーがランダムなトークンを生成し、クライアント(フォームやヘッダー)に送信します。
データを変更するリクエストには必ずこのトークンを含めます。

<input type="hidden" name="csrf_token" value="random_token_xyz" />

サーバー側での検証例:

if (req.body.csrf_token !== session.csrf_token) {
  return res.status(403).send("Forbidden");
}

2. SameSite Cookie

SameSite=Strict または Lax を付けるだけで、ほとんどのCSRFを防げます。

Set-Cookie: session=abc; SameSite=Strict; Secure

3. Double Submit Cookie

トークンをCookieとリクエストヘッダーの両方で送信し、サーバーで比較します。

4. Cookieを使わず、Authorization Headerを使用する

Authorization: Bearer <jwt_token>

ブラウザは異なるオリジンへのリクエストにこのヘッダーを自動添付しません → CSRFは成立しません。


3. クライアントサイドのセキュリティ

LocalStorage – 諸刃の剣

多くのアプリケーションがJWTを localStorage に保存しています:

localStorage.setItem("token", jwt);

👉 XSSがあった場合、localStorage.getItem("token") するだけで攻撃者はトークンを盗めます。

✅ 安全な設計

✔ トークンは HttpOnly Cookie で保存する

JavaScriptからアクセス不可 → XSSでも盗まれません。

✔ パスワードや秘密鍵など機密データはクライアントに保存しない

❌ 悪い例:

const API_SECRET = "123456";

✔ 入力値のバリデーション(クライアント+サーバー両方)

クライアント側でも簡単なチェックをしますが、サーバー側でのバリデーションは必須です。

if (!email.includes("@")) {
  throw new Error("Invalid email");
}

✔ HTTPSの強制

  • Man-in-the-Middle(MITM)攻撃を防ぎます
  • Cookieに Secure フラグを付けられます

✔ 正しいCORS設定

本番環境では Access-Control-Allow-Origin: * を使わないでください。

Access-Control-Allow-Origin: https://yourdomain.com

🚨 よくある誤解

誤解 現実
Reactを使っていれば安全 dangerouslySetInnerHTML を使うとXSS発生
JWTは安全 localStorage に保存している時点で危険
フロントエンドだけ対策すればOK ❌ サーバー側の防御が最終ライン

まとめ

ゴールデンルール

  • XSS → エスケープ、CSP、信頼できない入力に対する innerHTML 禁止
  • CSRF → CSRFトークン または SameSite=Strict Cookie
  • クライアント → トークンは HttpOnly Cookie に保存。LocalStorageに機密情報を入れない

実務チェックリスト

  • innerHTML をユーザー入力に対して使っていないか?
  • CSPを設定しているか?
  • CSRF対策(トークン / SameSite)を導入しているか?
  • Cookieに SameSiteSecure が付いているか?
  • トークンやパスワードを localStorage に保存していないか?
  • 全ページでHTTPSを強制しているか?
  • CORS設定が * になっていないか?
17
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?