Upload
ryo-kato
View
5.224
Download
45
Embed Size (px)
Citation preview
React.js に XSS 対策を 求めるのは間違っている
だろうか
#edomaesec 2015 5/30 LT
@clariroid
くらりど
React.js
React.js
React.js
青い紐
React.js
• UI を構築する JavaScript ライブラリ
React.js
• UI を構築する JavaScript ライブラリ
• Virtual DOM
React.js
• UI を構築する JavaScript ライブラリ
• Virtual DOM
• Component Composable
React.js
• UI を構築する JavaScript ライブラリ
• Virtual DOM
• Component Composable
• Server-side Rendering
• etc ...
React.js
• UI を構築する JavaScript ライブラリ
• (省略)
React.js
• UI を構築する JavaScript ライブラリ
• XML like な JavaScript – JSX で記述
React.js – JSX
var str = "Hello, React!";
React.render(
<span>{str}</span>,
document.body
);
React.js – JSX
var str = "Hello, React!";
React.render(
<span>{str}</span>, // js 式は { } で括る
document.body
);
React.js – JSX
• JSX Transformer
• JSX を生の JavaScript に変換
React.js – JSX
var str = "Hello, React!";
React.render(
React.createElement("span", null, str),
document.body
);
React.js – JSX
• 結果として生成される HTML
React.js – JSX
<body>
<span data-reactid=".0">
Hello, React!
</span>
</body>
React.js
• エスケープは自動でしてくれる
• が、React.js が XSS 対策にどこまで責任を負うかは議論となっている
• Issue #3473 “How Much XSS Vulnerability Protection is React Responsible For?”
React.js
• ユーザが入力した文字列から生成した 新しい DOM node を挿入したい
• Ex.) Markdown から変換された文字列
React.js
```Markdown
** edomaesec! **
```HTML
<em>edomaesec!</em>
React.js
• 直に innerHTML を叩くと怒られる
• React 版 innerHTML
• dangerouslySetInnerHTML API の利用
dangerouslySetInnerHTML
dangerouslySetInnerHTML
• 公式の Tutorial でも Backdoor な API として 使っている
• こんな風に使う
dangerouslySetInnerHTML
var str = "<em>edomaesec!</em>";
var obj = { __html: str };
React.render(
<span dangerouslySetInnerHTML={obj}/>,
document.body
);
dangerouslySetInnerHTML
var str = "<em>edomaesec!</em>";
var obj = { __html: str };
React.render(
<span dangerouslySetInnerHTML={obj}/>,
document.body
);
dangerouslySetInnerHTML
• 公式の docs より抜粋・翻訳
-- 不安を感じる言葉で意図的に名付けました。 -- プロパティ値はサニタイズされたデータを示す -- ために使われることでしょう。
dangerouslySetInnerHTML
• どれほど dangerously なのか分からない
• ソースコードを追ってみた
• react.js v0.13.3 ( 全19602行 )
• デバッガ使いましょう
dangerouslySetInnerHTML
var str = "<em>edomaesec!</em>";
var obj = { __html: str };
React.render(
<span dangerouslySetInnerHTML={obj}/>,
document.body
);
dangerouslySetInnerHTML
var str = "<em>edomaesec!</em>";
var obj = { __html: str };
React.render(
React.createElement(
"span", {dangerouslySetInnerHTML: obj}
), document.body
);
dangerouslySetInnerHTML
• L: 9928 ReactElement
{ key: “span”,
_store: {
props: {
dangerouslySetInnerHTML: { __html: str }
}
}
}
dangerouslySetInnerHTML
• ( DOM更新のトランザクションを開くコードが永遠と続くので中略)
dangerouslySetInnerHTML
• L: 7641 mountComponent
return
_createOpenTagMarkupAndPutListeners
+ _createContentMarkup
+ closeTag
dangerouslySetInnerHTML
• L: 7641 mountComponent
return
_createOpenTagMarkupAndPutListeners
+ _createContentMarkup
+ </span>
dangerouslySetInnerHTML
• L: 7664 _createOpenTagMarkupAndPut…
return
<span date-reactid=".0">
+ _createContentMarkup
+ </span>
dangerouslySetInnerHTML
• L: 7641
return
<span date-reactid=".0">
+ _createContentMarkup
+ </span>
dangerouslySetInnerHTML
• L: 7711 _createContentMarkup
var innerHTML
= props.dangerouslySetInnerHTML;
return "" + innerHTML.__html;
• __html の内容について何も確認なし
dangerouslySetInnerHTML
• L: 7641
return
<span date-reactid=".0">
+ <em>edomaesec!</em>
+ </span>
dangerouslySetInnerHTML
• L: 18971 setInnerHTML
node.innerHTML = html;
// html 先程 return された文字列
Demo
• str = "<input autofocus onfocus=alert(1)>" を代入してみる
まとめ
• React.js の dangerouslySetInnerHTML API は 通常の innerHTML 挿入と等価である
• 使わないようにリスク回避で設計したい
• dangerouslySetInnerHTML を利用する際は エスケープ(場合によってはサニタイズ)した文字列を代入させる
まとめ
サニタイズって簡単に言うけれども…
• RickDOM の使用
• marked.js の sanitize parameter を true にする ( Markdown のみ)
• HTML5 sandboxed iframe で wrap しておく ( js の実行を抑制 )
参考資料
• Dangerously Set innerHTML | React – e10s https://facebook.github.io/react/tips/dangerously-set-inner-html.html
• Yosuke Hasegawa『文字列から HTML を組み立てる話』@Shibuya.XSS techtalk #5 http://utf-8.jp/public/20140807/shibuyaxss.pdf