本文へジャンプ

セキュリティー

脆弱性の報告

脆弱性が報告されると、直ちに私たちの最大の関心事となり、専任のコントリビューターが最優先で問題に取り組みます。脆弱性を報告するには、security@vuejs.org にメールをください。

新たな脆弱性が発見されることは稀ですが、アプリケーションを可能な限り安全に保つために、常に最新バージョンの Vue と公式・関連ライブラリーを使用することも推奨しています。

ルール No.1: 信頼できないテンプレートは使わない

Vue を使用する際の最も重要なセキュリティールールは、信頼できないコンテンツをコンポーネントテンプレートとして使用しないことです。これはアプリケーション内で恣意的な JavaScript の実行を許可することと同じです。さらに悪いことに、そのコードがサーバーサイドレンダリング中に実行されると、サーバーの不正アクセスにつながる可能性があります。そのような使い方の例:

js
Vue.createApp({
  template: `<div>` + userProvidedString + `</div>` // 絶対にこのようなことはしないでください
}).mount('#app')

Vue のテンプレートは JavaScript にコンパイルされ、テンプレート内の式はレンダリングプロセスの一部として実行されます。式は特定のレンダリングコンテキストを対象に評価されます。ですが、潜在的なグローバル実行環境の複雑性のため、Vue のようなフレームワークが非現実的なパフォーマンスのオーバーヘッドを発生させずに潜在的な悪意のあるコードの実行から完全に守ることは現実的ではありません。この種の問題を全て回避する最も簡単な方法は、Vue テンプレートのコンテンツが常に信頼され、完全に制御されていることを確認することです。

Vue があなたを守るためにしていること

HTML コンテンツ

テンプレートとレンダー関数のどちらを使用しても、コンテンツは自動的にエスケープされます。つまり、このテンプレートで:

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

userProvidedString が以下を含んでいるとしたら:

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

その時は、以下のような HTML にエスケープされます:

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

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

属性のバインディング

同様に、動的な属性バインディングも自動的にエスケープされます。つまり、このテンプレートで:

template
<h1 :title="userProvidedString">
  hello
</h1>

userProvidedString が以下を含んでいるとしたら:

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

その時は、以下のような HTML にエスケープされます:

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

このため、title 属性を閉じて、新しい恣意的な HTML を注入することを防ぎます。このエスケープは setAttribute のようなブラウザーのネイティブ API を使って行われるため、ブラウザー自体が脆弱である場合にのみ脆弱性が存在することになります。

潜在的な危険

どんなウェブアプリケーションにおいても、ユーザーが提供したサニタイズされていないコンテンツが HTML 、CSS 、JavaScript として実行されることは潜在的に危険であり、可能な限り避けるべきです。しかし、ある程度のリスクは許容できる場合もあります。

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

HTML の注入

先ほど学習したように、Vue は自動的に HTML コンテンツをエスケープするので、実行可能な HTML が誤ってアプリケーションに注入されるのを防ぐことができます。しかし、HTML が安全であることが分かっている場合、HTML コンテンツを明示的にレンダリングすることができます:

  • テンプレートの使用:

    template
    <div v-html="userProvidedHtml"></div>
    
  • レンダー関数の使用:

    js
    h('div', {
      innerHTML: this.userProvidedHtml
    })
    
  • JSX とレンダー関数の使用:

    jsx
    <div innerHTML={this.userProvidedHtml}></div>
    

WARNING

ユーザーが提供する HTML は、サンドボックス化された iframe 内や、その HTML を書いたユーザーのみがアクセスできるアプリケーションの一部でない限り、100% 安全とは言い切れません。さらに、ユーザーが独自の Vue テンプレートを書けるようにすることも同様の危険性をもたらします。

URL の注入

このような URL 内について、:

template
<a :href="userProvidedUrl">
  click me
</a>

URL が javascript: を使って JavaScript を実行できないように "sanitized"(サニタイズ)されていない場合、潜在的なセキュリティーの問題があります。これを助けるものとして sanitize-url のようなライブラリーがありますが、以下に注意してください: フロントエンドで URL のサニタイズを行っている場合、すでにセキュリティー上の問題があります。ユーザーが提供する URL は、データベースに保存される前に、常にバックエンドでサニタイズされるべきです。 そうすれば、ネイティブのモバイルアプリケーションを含む、API に接続するすべてのクライアントで問題が回避されます。また、サニタイズされた URL を使用しても、Vue は URL が安全な目的地につながることを保証することはできない点に注意してください。

