9.7. XSS対策¶
目次
9.7.1. Overview¶
9.7.1.1. Stored, Reflected XSS Attacks¶
XSS攻撃は、大きく二つのカテゴリに分けられる。
Stored XSS Attacks
Reflected XSS Attacks
9.7.1.1.1. How to use¶
必要に応じて、3種類のエスケープを使い分けること。
エスケープの種類:
- Output Escaping
- JavaScript Escaping
- Event handler Escaping
9.7.1.2. Output Escaping¶
エスケープ前
|
エスケープ後
|
---|---|
“
& ” |
& |
“
< ” |
< |
“
> ” |
> |
“
" ” |
" |
“
' ” |
' |
f:h()
を使用すること。9.7.1.2.1. 出力値をエスケープしない脆弱性のある例¶
本例は、あくまで参考例として載せているだけなので、以下のような実装は、決して行わないこと。
出力画面の実装
<!-- omitted -->
<tr>
<td>Job</td>
<td>${customerForm.job}</td> <!-- (1) -->
</tr>
<!-- omitted -->
項番 | 説明 |
---|---|
(1)
|
customerFormのフィールドである、jobをエスケープせず出力している。
|
入力画面のJobフィールドに、<script>タグを入力する。
9.7.1.2.2. 出力値をf:h()関数でエスケープする例¶
出力画面の実装
<!-- omitted -->
<tr>
<td>Job</td>
<td>${f:h(customerForm.job)}</td> <!-- (1) -->
</tr>
<!-- omitted -->
項番 | 説明 |
---|---|
(1)
|
EL式の
f:h() を使用することにより、エスケープして出力している。 |
入力画面のJobフィールドに<script>タグを入力する。
出力結果
<!-- omitted -->
<tr>
<td>Job</td>
<td><script>alert("XSS Attack")</script></td>
</tr>
<!-- omitted -->
Tip
java.util.Date継承クラスのフォーマット
java.util.Date継承クラスをフォーマットして表示する場合は、JSTLの<fmt:formatDate>
を用いることを推奨する。
以下に、設定例を示す。
<fmt:formatDate value="${form.date}" pattern="yyyyMMdd" />
valueの値に前述した f:h()
を使用して値を設定すると、Stringになってしまい、jakarta.el.ELException
がスローされるため、そのまま${form.date}
を使用している。しかし、yyyyMMddにフォーマットするため、XSSの心配はない。
Tip
java.lang.Number継承クラス、またはjava.lang.Numberにパースできる文字列
java.lang.Number継承クラスまたはjava.lang.Numberにパースできる文字列をフォーマットして表示する場合は、<fmt:formatNumber>
を用いることを推奨する。
以下に、設定例を示す。
<fmt:formatNumber value="${f:h(form.price)}" pattern="###,###" />
上記は、Stringでも問題ないので、<fmt:formatNumber>
タグを使わなくなった場合に f:h()
を付け忘れることを予防するため、f:h()
を明示的に使用している。
9.7.1.3. JavaScript Escaping¶
JavaScriptにおいてエスケープが必要な特殊文字の例と、エスケープ後の例は、以下のとおりである。
エスケープ前
|
エスケープ後
|
---|---|
“
' ” |
\' |
“
" ” |
\" |
“
\ ” |
\\ |
“
/ ” |
\/ |
“
< ” |
\x3c |
“
> ” |
\x3e |
0x0D(復帰) |
\r |
0x0A(改行) |
\n |
9.7.1.3.1. 出力値をエスケープしない脆弱性のある例¶
XSS問題が発生する例を、以下に示す。
本例は、あくまで参考例として載せているだけなので、以下のような実装は、決して行わないこと。
<html>
<script type="text/javascript">
var aaa = '${warnCode}';
alert(aaa);
</script>
</html>
属性名 | 値 |
---|---|
warnCode
|
';alert('XSS Attack!');aaa='message |
上記例のように、ユーザーの入力を導出元としてコードを出力するなど、JavaScriptの要素を動的に生成する場合、意図せず文字列リテラルが閉じられ、XSSの脆弱性が生じる。
出力結果
<script type="text/javascript">
var aaa = '';alert('XSS Attack!');aaa='message';
alert(aaa);
</script>
Tip
業務要件上必要でない限り、JavaScriptの要素をユーザーからの入力値に依存して動的に生成する仕様は、任意のスクリプトが埋め込まれてしまう可能性があるため、別の方式を検討する、または、極力避けるべきである。
9.7.1.3.2. 出力値をf:js()関数でエスケープする例¶
XSSを防ぐために、ユーザーの入力値、が設定される値にEL式の関数、f:js()
の使用を推奨する。
使用例を、下記に示す。
<script type="text/javascript">
var aaa = '${f:js(warnCode)}'; // (1)
alert(aaa);
</script>
項番 | 説明 |
---|---|
(1)
|
EL式の
f:js() を使用することにより、エスケープして変数に設定している。 |
出力結果
<script type="text/javascript">
var aaa = '\';alert(\'XSS Attack!\');aaa=\'message';
alert(aaa);
</script>
Warning
スクリプトタグが含まれる値を、HTMLエスケープせずf:js()
でエスケープさせて出力する場合、document.write()を使用すると、
ブラウザにHTMLソースとして解釈させるよう出力するので、XSSの脆弱性が生じる。以下に例を示すが、このような実装は決して行わないこと。
JSP
<script type="text/javascript"> var aaa = '${f:js(warnCode)}'; document.write(aaa); </script>
属性名 値 warnCode<script>alert('XSS Attack!');</script>
出力結果
<script type="text/javascript"> var aaa = '\x3cscript\x3ealert(\'XSS Attack!\');\x3c\/script\x3e'; document.write(aaa); </script>
出力結果をソースだけ確認するとエスケープできているように見える。しかし、これは<script>alert('XSS Attack!');</script>
という内容の文字列を変数aaaに格納するコードとなるため、document.write(aaa);
と実装してしまうと、HTMLのソースとして<script>alert('XSS Attack!');</script>
を出力することになる。その結果、スクリプトが実行される。
ブラウザに値を出力させたい場合は、JavaScriptを使用せず、HTML特殊文字をエスケープするf:h()
を使用することが望ましい。
JSP
${f:h(warnCode)}
出力結果
<script>alert('XSS Attack!');</script>
あえてf:js()
を使用し、document.write()で出力したい場合は、以下のいずれかのような、追加のXSS対策が必要である。
- HTMLエスケープ用のJavaScript関数を用意し、document.write()の引数をエスケープする。
f:h()
でユーザーの入力値が設定される値をHTMLエスケープした後、f:js()
でJavaScriptの文字列リテラル用のエスケープを行う。
9.7.1.4. Event handler Escaping¶
javascript のイベントハンドラの値をエスケープする場合、f:h()
や、f:js()
を使用するのではなく、f:hjs()
を使用すること。${f:h(f:js())}
と同義である。
理由としては、 <input type="submit" onclick="callback('xxxx');">
のようなイベントハンドラの値に');alert("XSS Attack");//
を指定された場合、別のスクリプトを挿入できてしまうため、文字参照形式にエスケープ後、HTMLエスケープを行う必要がある。
9.7.1.4.1. 出力値をエスケープしない脆弱性のある例¶
XSS問題が発生する例を、以下に示す。
<input type="text" onmouseover="alert('output is ${warnCode}') . ">
属性名 | 値 |
---|---|
warnCode
|
'); alert('XSS Attack!'); // 上記の値が設定されてしまうことで、意図せず文字列リテラルが閉じられ、XSSの脆弱性が生じる。
|
マウスオーバ時、XSSのダイアログボックスが表示されてしまう。
出力結果
<!-- omitted -->
<input type="text" onmouseover="alert('output is'); alert('XSS Attack!'); // .') ">
<!-- omitted -->
9.7.1.4.2. 出力値をf:hjs()関数でエスケープする例¶
使用例を、下記に示す。
<input type="text" onmouseover="alert('output is ${f:hjs(warnCode)}') . "> // (1)
項番 | 説明 |
---|---|
(1)
|
EL式の
f:hjs() を使用することにより、エスケープして引数としている。 |
マウスオーバ時、XSSのダイアログは出力されない。
出力結果
<!-- omitted -->
<input type="text" onmouseover="alert('output is \'); alert(\'XSS Attack!\');\" \/\/ .') ">
<!-- omitted -->