SRIハッシュ生成

スクリプトやスタイルシートのアドレスを入力すると、integrity属性付きのタグが返ってきます。ブラウザがそれを受け入れるかどうかの確認も付いています。ファイルや貼り付けたコードのハッシュも計算でき、そちらは端末の中だけで処理されます。

公開されているhttps://のアドレスで、5 MBまで。ファイルは当サーバーがダウンロードしてハッシュを計算し、そのあと破棄します。

例: https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js, https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css

または、お使いの端末のファイルのハッシュを計算

まだ公開していないファイルや、手元に置いておきたいファイル向けです。ハッシュはブラウザが計算するので、ここにドロップしたり貼り付けたりした内容が当方に送信されることはありません。

ファイルを選ぶか、このボックス内のどこかにドロップしてください。

自分でホストしていないコードに封印をする

ページがCDNからライブラリを読み込むとき、あなたは見知らぬ他人のサーバーに、訪問者の目の前でコードを実行する権利を渡しています。そのサーバーが侵入されたり、プロジェクトの持ち主が変わったりすると、同じアドレスが別のものを返し始めることがあります。カード情報の窃取、仮想通貨のマイニング、リダイレクトなどです。ページは動き続け、あなたは気づきません。これは何十万ものサイトで使われている実在のライブラリで実際に起きています。

Subresource Integrity(SRI)は、これに対処するブラウザの機能です。確認したファイルの指紋を、SHA-384のようなハッシュ関数で計算してタグに加えます。ブラウザはファイルをダウンロードしてもう一度指紋を計算し、2つが一致したときだけファイルを実行します。1バイトでも変われば指紋は違ってくるので、ファイルはブロックされます。

<script src="https://cdn.example.com/lib@2.4.1/lib.min.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

このページが計算し、確認すること

アドレスを入力すると、当サーバーがファイルを1回ダウンロードします。そのとき、crossorigin付きのタグに対してブラウザがするのと同じようにOriginヘッダーを送ります。ハッシュはgzipやBrotliを展開したあとの本体で計算します。これはブラウザがハッシュを計算するのとまったく同じ対象です。SHA-384の完全なタグに加えて、SHA-256とSHA-512の値も得られます。

ハッシュが正しくても、タグが機能するとは限りません。そこで応答からさらに4つのことを読み取ります:

確認項目重要な理由
Access-Control-Allow-Origin別のオリジンのファイルでは、ブラウザがバイトを比べるためにこの許可が必要です。これがないと、integrity付きのタグは毎回失敗します。ヘッダーがなければエラーです。
アドレスの中のバージョン/3.7.1/や@5.3.3は、ずっと同じファイルを返します。@latest、@5、ブランチ名、?ver=だけのバージョン、バージョンなしは、ファイルが変わりうるということで、変わったときにページが壊れます。警告です。
Cache-Control1年の有効期間やimmutableは、ファイルが固定されていることを示します。1日未満の有効期間は、そのアドレスの中身が変わる前提であることを示します。
Content-TypeスクリプトはJavaScriptとして、スタイルシートはtext/cssとして届く必要があります。ファイルをそのまま表示するサービスはよくtext/plainを送り、ブラウザはそれを拒否します。

いくつかのアドレスは最初から受け付けません:アクセス解析やタグマネージャーの読み込み用スクリプト、決済のスクリプト、ブラウザごとに作られるフォントのスタイルシートです。これらは持ち主がその場で書き換えるので、ハッシュを付けるのは障害の予約をするようなものです。

2つ目の欄では、ドロップしたファイルや貼り付けたコードのハッシュを、ブラウザ自身のcrypto.subtleで計算します。何もアップロードされません。

ページを壊さずにタグを使う

  1. まずバージョンを固定する。library@latestをテストした正確なリリースに変えてから、そのアドレスでハッシュを生成します。
  2. crossorigin="anonymous"を残す。これがないと、ブラウザは中身を調べる許可のないリクエストを送り、ファイルをブロックします。
  3. ページを読み込んでコンソールを開く。不一致があれば、integrityチェックの失敗としてファイル名付きでそこに表示されます。
  4. アップグレードは意図して行う。ライブラリの新しいバージョンには、新しいアドレスと新しいハッシュが必要です。少し手間ですが、それこそが狙いです:あなたが決めるまで何も変わりません。

自分のドメインにあるファイルでは、すでにサーバーを支配している攻撃者に対してSRIはあまり役に立ちません。ページとハッシュを一緒に書き換えられるからです。それでも、ページとファイルが別々のシステムから配信される場合、たとえばHTMLは自分のサーバーから、ファイルは自分のCDNのバケットから配信する場合には役に立ちます。

限界:1つの場所からの1回のリクエストです。サーバーが別のブラウザや地域には別のバイトを送ることもあり得ます。また、ファイルが安全かどうかは確認せず、同じかどうかだけを確認します。integrity属性が守るのは、それが付いたファイルだけで、そのファイルがあとから読み込むスクリプトは対象外です。

よくある質問

sha256、sha384、sha512のどのハッシュを使うべきですか?

ブラウザは3つとも受け付け、弱いと知られているものはありません。慣例はSHA-384です。CDNが公開しているのも、ほとんどのドキュメントに載っているのもこれなので、ポリシーで別の指定がなければこれを使ってください。

integrityを加えたらスクリプトが読み込まれなくなったのはなぜですか?

よくある原因は3つです。サーバーがAccess-Control-Allow-Originを送っていないか、タグにcrossorigin="anonymous"がない。アドレスがバージョンに結びついていないため、ハッシュを取ってからファイルが変わった。あるいは、スクリプトを圧縮するプロキシのように、サーバーとブラウザの間にある何かがファイルを書き換えている。どれが原因かはブラウザのコンソールでわかります。

Google Fonts、Google Analytics、StripeでSRIを使えますか?

いいえ。これらのアドレスは、持ち主が好きなときに更新する内容を返します。フォントのスタイルシートはブラウザごとに違います。固定のハッシュは、次の変更のときにそれらをブロックしてしまいます。代わりにContent-Security-Policyで制限してください。

サーバーがファイルを圧縮すると、ハッシュは変わりますか?

いいえ。ブラウザは展開したあとのファイルを確認するので、gzip、Brotli、zstdは関係ありません。ファイルを圧縮(minify)したり整形し直したりすると変わります。

画像やフォントはintegrity属性に対応していますか?

今のところ対応していません。ブラウザがこれを適用するのは、<script>と、スタイルシート、プリロード、モジュールのプリロード用の<link>です。ほかの要素では無視されます。

コマンドラインで同じハッシュを計算するにはどうすればよいですか?

OpenSSLならopenssl dgst -sha384 -binary file.js | openssl base64 -Aを実行し、出力の前にsha384-を付けます。