スタイルの注入

この例を見てみると:

template
<a
  :href="sanitizedUrl"
  :style="userProvidedStyles"
>
  click me
</a>

sanitizedUrl はサニタイズされているので、本物の URL であり、JavaScript ではないことは間違いないとします。userProvidedStyles を使えば、悪意のあるユーザーはまだ "click jack"(クリックジャック)、例えば "ログイン" ボタンの上にリンクを入れた透明なボックスをスタイリングするような CSS を提供することができます。その時、もし https://user-controlled-website.com/ があなたのアプリケーションのログインページに似せて作られていたら、ユーザーの本当のログイン情報を取得するかもしれません。

ユーザーが提供するコンテンツを <style> 要素に許可すると、そのユーザーがページ全体のスタイルを完全に制御できるようになり、さらに大きな脆弱性が生じることは想像できるかもしれません。そのため、Vue では、次のようなテンプレート内の style タグのレンダリングを防止しています:

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

クリックジャックからユーザーを完全に保護するために、サンドボックス化された iframe 内でのみ CSS の完全な制御を許可することをお勧めします。また、ユーザーにスタイルバインディングのコントロールを提供する場合は、オブジェクト構文を使用し、以下のように安全に制御できる特定のプロパティにのみ値を提供できるようにすることをお勧めします:

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

JavaScript の注入

テンプレートやレンダー関数に副作用があってはならないので、Vue で <script> 要素をレンダリングすることは控えてください。しかし、実行時に JavaScript として評価される文字列を含める方法は、これだけではありません。

すべての HTML 要素には、onclickonfocusonmouseenter のような JavaScript の文字列を受け入れる値を持つ属性があります。これらのイベント属性のいずれかにユーザーが提供した JavaScript をバインドすることは、潜在的なセキュリティーリスクとなるため避けるべきです。

WARNING

ユーザーが提供する JavaScript は、サンドボックス化された iframe 内や、その JavaScript を書いたユーザーだけがアクセスできるアプリケーションの一部でない限り、100% 安全とは言い切れません。

Vue のテンプレートでクロスサイトスクリプティング (XSS) が可能であるという脆弱性の報告を受けることがあります。一般に、私たちは、このようなケースは実際の脆弱性とは考えません。なぜなら、XSS が起こりうる 2 つのシナリオから開発者を保護する現実的な方法がないからです:

  1. 開発者は、ユーザーが提供したサニタイズされていないコンテンツを Vue のテンプレートとしてレンダリングするよう、Vue に明示的に依頼しています。これは本質的に安全ではなく、Vue がその原因を知る方法はありません。

  2. 開発者は、サーバーがレンダリングしユーザー提供したコンテンツを含む HTML ページの全体に Vue をマウントしています。これは、基本的には #1 と同じ問題ですが、開発者がそうと気づかずにやってしまうことがあります。これは、プレーンな HTML としては安全ですが、Vue テンプレートとしては安全でないような HTML を攻撃者が提供するという脆弱性につながる可能性があります。ベストプラクティスは、サーバーレンダリングやユーザー提供のコンテンツを含む可能性のあるノードには Vue をマウントしないことです。

ベストプラクティス

一般的なルールとして、ユーザーが提供したサニタイズされていないコンテンツの実行(HTML 、JavaScript 、または CSS として)を許してしまうと、あなた自身が攻撃の対象になる可能性があります。このアドバイスは、Vue 、他のフレームワーク、またはフレームワークを使用しない場合にも当てはまります。

上記の潜在的な危険の推奨事項に加え、以下のリソースに精通することをお勧めします:

そして、学んだことを活かして、あなたの依存関係内のソースコードにサードパーティーのコンポーネント、もしくはレンダリングされた DOM に影響するようなものがあれば、危険性が潜むパターンがないか吟味し直してください。

バックエンドとの連携

クロスサイトリクエストフォージェリ (CSRF/XSRF) やクロスサイトスクリプトインクルージョン (XSSI) などの HTTP セキュリティー脆弱性は、主にバックエンドで対処されるため Vue の関心事ではありません。しかし、バックエンドのチームと話し合い、フォーム送信時に CSRF トークンを送信するなど、API との最適なやり取りの方法を学ぶのは良いアイデアです。

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

SSR を使用する場合、セキュリティー上の懸念があるため、SSR ドキュメントで説明されているベストプラクティスに従って、脆弱性を回避するようにしてください。

セキュリティーが読み込まれました