[Chrome 45+][Firefox 43+][Opera 32+] CDNからのXSSを防ぐ

# CDN
外部のURIを使ってライブラリなどのダウンロードを高速化する仕組み。

```html
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
```

見ての通り、CDNサービスがハックされてファイルが改竄されたりするとXSSが起こる。

# Subresource Integrity
ファイルのハッシュをHTMLに書いておいて改竄を検出する仕組み。

```html
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
        integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
        crossorigin="anonymous"></script>
<link rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
      integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
      crossorigin="anonymous">
```

`integrity`属性にはファイルのハッシュとハッシュを作るのに使ったハッシュ関数を書いておく。
`crossorigin`属性はCORS<a href="#1">[1]</a>に関係する属性で、`integrity`属性を使う場合は必須。
<a href="#2">[2]</a>によれば、`crossorigin`属性はCross-origin data leakageという脆弱性が生まれてしまうのを防ぐために必要なのだそうだ（よく分かってない）。
<a href="#3">[3]</a>によれば、`crossorigin="anonymous"`は、Cookieや認証情報のような、ユーザとCDNを利用しているドメインとを関連付けることを可能にする情報を、CDNサービスに送信しないことを意味し、これによってCross-origin data leakageを防ぐことができるという。

ハッシュ関数として何を使うかは、SHA-256、SHA-384、SHA-512の中から選ぶことができる。
例えばSHA-384をハッシュとして使う場合の`integrity`属性の値は以下のコマンドで求めることができる。

```bash
echo sha384-"$(openssl dgst -sha384 -binary bootstrap.min.js | openssl base64 -A)"
```

わざわざローカルにファイルをダウンロードするのが面倒なら以下のようなコマンドも使える。

```bash
# Wget版
echo sha384-"$(wget -nv -O - https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js | openssl dgst -sha384 -binary | openssl base64 -A)"

# cURL版
echo sha384-"$(curl -s -S -f https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js | openssl dgst -sha384 -binary | openssl base64 -A)"
```

コマンドを打つのも面倒なら以下のサイトを使うことができる。
[SRI Hash Generator](https://www.srihash.org/)

# ブラウザの対応状況

Subresource Integrityが有効に働くためにはユーザがSubresource Integrityを実装したブラウザを使っている必要がある。
<a href="#4">[4]</a>によれば、この記事執筆時のブラウザの対応状況は、Chrome 45以上、Firefox 43以上、Opera 32以上。
まだ仕様が策定段階なので<a href="#2">[2]</a>、IEなどは実装していない。

以下のサイトを使うことで、あなたのブラウザがSubresource Integrityをサポートしているかどうかについては確認することができる。
[Subresource Integrity](http://w3c-test.org/subresource-integrity/subresource-integrity.sub.html)

# フォールバック

ファイルが改竄されていた場合、そのファイルはロードされないのでサイトの表示が崩れることになる。
JavaScriptについては以下のようなフォールバック策があるようだ<a href="#5">[5]</a>。

```html
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
        integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
        crossorigin="anonymous"></script>
<script>window.jQuery.fn.modal || document.write('<script src="bootstrap.min.js"><\/script>')</script>
```

CSSについては以下のようなフォールバック策があるようだ<a href="#6">[6]</a>。

```html
<link rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
      integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7"
      crossorigin="anonymous">
<script>
for (var i = 0; i < document.styleSheets.length; i++) {
    var sheet = document.styleSheets[i];
    if (sheet.href == "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css") {
        var rules = sheet.rules ? sheet.rules : sheet.cssRules;
        if (rules.length == 0) {
            var link = document.createElement("link");
            link.setAttribute("rel", "stylesheet");
            link.setAttribute("href", "bootstrap.min.css");
            document.getElementsByTagName("head")[0].appendChild(link);
        }
    }
}
</script>
```

# まとめ

書いておいて損は無いので今からでも`integrity`属性と`crossorigin`属性を書いておきましょう。

# References

* <a name="1">[1]</a>: [Cross-Origin Resource Sharing](http://www.w3.org/TR/cors/)
* <a name="2">[2]</a>: [Subresource Integrity](http://www.w3.org/TR/SRI/)
* <a name="3">[3]</a>: [Do not let your CDN betray you: Use Subresource Integrity ★ Mozilla Hacks – the Web developer blog](https://hacks.mozilla.org/2015/09/subresource-integrity-in-firefox-43/)
* <a name="4">[4]</a>: [Subresource Integrity - Web security | MDN](https://developer.mozilla.org/ja/docs/Web/Security/Subresource_Integrity)
* <a name="5">[5]</a>: [javascript - how to fallback twitter-bootstrap cdn to local copy - Stack Overflow](http://stackoverflow.com/questions/12575203/how-to-fallback-twitter-bootstrap-cdn-to-local-copy)
* <a name="6">[6]</a>: [jquery - How to fallback to local stylesheet (not script) if CDN fails - Stack Overflow](http://stackoverflow.com/questions/7383163/how-to-fallback-to-local-stylesheet-not-script-if-cdn-fails)