結論(外部資産のバージョン固定)
外部資産のバージョン固定とは、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件でもあれば△、無ければ○、外部資産が無ければ対象外と判定します。
| 診断項目名 | 外部資産のバージョン固定 |
|---|---|
| 項目ID | supply.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チェッカー)
取得対象
- トップページのHTMLから、
<script src>とrelにstylesheetを含む<link href>のURLを抽出します。 - 相対URLを絶対URLに変換し、トップページと異なるオリジンのものを重複除去して最大8件を対象にします。
- そのうち先頭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)
基本方針
- 外部資産は完全なバージョン(例
@3.7.1)を指定し、integrityとcrossorigin="anonymous"を付ける。 - 可能であれば自サーバー(同一オリジン)へ配置し、外部依存をなくす。
- 不要になった外部資産は削除する。
- CSPの
script-src/style-srcで読み込み先を許可リストに限定する。 - 固定したライブラリの脆弱性情報を定期的に確認し、計画的に更新する。
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フォントは対象外です。
この項目を含む127項目を、URLを入れるだけで無料診断します。