• 診断項目 No.95/127
  • 巡回範囲・サプライチェーン
  • 重大度 中(medium)

外部資産のバージョン固定とは?CDNのURLの書き方・SRIとの関係・診断基準

外部資産のバージョン固定とは、CDN等から読み込むJavaScript・CSSを「@3.7.1」のような完全なバージョンのURLで指定することです。latest・範囲指定の危険、SRIとの組み合わせ、修正例と診断基準を解説します。

結論(外部資産のバージョン固定)

外部資産のバージョン固定とは、CDNなど外部オリジンから読み込むJavaScriptやCSSを、jquery@3.7.1 のような完全なバージョンを含むURLで指定し、配信元の更新で中身が変わらないようにすることです。latest指定・バージョン省略・@3のような範囲指定では、配信元の更新や乗っ取りでページ上のコードが勝手に変わります。OWASP ASVS 5.0.0 V3.6.1は、外部資産を静的・バージョン付きにしSRIで検証することを求めています。

この記事のポイント

  • バージョン省略・@latest・@3や@^3.0.0のような範囲指定・@mainのようなブランチ指定は、中身が変わりうる未固定URLです。
  • SRIはファイル内容のハッシュを照合するため、未固定URLとは両立せず、バージョン固定がSRIの前提条件になります。
  • 2024年6月に公表されたpolyfill.ioの事例のように、配信元の信頼は永続しません。
  • 最善は完全なバージョン+integrity+crossorigin="anonymous"、または自サーバー配置です。
  • セキュリティAIチェッカーは、未固定URLかオリジン変更リダイレクトが1件でもあれば△、無ければ○、外部資産が無ければ対象外と判定します。
診断項目名外部資産のバージョン固定
項目IDsupply.unpinned-resource
分類巡回範囲・サプライチェーン
重大度・配点の重み中(medium) 重み 2
関連する標準・ガイドラインOWASP ASVS 5.0.0 V3.6.1(Level 3:外部ホストの資産は静的・バージョン付きでSRIを使う)/OWASP Third Party JavaScript Management Cheat Sheet/CWE-829(Inclusion of Functionality from Untrusted Control Sphere)/CWE-494(Download of Code Without Integrity Check)/関連項目 html.sri・html.style-sri

外部資産のバージョン固定とは

外部資産のバージョン固定とは、CDNなど外部オリジンから読み込むJavaScriptやCSSを、jquery@3.7.1 のような完全なバージョンを含むURLで指定し、配信元の更新によって中身が変わらないようにすることです。

固定   : https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js
未固定 : https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js   (バージョン省略=最新)
未固定 : https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js (メジャー範囲)

Webページが外部のCDN等から読み込むJavaScriptやCSSは、そのページの一部として実行・適用されます。読み込むURLが「最新版」や「メジャーバージョンの範囲」を指していると、配信元で内容が更新されたときに、自治体側が何もしなくてもページで動くコードが変わります。更新が不具合を含めば表示崩れに、配信元が乗っ取られれば悪意あるコードの実行につながります。

OWASP ASVS 5.0.0 の要件3.6.1は「JavaScriptライブラリ、CSS、Webフォントなどのクライアント側資産を外部でホストするのは、資産が静的でバージョン付けされ、SRI(Subresource Integrity)で完全性を検証している場合に限る」と定めています。SRIはファイル内容のハッシュを照合する仕組みなので、内容が変わりうるURL(未固定URL)とは両立しません。バージョン固定はSRIの前提条件でもあります。

セキュリティAIチェッカーのこの項目は、トップページで読み込まれている外部オリジンのJavaScript・CSSのURLを調べ、latest 指定・バージョン省略・バージョン範囲指定などの「中身が変わりうるURL」と、別オリジンへ転送されるURLを検出します。integrity 属性の有無そのものは「外部JavaScriptのSRI」と「外部CSSのSRI」で評価します。

