セキュリティ

最終更新日: 2020年2月5日

脆弱性の報告

脆弱性が報告された場合、それは直ちに私達にとって最大の課題となり、フルタイムのコントリビュータがその解決に取り組みます。脆弱性を報告するには vuejs.org@gmail.com へメールを送信してください。

新たな脆弱性の発見はめったにありませんが、常に Vue とその公式ライブラリの最新バージョンを使用し、アプリケーションのセキュリティを可能な限り維持することをお勧めします。

ルール No.1: 信頼できないテンプレートを絶対に使わない

Vue を使うときの最も基本的なセキュリティルールは、信頼できないコンテンツをコンポーネントのテンプレートとして絶対に使わない ということです。そうすることは、あなたのアプリケーション内で任意の JavaScript 実行を許してしまうことと同じです。さらに悪いことに、コードがサーバーサイドレンダリング中に実行された場合には、サーバー側の欠陥につながります。例えば、次のような使い方です:

new Vue({
  el: '#app',
  template: `<div>` + userProvidedString + `</div>` // 絶対にしてはいけない
})

Vue のテンプレートは JavaScript にコンパイルされ、テンプレート中の式はレンダリング処理の過程で実行されます。式は特定のレンダリングコンテキストで評価されますが、潜在的にはグローバルに実行される複雑な実行環境となるため、Vue のようなフレームワークでパフォーマンスの非現実的なオーバーヘッドを受けることなく悪意のあるコード実行を完全に防ぐことは困難です。この手の問題を回避する最も直接的な方法は、完全にあなたの管理下にある信頼されたコンテンツだけを Vue テンプレートのコンテンツにすることです。

Vue が行っているセキュリティ対策

HTML コンテンツ

テンプレートを使用する場合も、描画関数を使用する場合も、コンテンツは自動的にエスケープ処理されます。例えばこのようなテンプレートの場合:

<h1>{{ userProvidedString }}</h1>

もし userProvidedString に以下が含まれていると:

'<script>alert("hi")</script>'

以下のようにエスケープされた HTML になります。

&lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;

Vue では、このようにスクリプトのインジェクションを防ぎます。このエスケープ処理は textContent 等のネイティブブラウザ API を使用して行われるため、ブラウザ自体が脆弱な場合のみ脆弱性が存在します。

属性のバインディング

同様に、動的な属性のバインディングも自動でエスケープ処理がされます。例えばこのようなテンプレートの場合:

<h1 v-bind:title="userProvidedString">
  hello
</h1>

もし userProvidedString に以下が含まれていると:

'" onclick="alert(\'hi\')'

以下のようにエスケープされた HTML になります。

&quot; onclick=&quot;alert('hi')

このように、title 属性に任意の HTML がインジェクトされることを防ぎます。このエスケープ処理も setAttribute 等のネイティブブラウザ API を使用して行われるため、ブラウザ自体が脆弱な場合のみ脆弱性が存在します。

潜在的な危険

どのような Web アプリケーションでも、ユーザの入力による無害化 (sanitize)されていないコンテンツを HTML、CSS、JavaScript として実行できるようにすることは潜在的な危険があるため、可能な限り避ける必要があります。しかしながら、ある程度のリスクなら許容される場合もあります。

例えば、CodePen や JSFiddle などのサービスでは、ユーザによって入力されるコンテンツを実行できます。しかし、これは想定通りで、iframe 内である程度サンドボックス化されている状況においてです。重要な機能が本質的にある程度の脆弱性を必要とする場合、その機能の重要性と脆弱性による最悪のシナリオとをてんびんにかけることは、あなたのチームに委ねられます。

HTML の挿入

前述の通り、Vue では HTML コンテンツを自動でエスケープ処理し、誤って実行可能な HTML をアプリケーション内に挿入することを防いでいます。

ただし、HTML が安全なことが事前にわかっている場合は明示的にそれをレンダリングすることが可能です:

ユーザが入力した HTML は、サンドボックス化された iframe、またはそれを入力したユーザのみがアクセスできるアプリの一部という場合を除き、100%安全とはみなされないことに注意して下さい。また、ユーザが独自の Vue テンプレートを作成できるようにすることも同様の危険があります。

URL の挿入

このような URL の場合:

<a v-bind:href="userProvidedUrl">
  click me
</a>

URL を “無害化 (sanitize)” して javascript: の利用による JavaScript 実行を防いでいない場合、潜在的なセキュリティの問題があります。これの対策に、sanitize-url 等のライブラリがあります。しかし注意してください:

フロントエンドで URL の無害化 (sanitize)処理を行ったことがある場合、それはすでにセキュリティ上の問題をはらんでいます。ユーザの入力による URL は、常にバックエンドでデータベースに保存する前の処理が必要です。そうすることでモバイルのネイティブアプリを含め、API に接続するすべてのクライアントで問題を回避することができます。また、無害化 (sanitize)処理がされた URL だとしても、Vue はリンク先の安全性を保証することはできません。

