Skip to content

Migrate security guide from Vue 2 #418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const sidebar = {
{
title: 'スケールアップ',
collapsable: false,
children: ['/guide/routing', '/guide/state-management', '/guide/ssr']
children: ['/guide/routing', '/guide/state-management', '/guide/ssr', '/guide/security']
},
{
title: 'アクセシビリティ',
Expand Down
187 changes: 187 additions & 0 deletions src/guide/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# セキュリティ

## 脆弱性の報告

脆弱性が報告されると、それは直ちに私たちの最重要課題となり、フルタイムコミットのコントリビュータがすべてを投げ出して取り組みます。脆弱性の報告は、[security@vuejs.org](mailto:security@vuejs.org) までメールしてください。

新しい脆弱性が発見されることはめったにありませんが、あなたのアプリケーションを可能な限り安全に保つために、Vue とその公式ライブラリの最新バージョンを常に利用することをおすすめします。

## 一番のルール: 信頼できないテンプレートを使わない

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

```js
Vue.createApp({
template: `<div>` + userProvidedString + `</div>` // 絶対にやってはいけないこと
}).mount('#app')
```

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

## Vue があなたを守るために行っていること

### HTML コンテンツ

テンプレートでも Render 関数でも、コンテンツは自動的にエスケープされます。つまり、このテンプレートでは:

```html
<h1>{{ userProvidedString }}</h1>
```

`userProvidedString` が含まれている場合:

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

これは次のような HTML にエスケープされます:

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

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

### 属性の束縛

同じように、動的な属性の束縛も自動的にエスケープされます。つまり、このテンプレートでは:

```html
<h1 :title="userProvidedString">
hello
</h1>
```

`userProvidedString` が含まれている場合:

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

これは次のような HTML にエスケープされます:

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

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

## 潜在的な危険性

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

例えば、CodePen や JSFiddle といったサービスでは、ユーザが提供したコンテンツを実行することができますが、それは iframe の中である程度サンドボックス化されていて、想定されたコンテキストの中でのことです。もし重要な機能が本質的にいくらかの脆弱性を必要とする場合、その機能の重要性とその脆弱性が起こす可能性のある最悪のシナリオを比較検討するのは、あなたのチームの責任です。

### HTML の注入

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

- テンプレートを利用:

```html
<div v-html="userProvidedHtml"></div>
```

- Render 関数を利用:

```js
h('div', {
innerHTML: this.userProvidedHtml
})
```

- JSX と Render 関数を利用:

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

:::tip
ユーザが提供した HTML は、サンドボックス化された iframe や、その HTML を書いたユーザだけがアクセスできるアプリケーションの一部に置かない限り、100% 安全とは言えないことに注意してください。加えて、ユーザが独自の Vue テンプレートを書けるようにしても同様の危険があります。
:::

### URL の注入

このような URL で:

```html
<a :href="userProvidedUrl">
click me
</a>
```

`javascript:` を使った JavaScript の実行を防ぐ URL の「サニタイズ」がされていないと、これにはセキュリティの問題を抱える可能性があります。[sanitize-url](https://www.npmjs.com/package/@braintree/sanitize-url) のようなライブラリがこのような問題の助けになりますが、注意が必要です:

:::tip
あなたがフロントエンドで URL のサニタイズをしているのであれば、すでにセキュリティの問題があります。ユーザが提供した URL は、データベースに保存する前に、常にバックエンドでサニタイズされるべきです。そうすれば、ネイティブ・モバイルアプリケーションを含む API に接続する _すべての_ クライアントに対して、この問題を回避することができます。またサニタイズされた URL でも、Vue はそれが安全なリンク先につながることを保証できないことに注意してください。
:::

### スタイルの注入

この例を見てください:

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

`sanitizedUrl` がサニタイズされていて、JavaScript ではない本物の URL だということが明確だとします。`userProvidedStyles` を利用して、悪意のあるユーザは「クリックジャック」のための CSS をまだ与えることができます。例えば、リンクを「ログイン」ボタンの上に透明なボックスで追加スタイルできます。それから `https://user-controlled-website.com/` があなたのアプリケーションのログインページに似せて作られていた場合、悪意のあるユーザはユーザの本当のログイン情報を取得してしまうかもしれません。

`<style>` 要素にユーザが提供したコンテンツを許可すると、そのユーザがページ全体のスタイルをすべて制御できるようになるので、さらに大きな脆弱性を生み出すことが想像できるでしょう。それが Vue が以下のようなテンプレート内で style タグのレンダリングを防止する理由です:

```html
<style>{{ userProvidedStyles }}</style>
```

クリックジャックからユーザを完全に守るために、サンドボックス化された iframe 内でのみ CSS の完全な制御を許可することをおすすめします。 あるいは、スタイル・バインディングでユーザの制御をするなら、[オブジェクト構文](class-and-style.html#object-syntax-2) を使って、このようにユーザが制御しても安全な特定のプロパティにのみ値を提供することをおすすめします:

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

### JavaScript の注入

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

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

:::tip
ユーザが提供した JavaScript は、サンドボックス化された iframe や、その JavaScript を書いたユーザだけがアクセスできるアプリケーションの一部出ない限り、100% 安全とは言えないことに注意してください。
:::

ときどき Vue のテンプレートでどのようにクロスサイトスクリプティング(XSS)ができるかという脆弱性の報告を受け取ることがあります。一般的に、XSS を許してしまう 2 つのシナリオから開発者を守る実用的な方法がないため、このようなケースを実際の脆弱性とは考えていません:

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

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

## ベストプラクティス

一般的なルールとして、ユーザが提供したサニタイズされていないコンテンツ(HTML、JavaScript、または CSS として)の実行を許可すると、攻撃を受ける可能性があります。このアドバイスは Vue もそれ以外のフレームワークも、あるいはフレームワークを利用しなくても実際に当てはまります。

上記の [潜在的な危険性](#潜在的な危険性) に関する推奨事項に加えて、次のリソースをよく理解しておくことをおすすめします:

- [HTML5 セキュリティ・チートシート](https://html5sec.org/)
- [OWASP クロスサイトスクリプティング(XSS)防止・チートシート](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)

次に学んだことを利用して、サードパーティのコンポーネントや DOM のレンダリング内容に影響するものがあれば、潜在的な危険性パターンの依存関係のソースコードも確認します。

## バックエンドの調整

クロスサイトリクエストフォージェリ(CSRF/XSRF)やクロスサイトスクリプティングインクルージョン(XSII)などの HTTP セキュリティの脆弱性は、主にバックエンドで対処されるため、Vue の懸念はありません。しかし、あなたのバックエンドチームとコミュニケーションをとって、例えばフォームの送信時に CSRF トークンを送信するなど、API との最適なやり取りを学んでおくことはよいアイデアです。

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

SSR を使うときには、いくつか追加でセキュリティの問題があるため、脆弱性を回避するために [SSR ドキュメント](ssr/introduction.html) に記載されているベストプラクティスに必ず従ってください。