なぜ外部資産のバージョンを固定する必要があるのか

未固定URLの典型(jsDelivr・unpkg)

主要な公開CDNでは、URLの書き方によって配信内容が固定されるかどうかが変わります。jsDelivrの公式ドキュメントは、バージョンを省略するか latest を使うと最新版が読み込まれ「本番利用には推奨しない」としています。また jquery@3 や jquery@3.1 のようなバージョン範囲も使え、範囲指定や latest のキャッシュは7日、ブランチ指定は12時間、完全なバージョンやコミットハッシュのキャッシュは1年と説明しています。範囲指定は、新しいバージョンの公開とともに中身が変わることを意味します。

URLの例固定されているか
https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js固定(完全なバージョン)
https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js未固定(バージョン省略=最新)
https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js未固定
https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js未固定(メジャー範囲)
https://cdn.jsdelivr.net/npm/@popperjs/core/dist/umd/popper.min.js未固定(スコープ付きパッケージのバージョン省略)
https://cdn.jsdelivr.net/gh/user/repo@main/app.js未固定(ブランチ指定)
https://unpkg.com/react@18/umd/react.production.min.js未固定(メジャー範囲)

常に最新版を配信する外部サービス

一方、Google Analyticsのタグ、地図API、翻訳ウィジェットなど、提供者が常に最新版を配信する設計のサービスもあります。これらはSRIを付けられず、多くは完全なバージョン固定もできないため(地図APIのように版を選べるものもありますが、古い版は提供が終了していきます)、提供者の信頼性評価、CSPによる読み込み先の制限(「Content Security Policy」)、利用目的の見直しで管理します。

オリジンをまたぐリダイレクト

外部資産のURLが別のオリジンへリダイレクトされる場合、実際のコードはHTMLに書かれていない配信元から来ます。配信元のドメイン移管や乗っ取りがあると、ページ側の記述を変えないまま読み込み先が変わります。HTTPSからHTTPへの転送は、通信経路での改ざんを許します。

SRIとの関係(固定バージョン+SRI)

SRIは integrity 属性に書いたハッシュと取得内容を照合し、一致しなければ読み込みを拒否します(MDN)。外部オリジンの資産には crossorigin="anonymous" 属性も必要です。未固定URLにSRIを付けると、配信元の更新時にハッシュが一致せず、機能が突然停止します。そのため「固定バージョン+SRI」を組み合わせます。

外部資産についてよくある誤解

  • 「有名なCDNなら安全」:2024年6月に公表されたpolyfill.ioの事例では、ドメインの所有者が変わった後、同ドメインから配信されるスクリプトに悪意あるリダイレクトコードが混入し、10万を超えるサイトが影響を受けたと報じられています。配信元の信頼は永続しません。
  • 「バージョンを固定すれば更新しなくてよい」:固定は「意図しない変更を防ぐ」ためのものです。脆弱性が修正された版へは計画的に更新します。

外部資産のバージョン固定の診断基準(セキュリティAIチェッカー)

取得対象

  1. トップページのHTMLから、<script src> と rel に stylesheet を含む <link href> のURLを抽出します。
  2. 相対URLを絶対URLに変換し、トップページと異なるオリジンのものを重複除去して最大8件を対象にします。
  3. そのうち先頭5件に、HEADリクエストを1回だけ送ります(リダイレクトは追跡しない、タイムアウト4秒)。Location ヘッダーがあれば転送先を調べます。

未固定URLの判定

次のいずれかに一致するURLを「未固定」とします。

  • @latest、URL中の /latest/(末尾の /latest を含む)、クエリの v=latest/version=latest(配信元を問わず判定)
  • jsDelivrの /npm/ またはunpkgで、パッケージ名(@scope/name を含む)の直後にバージョン指定が無い
  • jsDelivrの /gh/ユーザー/リポジトリ の直後にバージョン指定が無い
  • jsDelivr(npm・gh)またはunpkgで、@3・@3.7 のようなメジャー/マイナーまでの指定、@^3.0.0・@~3.7.0 のような範囲指定。指定の直後が /・?・#・URL末尾のいずれでも未固定とします(https://unpkg.com/react@18?module は未固定、https://unpkg.com/react@18.2.0?module は固定)
  • jsDelivrの /gh/ で @main/@master のブランチ指定

