ひと言で言うと
サブリソース完全性(SRI: Subresource Integrity)は、ブラウザがCDNのような外部ソースから取得するファイルが、こっそり改ざんされていないかを検証できるようにするセキュリティ機能です。
これが解決する問題
想像してみてください。あなたはイケてる新しいWebアプリを構築中です。表示を爆速にするため、ReactやVue、あるいはちょっとオシャレなフォントといった共通ライブラリを、コンテンツ配信ネットワーク(CDN)経由で配信しています。これは定番のやり方です。ユーザーは、他のサイトを訪れた際にファイルがブラウザにキャッシュされている可能性が高いか、あるいは物理的に自分に近いサーバーから配信されるため、より高速な体験を得られます。まさにWin-Win、ですよね?
まあ、ほとんどは。しかし、あなたは今、巨大な信頼関係を一つ持ち込んでしまいました。CDNプロバイダが 常に あなたが意図した まさにその ファイルを配信してくれる、と信頼しているのです。もし、そのCDNがハッキングされたら?攻撃者は、親切で役立つ react.min.js を、悪意のあるバージョン、例えば react.min.js-plus-a-crypto-miner-and-password-stealer に置き換えるかもしれません。
突如として、この悪意のあるコードが あなたの Webサイト上で、ユーザーのブラウザから全幅の信頼を得て実行されてしまいます。ログイン情報を引っこ抜いたり、ページを改ざんしたり、あなたの訪問者をボットネットに引きずり込んだりするかもしれません。これは古典的なサプライチェーン攻撃であり、あなた自身のサーバーでは何も悪いことをしていないだけに、非常に恐ろしい事態です。ただ、タイミング悪く間違った相手を信頼してしまっただけなのです。
SRIが登場する前は、これを防ぐためのネイティブなブラウザの仕組みはありませんでした。開発者たちは回避策を使っていましたが、どれも野暮ったいものでした。SRIは、この特定の問題に正面から取り組むためにW3Cによって作成されました。SRIは、ブラウザに「ねえ、このスクリプトを取ってきて。でも実行する前に、僕が期待しているものと寸分たがわないか絶対に確認して。もし1バイトでも違っていたら、即座に破棄して、こっちに知らせてくれ」と伝えるための、シンプルで標準化された方法を提供します。
舞台裏の仕組み
SRIは、シンプルなHTML属性と、本格的な暗号理論との見事な融合です。分解して見ていきましょう。
integrity 属性
魔法は、<script> タグや <link> タグに追加できる新しい属性から始まります。その名も integrity です。
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3"
crossorigin="anonymous"></script>
この属性には、ハッシュアルゴリズムの接頭辞(ここでは sha384-)と、Base64でエンコードされた暗号学的ハッシュの2つの部分を含む文字列が保持されています。これが、あなたが受け取ることを期待しているファイルの「デジタル指紋」です。
ハッシュ:デジタル指紋
暗号学的ハッシュ関数とは、入力(JavaScriptファイル全体の内容など)を受け取り、ハッシュと呼ばれる短く固定長の文字列を生成する数学的アルゴリズムです。超強力なチェックサムのようなものだと考えてください。
これらのハッシュには、いくつかの重要な特性があります:
- 決定的(Deterministic): 同じ入力ファイルは 常に まったく同じハッシュを生成します。
- アバランシェ効果(Avalanche Effect): 入力ファイル内のたった1文字を変更するだけで(スペースを追加する、変数名を変えるなど)、結果のハッシュはまったく異なる、認識不能なものになります。
- 一方向性(One-way): プロセスを逆にすることは事実上不可能です。ハッシュから元のファイルの内容を割り出すことはできません。
SRI標準は、SHA-256、SHA-384、SHA-512の3つの安全なハッシュアルゴリズムをサポートしています。数字はハッシュのビット長を指し、一般的に大きいほど強力です。SHA-384はオールラウンドな選択肢として優れています。
したがって、CDNファイルにリンクしようとするときは、まずそのハッシュを生成します。あなたは本質的にその瞬間のファイルのスナップショットを撮り、「本物の jquery-3.6.0.min.js はこんな見た目だよ」とブラウザに伝えているのです。
crossorigin 属性
サンプルの crossorigin="anonymous" に気づきましたか?これはただの飾りじゃありません。必須です。ブラウザが異なるオリジンからリソースを取得し(例:あなたのサイト my-app.com が code.jquery.com からスクリプトを取得する)、SRIチェックのためにその内容を検査するには、オリジン間リソース共有(CORS)による許可が必要です。
crossorigin="anonymous" を設定すると、ブラウザはCookieやHTTP認証ヘッダなどのユーザー認証情報を送信せずにリクエストを行うようになります。これはセキュリティとプライバシーの観点から必須です。この属性を忘れると、ブラウザは完全性チェックの実行を拒否し、リソースの読み込みを単純にブロックしてしまうため、サイトが壊れる原因になります。
すべてをまとめる:ブラウザのチェックリスト
ブラウザが integrity 属性を持つタグに遭遇すると、この厳格なプロトコルに従います:
<script>タグを見て、src、integrity、crossorigin属性を記録します。srcURLにあるファイルのリクエストを送信します。crossoriginのおかげで、これはCORSリクエストになります。- ファイルがダウンロードされます。
- ここが重要ですが、 何も実行する前に、ブラウザはダウンロードしたファイルの内容から、
integrity属性で指定されたのと同じアルゴリズム(例:sha384)を使用して独自のハッシュを計算します。 - 次に、新たに計算したハッシュを、あなたが属性で提供したハッシュと比較します。
- 一致した場合: やったね!ファイルは本物です。ブラウザはスクリプトを実行するか、スタイルシートを適用します。
- 一致しなかった場合: レッドアラート!ブラウザはファイルが改ざんされたと判断します。ファイルを完全に破棄し、実行しません。そして、開発者コンソールに
Failed to find a valid digestというエラーを出力します。あなたのサイトは壊れて見えるかもしれませんが(例:グラフやフォントが表示されない)、あなたは見事に銃弾をかわしたというわけです。
実際の事例
改ざんされた分析ダッシュボード
あるマーケティングチームは、キャンペーンデータを視覚化するために、ニッチなCDNから取得したサードパーティのグラフ作成ライブラリを使用したダッシュボードに依存していました。セキュリティのベストプラクティスを学んだばかりの開発者は、そのライブラリの <script> タグにSRIハッシュを追加していました。ある月曜の朝、そのCDNが短時間ながら侵害を受けました。攻撃者は人気のグラフ作成ライブラリを、巨大で嘲笑的なアスキーアートの顔を表示するだけのスクリプトに置き換えました。
マーケティングチームがダッシュボードを読み込むと、グラフは壊れていました。空のボックスが表示されるだけです。彼らはイライラしてIT部門に電話しました。開発者がブラウザコンソールを確認すると、そこには美しく輝くSRI検証エラーが表示されていました。ブラウザは変更されたファイルを検出し、実行を拒否し、改ざんを防いだのです。重大なセキュリティインシデントとパニックに陥る経営陣、という事態の代わりに、それは一時的に別のCDNを指すように変更して終わる15分間の調査で済みました。
教訓: SRIは、潜在的なセキュリティの大惨事を、対処可能な可用性の問題に変えてくれます。
こっそり仕込まれた仮想通貨マイナー
無料CDNでホストされていた、人気で軽量なJavaScriptユーティリティライブラリは、インディー開発者のお気に入りでした。攻撃者はCDNへのアクセス権を取得し、ライブラリファイルを変更して、WebAssemblyの仮想通貨マイナーを起動する数行の難読化されたコードを追加しました。ファイルサイズはほとんど変わらず、ライブラリのコア機能も完璧に動作していました。
SRIなしでこのライブラリを使用していたWebサイトは、突然、ユーザーのラップトップのファンを唸らせ、バッテリーを消耗させ始めました。ユーザーは動作の遅さを訴えましたが、診断は困難でした。サイト自体は正常に見えたからです。しかし、SRIを実装していたサイトは影響を受けませんでした。彼らのブラウザは変更されたスクリプトをブロックし、ユーティリティ関数は壊れましたが、ユーザーのCPUは安全でした。
教訓: SRIは、明らかな改ざんだけでなく、サイトの評判を損なう可能性のある、巧妙で寄生虫のような攻撃もキャッチします。
忘れ去られたフォントの更新
あるデザイナーが、サードパーティのフォントファウンドリから提供される特定のバージョンのフォントを使うことにこだわりました。そのフォントは彼らのCDN経由で配信されていました。開発者は忠実に、SRIハッシュを含む <link> タグをコピーしました。サイトは無事ローンチし、見た目も素晴らしかったです。半年後、そのファウンドリは新しい通貨記号を追加し、カーニングを改善するためにフォントファイルを更新しました。これは正当で、役立つアップデートでした。
突然、サイトのテキストがダサいデフォルトのArialに戻ってしまいました。開発者はコンソールでSRIエラーを見るまで、何が起きたのか分かりませんでした。ブラウザは、ハッシュがHTML内の古いものと一致しなくなったため、新しく変更されたフォントファイルを正しくブロックしていたのです。「攻撃」は単なる良性のアップデートでしたが、SRIはその仕事を果たしました。修正は簡単で、更新されたフォントの新しいハッシュを生成し、変更をデプロイするだけでした。
教訓: SRIは厳格なバージョニングを強制します。悪意のある変更 だけでなく、予期せぬ上流のアップデートからもあなたを守り、使用するアセットについて意図的であることを強制します。
よくある間違いと落とし穴
crossorigin="anonymous"を忘れる。 これはNo.1の間違いです。これがないと、ブラウザはリソースを検査するためのCORS許可を持たないため、セキュリティ上の理由からそれをブロックします。完全性チェックは行われません。あなたのスクリプトやスタイルは単に読み込みに失敗します。- 間違ったものをハッシュ化する。 ブラウザが受け取るファイルの内容そのものをハッシュ化しなければなりません。圧縮済みのCDNバージョンにリンクしている場合に、ローカルの未圧縮版のスクリプトをハッシュ化してはいけません。URL文字列自体をハッシュ化するのも間違いです。ファイルのボディのハッシュが必要です。
- 弱いハッシュアルゴリズムを使う。 MD5とSHA-1には既知の脆弱性があり、セキュリティ目的で使用すべきではありません。仕様では、ブラウザは少なくともSHA-256、SHA-384、SHA-512をサポートすることが要求されています。これらを使いましょう。
- 正規のアップデート後にハッシュを更新しない。 SRIはバグではなく仕様です。リンク先のファイルが何らかの理由で更新された場合は、必ず 新しい完全性ハッシュを生成し、HTMLの
integrity属性を更新しなければなりません。これを怠ると、リソースはブロックされます。 - 自分のサーバーも保護されると勘違いする。 SRIはサードパーティのリソースを検証するために設計されています。もし攻撃者があなたのサーバーを侵害してHTMLファイルを変更できるほどであれば、彼らは単にSRIハッシュを悪意のあるスクリプトに合うように変更するだけです。同一オリジンのリソースに対しては利点はありません。
なぜこれを気にかけるべきか
あなたが管理していないドメインを指す <script src="..."> や <link rel="stylesheet" href="..."> を書くたびに、SRIについて考えるべきです。
これは現代のWebセキュリティの基礎となる要素です。NPM、CDN、そして複雑に絡み合ったサードパーティ依存関係の網の上に構築された世界では、あなたのサプライチェーンは巨大な攻撃対象領域(アタックサーフェス)です。SRIは、そのサーフェスを強化するための最もシンプルで効果的なツールの一つです。侵害されたCDNに対するあなたの第一防衛線です。コンテンツセキュリティポリシー(CSP)と組み合わせることで、堅牢な多層防御を提供します。
リソースを追加する際にSRIを追加するのは数秒の手間ですが、将来的に大きな痛手からあなたを救うことができます。SRIは、静かで危険なエクスプロイトを、やかましくも安全な失敗へと変えてくれるのです。
もっと深く知る
- MDN Web Docs: Subresource Integrity - 開発者向けの決定版リファレンス。
- W3C Recommendation: Subresource Integrity - 公式の技術仕様書。もっともディープに潜りたいなら、これです。
- Can I use... Subresource Integrity - SRIの最新ブラウザ対応状況チャート。
- Wikipedia: Cryptographic hash function - SRIの背後にある「指紋」の魔法をより深く理解するために。
- Scott Helme: Subresource Integrity - セキュリティ専門家による、SRIの『何か、なぜ、どうやって』を解説した素晴らしいブログ記事。