みーのぺーじ

みーが趣味でやっているPCやソフトウェアについて.Python, Javascript, Processing, Unityなど.

Vue.jsのv-bindにObjectを使うときはXSSに気をつける

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を生成します.

API — Vue.js

<!-- binding an object of attributes -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

つまり,指定したObjectにユーザーの入力が含まれる場合はXSSの原因となります.

解決方法

inheritAttrsをfalseにすれば,ルート要素にattributeが追加されなくなります.

API — Vue.js

簡単なXSSのサンプル

goodParagraph.vueinheritAttrsを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レポジトリに公開しています.

https://github.com/atsuhiro-me/vue-v-bind-xss