Vue.jsを使うときはDOM-based XSSに注意しなければなりません.例えば,v-html
がXSSの原因となる可能性があることは有名です.
Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS attacks. Only use v-html on trusted content and never on user-provided content. *1
v-bindとJSONを組み合わせることで,同様にXSSの原因となる場合があることを紹介します.
v-bind
v-bindを引数なしで使うと,Vue.jsは指定したObjectに従ってルート要素のattributeを生成します.
<!-- binding an object of attributes --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
つまり,指定したObjectにユーザーの入力が含まれる場合はXSSの原因となります.
解決方法
inheritAttrs
をfalseにすれば,ルート要素にattributeが追加されなくなります.
簡単なXSSのサンプル
goodParagraph.vue
はinheritAttrs
をfalseに指定したコンポーネントで,badParagraph.vue
は初期設定のままのコンポーネントです.それ以外は全く同じです.これらに同じmessage
変数をv-bind
で指定してレンダリングしてみます.
App.vue
<template> <div id="app"> <good-paragraph v-bind="message" /> <bad-paragraph v-bind="message" /> </div> </template> <script> import GoodParagraph from "./components/goodParagraph.vue"; import BadParagraph from "./components/badParagraph.vue"; export default { name: "App", components: { GoodParagraph, BadParagraph, }, data() { return { message: { text: "body", onClick: "javascript:alert(1);", }, }; }, }; </script>
goodParagraph.vue
<template> <div> <h1>GoodParagraph</h1> <p v-text="text" /> </div> </template> <script> export default { inheritAttrs: false, props: { text: String, }, }; </script>
badParagraph.vue
<template> <div> <h1>BadParagraph</h1> <p v-text="text" /> </div> </template> <script> export default { inheritAttrs: true, props: { text: String, }, }; </script>
実行結果
<div id="app"> <div> <h1>GoodParagraph</h1> <p>body</p> </div> <div onclick="javascript:alert(1);"> <h1>BadParagraph</h1> <p>body</p> </div> </div>
上記の例では,badParagraph.vue
にonclick
attributeを追加できていますが,inheritAttrs
をfalseにすることで,XSSが回避できていることが分かります.
このサンプルは以下のGitHubレポジトリに公開しています.