スタイルの挿入

この例を見てください:

<a
  v-bind:href="sanitizedUrl"
  v-bind:style="userProvidedStyles"
>
  click me
</a>

sanitizedUrl は無害化 (sanitize)されていると仮定しましょう。 これは紛れもなく実際の URL で、JavaScript ではありません。userProvidedStyles を利用すると、悪意のあるユーザは”クリックジャック”のための CSS を設置できます。例えば、ログインボタンの上の透明ボックスにリンクをスタイリングします。そして、ログインページに似せた https://user-controlled-website.com/ というサイトが構築されていた場合、ユーザーはログイン情報を盗まれてしまうかもしれません。

<style> 要素に対してユーザの入力するコンテンツを許可してしまうと、更に大きな脆弱性が発生し、そのページ全体のスタイルが制御されてしまうということが想像できるでしょう。そのために、Vue では以下のようなテンプレート内でのスタイルタグのレンダリングはやめるべきです:

<style>{{ userProvidedStyles }}</style>

ユーザによるクリックジャックを完全に防止するためには、CSS の完全な制御は iframe 内のサンドボックス化された場所でのみ行うことをお勧めします。または、スタイルバインディングを介してユーザに制御を与えたい場合は、オブジェクト構文 を利用し、特定のプロパティの値だけを設定できるようにすることをお勧めします、この例のように:

<a
  v-bind:href="sanitizedUrl"
  v-bind:style="{
    color: userProvidedColor,
    background: userProvidedBackground
  }"
>
  click me
</a>

JavaScript の挿入

テンプレートと描画関数は副作用をもつべきではないため、Vue で <script> 要素をレンダリングすることは強くお勧めしません。ただしこれは、実行時に JavaScript として評価される文字列を含めるための唯一の方法ではありません。

すべての HTML 要素には、onclickonfocusonmouseenter などの JavaScript 文字列を値として受け入れる属性があります。ユーザによって入力された JavaScript をこれらのイベント属性にバインドすることは、潜在的なセキュリティリスクとなるので避ける必要があります。

ユーザが入力した JavaScript は、サンドボックス化された iframe、またはそれを入力したユーザのみがアクセスできるアプリの一部という場合を除き、100%安全とはみなされないことに注意してください。

Vue テンプレートでのクロスサイトスクリプティング (XSS) を実行する方法について、脆弱性レポートを受け取ることが時折あります。一般的に、XSS を許可する以下の2つの例から開発者を保護する実用的な方法は無いため、このような場合は脆弱性とは見なしません。

  1. 開発者が、ユーザ入力による無害化 (sanitize)されていないコンテンツを Vue テンプレートとしてレンダリングするように明示的に指示している場合。これは本質的に安全ではなく、また Vue がそのオリジナルのコンテンツを知る方法はありません。

  2. 開発者が、サーバでレンダリングされたユーザ入力のコンテンツを含む HTML ページ全体に Vue をマウントしている場合。問題は基本的に#1と同じですが、開発者が気づかぬうちにそうしてしまうことがあります。これにより、プレーンな HTML としては無害だとしても、Vue テンプレートとしては安全でない HTML を攻撃者が設置することできる脆弱性が発生します。ベストプラクティスは、サーバーでレンダリングされたユーザー入力のコンテンツ (user-provided content)要素に Vue をマウントしないことです。

ベストプラクティス

一般的なルールとして、無害化 (sanitize)されていないユーザー入力のコンテンツ (user-provided content)(HTML、CSS、JavaScript)を実行することは、攻撃にさらされる可能性があるということです。このアドバイスは Vue に限らず、他のフレームワーク、さらにはフレームワークを利用していない場合でも有効です。

潜在的な危険の推奨事項に加え、以下のリソースを熟知しておくことをお勧めします。

DOM へ描画するようなサードパーティ製のコンポーネントを使用する場合は、先に学んだ内容を利用して依存するソースコードの潜在的な危険パターンについてチェックしてください。

バックエンドとの調整

クロスサイト・リクエストフォージェリ ( CSRF / XSRF ) や クロスサイト・スクリプトインクルージョン ( XSSI ) などの HTTP の脆弱性は、主にバックエンド側で対処されるため、Vue で気にする必要はありません。ですが、バックエンドチームと連携を取り、API との最適な対話方法 (例えばフォーム送信時に CSFR トークンを送信する。など) を学ぶことをお勧めします。

サーバーサイドレンダリング (SSR)

SSR を利用する場合、上記の推奨事項に加えていくつかセキュリティ上の懸念があります。別途、Vue.js サーバサイドレンダリングガイド で解説されているベストプラクティスを参照してください。