転送(リダイレクト)の判定

HEADの応答に Location があり、転送先のオリジンが元と異なる、またはHTTPSからHTTPへの転送である場合を「オリジン変更リダイレクト」とします。到達できなかった資産は、本項目では問題として数えません。

判定条件

判定条件得点への影響
対象外(na)外部オリジンのJavaScript/CSSが無い採点から除外
△ 要確認(warn)未固定URLまたはオリジン変更リダイレクトが1件以上重み2×0.45
○ 問題なし(pass)どちらも無い重み2×1.00
  • 根拠欄には「未固定URL n件、オリジン変更リダイレクト m件」を表示します。
  • 悪用が確認されたわけではなく、変更管理上の弱点を示すものとして要確認にとどめます。
  • 重大度はmedium、重みは2です。

自治体サイトでの判定例

状況判定
CMSが自サーバーからjQueryを配信対象外(外部資産なし)
https://code.jquery.com/jquery-3.7.1.min.js と https://cdnjs.cloudflare.com/ajax/libs/... の固定版のみ○ 問題なし
防災マップのページで https://unpkg.com/leaflet/dist/leaflet.js を読み込む△ 要確認(バージョン省略)
https://unpkg.com/react@18?module を読み込む△ 要確認(メジャー範囲。クエリ文字列が続いても未固定)
外部アクセシビリティツールのスクリプトURLが別ドメインへ302転送される△ 要確認

判定の限界

トップページ以外のページ、JavaScriptが動的に追加する資産、@import で読み込まれるCSS、Webフォントは対象外です。主要CDN以外の独自配信元については、URLから固定の有無を判断できないため、latest を含む場合のみ検出します。

外部資産のバージョンが固定されていないとどんなリスクがあるか

  • 配信元の侵害・譲渡による改ざん:polyfill.ioの事例のように、外部配信元が第三者の手に渡ると、読み込んでいる全サイトで任意のコードが実行されます(CWE-829)。自治体サイトでは、利用者を詐欺サイトへ転送する、偽の申請フォームを表示する、入力情報を盗むなどの被害が想定されます。
  • 予期しない更新による障害:メジャーバージョン範囲や最新版の指定では、互換性のない更新が配信された時点で、施設予約や防災マップなどの機能が停止します。原因が自治体側の変更ではないため、切り分けに時間がかかります。
  • SRIの適用不能:中身が変わるURLにはSRIを付けられず、改ざん検知の手段がありません(CWE-494)。

外部資産のバージョンを固定する方法(HTML・Apache・nginx・Next.js・CMS)

基本方針

  1. 外部資産は完全なバージョン(例 @3.7.1)を指定し、integrity と crossorigin="anonymous" を付ける。
  2. 可能であれば自サーバー(同一オリジン)へ配置し、外部依存をなくす。
  3. 不要になった外部資産は削除する。
  4. CSPの script-src/style-src で読み込み先を許可リストに限定する。
  5. 固定したライブラリの脆弱性情報を定期的に確認し、計画的に更新する。

HTML

<!-- NG: 未固定 -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>

<!-- OK: 完全なバージョン+SRI -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
        integrity="sha384-(配布元が公開する値、または下記で計算した値)"
        crossorigin="anonymous"></script>

ハッシュはライブラリ公式やCDNが公開する値を使うか、取得したファイルから計算します。

curl -s https://unpkg.com/leaflet@1.9.4/dist/leaflet.js | openssl dgst -sha384 -binary | openssl base64 -A

自サーバー配置(Apache / nginx / IIS共通の考え方)

