外部scriptを読むときはSRIで固定するversionと更新を受けるresourceを分ける
公開日: 執筆:佐藤
外部CDNからJavaScriptやCSSを読み込むなら、内容を固定したいresourceにはSRIを使い、更新を受けたいresourceとは分けて管理します。SRIはintegrity属性に記録したhashと、browserが取得したfileの内容を照合する仕組みです。URLが同じでもfileが変われば照合は通りません。導入の目的は外部resourceを何でも安全にすることではなく、読み込む予定だった特定versionのfileだけを使うことです。
web pageへ外部scriptを置くことは、そのscriptがbrowserで動くことを受け入れる行為です。計測tag、map、video player、Web font、libraryを外部domainから読めば、自社serverの外で配信されるfileもpageの動作に関わります。SRIを付ける前に、どのresourceが必要か、誰がURLとversionを決めるか、更新時に何を確認するかを決めます。
W3CのSubresource Integrity勧告は、script要素とstylesheetを読むlink要素へintegrity属性を付け、期待するresourceのhashをbrowserが確認する仕組みを定めています。取得したdataが指定したhashと一致しなければ、browserはそのscriptを実行せず、stylesheetを適用しません。これは改変されたfileをそのまま動かさないための確認であり、最初に選んだversion自体が目的に合うかどうかは別に判断します。
URLが固定でも内容が固定とは限らない
外部resourceのURLが短く、いつも同じように見えても、配信側が中身を差し替えればbrowserが受け取るdataは変わります。https://cdn.example.net/library.min.jsというURLが同じでも、今日読むfileと来月読むfileが同じとは限りません。version番号をURLへ含める場合も、配信側の運用やcacheの設定によって、いつ何が変わるかを自社で確認できるようにします。
SRIでは、読み込むfileのhashをHTMLへ置きます。概念的には次のような記述です。
<script
src="https://cdn.example.net/library-1.2.3.min.js"
integrity="sha384-取得したversionのhash"
crossorigin="anonymous"></script>
この記述で重要なのは、integrityの文字列を一度貼ることではありません。hashは、srcで示したfileの特定の内容に対応します。libraryを1.2.3から1.2.4へ変えるなら、URLだけでなくhashもその新しいfileに合わせて更新します。古いhashのままURLだけを変えれば、browserは一致しないfileとして扱い、scriptを動かしません。
| resourceの扱い | 例 | SRIで決めること | 先に確認すること |
|---|---|---|---|
| versionを固定するlibrary | siteの表示に使うJavaScript | そのversionのhashをHTMLへ置く | libraryが必要か、更新担当は誰か |
| 自社で管理するCSS | CDNへ置いたtheme CSS | 公開するfileとhashを同じreleaseで管理する | cacheを更新して新しいCSSへ切り替わるか |
| 配信側が内容を変えるtag | 計測や広告のscript | 固定することで動作を止めないか | scriptを外部へ任せる理由と変更通知 |
| 利用者ごとに応答が変わるresource | 個別dataを返すAPI | SRIの対象として考えない | 認証、CORS、cache、権限 |
外部resourceを読んでいる理由も分けます。siteの基本表示に必要なlibraryと、広告やaccess analysisのためのtagは、止まったときの影響が違います。前者が一致しなければpageのmenuやformが動かないかもしれません。後者が一致しなければ計測dataが欠けるかもしれません。どちらも無関係なfileを読み込むよりはよいとは限りませんが、更新時に誰が困るかを同じに扱わない方が判断しやすくなります。
SRIで確認できることとできないことを分ける
SRIが確認するのは、browserが受け取ったresourceの内容が、HTMLへ書いたhashと一致することです。指定したhashと一致するfileなら実行または適用されます。したがって、SRIは外部scriptを採用する前のreview、license確認、利用規約確認、個人情報の扱い、利用者の同意を代わりに確認しません。
たとえば、外部libraryに不要な機能が含まれていたとしても、そのfileのhashを自分でHTMLへ登録すればSRIの照合は通ります。SRIは「このfileを選んだ」という判断を検査するのではなく、選んだfileと届いたfileが同じかを検査します。外部serviceのscriptを入れるときは、提供元、読み込むdomain、version、必要な機能、問い合わせformや個人情報を扱うpageへの影響を先に確認します。
また、SRIの照合に失敗したときは、壊れたfileをbrowserが使わないことが利点である一方、pageの機能が止まる可能性もあります。slider用scriptが止まるだけなら写真の見え方に影響するかもしれません。formの入力検査に使うscriptが止まれば、利用者が送信できないかもしれません。SRIを付けるpageでは、照合に失敗した場合でも本文、連絡先、必要なform操作をどう扱うかを確認します。
crossorigin属性も、外部resourceの取得条件に関わります。外部domainのresourceへSRIを使う場合は、HTMLの属性だけを追加して終わりにせず、配信元がその取得を許可するresponseを返すかを実際のbrowserで確認します。scriptがnetwork画面では取得されているように見えても、SRI照合の結果、実行されないことがあります。HTML、response header、browser console、公開画面を別に見ます。
更新を受けるscriptはhashを貼る前に運用を決める
外部serviceのscriptには、提供元がURLを変えずに内容を更新するものがあります。計測tagや広告tagのように、提供元が更新する前提のresourceへ古いhashを固定すれば、提供元がfileを変えた時点で照合が失敗する可能性があります。反対にhashを付けないなら、どの変更を受けるかを提供元へ委ねることになります。どちらが正しいかは、resourceの目的と更新を確認できる体制で決めます。
自社のpage表示に必要なlibraryをCDNから読むなら、versionを固定し、更新のたびにrelease note、file、hash、画面を確認する方法が取りやすいでしょう。外部serviceが管理するtagを使うなら、SRIを付けない理由、更新通知を受ける方法、停止したときに誰が確認するかを残します。securityを理由にhashを付けたのに、更新時の責任者がいなければ、いずれpageが止まるか、誰かが内容を確認せずhashを貼り替えることになります。
外部の制作者へthemeを頼む場合も、HTMLへ置いたhashだけを納品物として受け取らない方がよいでしょう。外部resourceの一覧、URL、version、用途、SRIの有無、更新方法、設定を変更できるrepositoryまたはserver accountを引き継ぎます。制作を委託したことと、外部resourceを自社が把握していることは別です。
実際のbrowserで一致と画面を確認する
SRIを入れたあと、source codeにintegrityがあることだけでは検証になりません。対象pageを実際に開き、browserがscriptまたはstylesheetを読み込めているか、pageが使えるかを見ます。更新する場合は、URLとhashを一緒に変え、cacheが残った状態と新しく取得する状態の両方を確認します。
確認の順序は、次のようにすると整理しやすくなります。
- pageが読む外部scriptとstylesheetを一覧にし、URL、用途、提供元、version、担当者を書く。
- 内容を固定するresourceと、提供元の更新を受けるresourceを分け、SRIを付ける理由を決める。
- 固定するfileは、確認したversionからhashを作り、
srcまたはhrefとintegrityを対応させる。 - test environmentと公開pageで、browser console、network、本文、menu、formを確認する。
- 更新日、変更したURLとhash、確認した画面、戻すreleaseを記録する。
最初の一手は、siteが外部から読むJavaScriptを一つ選び、そのfileを固定してよいのか、提供元の更新を受ける必要があるのかを書き分けることです。SRIは外部resourceを増やす理由ではありません。必要なfileの内容と更新責任を明らかにするための仕組みです。
関連記事

