SRI ハッシュを生成する方法
- SRI モードが選択されています。ハッシュ: で sha256、sha384、sha512 のいずれか、または複数にチェックを入れます。デフォルトでは sha384 にチェックが入っています。
- 入力 を ファイル にして
.jsや.cssファイルをドロップします。タグの種類はファイルの拡張子で決まります。 - または、入力 を テキスト にして、ファイルの中身を ファイルの内容(貼り付け) に貼り付け、タグ(
<script>または<link rel="stylesheet">)を選び、ファイルを配信する パス または URL を入力します。 integrityの値、または HTML タグ 全体をコピーします。複数のアルゴリズムにチェックを入れた場合は、1 つの属性の中にスペース区切りで並びます。
貼り付けるよりファイルをドロップするほうが確実です。ブラウザのテキストボックスは CRLF 改行を LF に変換しますし、最後の改行の有無でもハッシュが変わります。ツールはどちらについても注意を表示します。
integrity の値とは
SRI の値は、アルゴリズム名、ハイフン、そしてファイルの正確なバイト列の Base64 ダイジェストで構成されます。console.log('hello'); の 1 行と改行だけを含む app.js の場合は次のとおりです。
<script src="https://cdn.example.com/app.js"
integrity="sha384-eEqgsNuDqSzbarn2kIY7c4WH6zkoJrjJRQIhlYgVwRR+6Eb8tkfTItTNLlIjbE3O"
crossorigin="anonymous"></script>
最後の改行がなければ、同じ行のハッシュは sha384-v393mDht/MNBowq0Z9UpetDvKE6u6EdCihklP1GZs66vL1YCFm1Z4Q4wJtb94rY9 になります。ブラウザはファイルをダウンロードし、受け取った本文のハッシュを計算します(gzip や Brotli の転送圧縮は解除した後なので、圧縮の有無は関係ありません)。一致するハッシュが一つもなければ、実行も適用も拒否します。
SRI が役立つ場面
SRI が守ってくれるのは、自分のコードを他者が配信している場合です。公開 CDN、サードパーティのウィジェット配信元、他チームと共有するストレージバケットなどがそうです。そのサーバーが侵害されてファイルが差し替えられても、ブラウザは改ざんされたファイルをページで実行せずにブロックします。
library@1.2.3/dist/lib.min.js のように、同じ URL で内容が変わらないファイルに向いています。次のものには向きません。
/latest/や@1のように、新しいリリースに切り替わる URL。- ブラウザや地域によって内容が変わるレスポンス。たとえば、ユーザーエージェントに応じて異なる
@font-faceルールを返す Web フォントの CSS などです。 - ベンダーが意図的にその場で更新するスクリプト。多くのアナリティクスやチャットのローダーがこれに当たります。この場合は SRI とベンダーの更新のどちらかを選ぶしかありません。
自分のドメインで配信する自分のファイルについては、SRI の効果はあまりありません。ファイルを書き換えられる攻撃者は、たいてい HTML も書き換えられるからです。そこでは Content Security Policy のほうが適しています。その方面は CSP nonce 生成ツール と、SHA-256 を Base64 に で作る CSP ハッシュで対応できます。
CORS と crossorigin 属性
integrity 属性付きのクロスオリジンのファイルは CORS モードで取得する必要があり、サーバーは Access-Control-Allow-Origin(公開 CDN では通常 *)でそれを許可しなければなりません。それを要求するのが crossorigin="anonymous" で、生成されるタグには必ず含まれます。CORS がないとブラウザはハッシュ計算のためにバイト列を読めず、ファイルをブロックします。コンソールには integrity または CORS の失敗を示すメッセージが表示されます。
同一オリジンのファイルにはこの属性は不要ですが、付けても害はありません。
ビルド時に SRI ハッシュを生成する
ファイルが多い場合は、ビルド工程でハッシュを計算してください。
openssl dgst -sha384 -binary app.js | openssl base64 -A
const crypto = require("crypto");
const fs = require("fs");
const integrity = "sha384-" +
crypto.createHash("sha384").update(fs.readFileSync("app.js")).digest("base64");
console.log(integrity);
どちらも、例のファイルに対して上記の eEqgsNuD… の値を出力します。バンドラーには HTML に属性を書き込んでくれるプラグインがあることが多く、ビルドのたびにハッシュを最新に保てます。受け取った値を確認するには、ファイルをここにドロップして比較してください。比較は 1 文字単位で厳密に行われます。
よくある質問
SRI にはどのアルゴリズムを使うべきですか?
通常は SHA-384 で、CDN が公開しているのもたいていこれです。ブラウザは sha256、sha384、sha512 を受け付け、複数が並んでいる場合はその中で最も強いものを使い、弱いものは無視します。
integrity を付けたらブラウザがスクリプトをブロックするのはなぜですか?
バイト列がハッシュを計算したときと異なるか、ファイルが CORS なしで別オリジンから配信されているかのどちらかです。コンソールのメッセージを確認し、サーバーが実際に配信するファイルと比較してください。また、レスポンスに Access-Control-Allow-Origin があり、タグに crossorigin="anonymous" が付いているかも確かめてください。
「latest」URL のように内容が変わるファイルに SRI を使えますか?
使えません。ハッシュは特定のバージョン 1 つに固定されるので、更新があるとハッシュを書き換えるまでページが壊れます。バージョン付きの URL を参照するか、変更を展開する間は古いハッシュと新しいハッシュを並べて記載してください。
コマンドラインで SRI ハッシュを生成するには?
openssl dgst -sha384 -binary app.js | openssl base64 -A を実行し、結果の前に sha384- を付けます。-binary は外さないでください。これがないと16進数のテキストをエンコードしてしまいます。