ライブラリを /assets/vendor/leaflet-1.9.4/ のようにバージョン名付きのディレクトリへ置き、長期キャッシュを設定します。

<IfModule mod_headers.c>
  <LocationMatch "^/assets/vendor/">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </LocationMatch>
</IfModule>
location /assets/vendor/ { add_header Cache-Control "public, max-age=31536000, immutable"; }

Apacheの <LocationMatch> は .htaccess では使えないため、サーバー設定(VirtualHost)に記述します。nginxでは、location 内に add_header を書くと server ブロックの add_header(セキュリティヘッダー)が継承されなくなるため、必要なヘッダーを同じ location にも記述します。IISでも同様に、バージョン名付きのディレクトリへ配置して外部依存をなくします。

Next.js

npmでバージョンを固定してインストールし、バンドルに含めます。package-lock.json をリポジトリに含め、npm ci でビルドします。外部 <Script> を使う場合も完全なバージョンのURLを指定します。

CMS

WordPress等のテーマやプラグインが外部CDNを読み込んでいる場合は、設定で自サーバー配信へ切り替えられるか確認します。委託事業者には「外部資産は完全なバージョン指定とSRI」を仕様書に明記します。

設定後の確認方法

curl -s https://www.city.example.lg.jp/ | grep -oiE '<(script|link)[^>]+(src|href)="https?://[^"]+"' | sort -u
curl -sI https://cdn.example.net/lib.js | grep -i '^location'

1行目で外部資産のURLを一覧し、すべて完全なバージョンを含むことを確認します。2行目で Location が返る資産は、転送先のURLへ直接書き換えます。修正後はセキュリティAIチェッカーで再診断し、根拠欄の件数が0件になったことを確認します。

サイトを壊さないための注意点(ガードレール)

  • SRIを付ける前に、HTMLに書いたURLが完全なバージョンであることを確認します。範囲指定のままSRIを付けると、配信元の更新時に読み込みが拒否されます。
  • 自サーバーへ移す際は、ライブラリが追加で読み込む画像・フォント・プラグインの相対パスも一緒に配置します。
  • 最新版を前提とする外部サービス(解析タグ等)は、無理に固定せず、CSPと提供者管理で対処します。

よくある質問(FAQ)

CDNのURLでバージョンを省略するとどうなりますか?

最新版が読み込まれます。jsDelivrの公式ドキュメントは、バージョン省略やlatestの利用を本番では推奨しないとしています。配信元で新しい版が公開されると、自治体側が何もしなくてもページで動くコードが変わります。

@3 のようなメジャーバージョン指定なら固定されていますか?

固定されていません。@3や@3.7はバージョン範囲の指定で、新しいバージョンの公開とともに中身が変わります。@3.7.1のように完全なバージョンを書いて初めて固定になります。

未固定のURLにSRIを付ければ安全ですか?

付けると、配信元の更新時にハッシュが一致せず読み込みが拒否され、機能が突然停止します。SRIは必ず完全なバージョンのURLと組み合わせます。

Google Analyticsや地図APIもバージョン固定が必要ですか?

これらは提供者が内容を更新し続ける設計のため、SRIを付けられず、多くは完全なバージョン固定もできません(地図APIのように版を選べるものもありますが、古い版は提供が終了していきます)。提供者の信頼性評価、CSPによる読み込み先の制限、利用目的の見直しで管理します。

バージョンを固定したら更新しなくてよいですか?

よくありません。固定は意図しない変更を防ぐためのもので、脆弱性が修正された版へは計画的に更新します。固定したライブラリの脆弱性情報を定期的に確認します。

外部資産のバージョン固定の診断では、どのページの外部資産を調べますか?

トップページのHTMLにあるscriptのsrcとstylesheetのlinkのhrefのうち、外部オリジンのものを最大8件調べます。他のページ、JavaScriptが動的に追加する資産、@importのCSS、Webフォントは対象外です。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料