2026年6月16日
2026年6月16日
WordPressでSubresource Integrity(SRI)を設定する方法|CDNのJS・CSSにintegrity属性を付ける
WordPressでSubresource Integrity(SRI)を使うには、外部CDNから読み込むJavaScriptやCSSのハッシュ値を計算し、script_loader_tag / style_loader_tag フィルターで integrity 属性と crossorigin 属性を付け加えます。WordPress本体にはSRIを設定する画面がないため(2026年10月現在)、テーマかプラグインのコードで対応します。この記事では、ハッシュの作り方、WordPressでの実装、読み込みが止まったときの切り分けまでを順に解説します。
SRIで防げること・防げないこと
SRIは、ブラウザが外部から取得したファイルの中身を、HTMLに書かれたハッシュ値と照合する仕組みです。中身が1バイトでも違えばハッシュが一致しなくなり、ブラウザはそのファイルを実行(適用)しません。
想定している脅威は「読み込み先のCDNやライブラリ配信元が改ざんされる」ケースです。自分のサーバーが安全でも、<script src="https://cdn.example.com/..."> で読み込んだファイルが書き換えられれば、訪問者のブラウザで攻撃者のコードが動きます。SRIはこの経路をふさぎます。
一方で、次のことはSRIでは防げません。
- 自分のサイトそのものが改ざんされた場合:攻撃者がHTMLを書き換えられるなら、ハッシュ値も一緒に書き換えられます。
- 読み込んだスクリプトが後から別のスクリプトを動的に読み込む場合:最初のファイルは検証されますが、そのファイルが追加で読み込むファイルにはSRIがかかりません。
- 頻繁に中身が変わるファイル:解析タグや広告タグのように配信元が内容を随時更新するものは、ハッシュを固定できないのでSRIに向きません。
このため、SRIの対象は「バージョンを固定したURLで配信されているライブラリ」が中心になります。自サイト内のファイルに付けても防御上の効果は小さく、更新のたびにハッシュがずれて表示が崩れる原因になるので、基本は外部CDNのファイルだけに絞るのがおすすめです。
手順1:読み込んでいる外部ファイルを洗い出す
まず、サイトがどの外部ファイルを読み込んでいるかを確認します。ブラウザの開発者ツールを開き、「ネットワーク」タブでページを再読み込みすると、読み込まれたファイルの一覧が出ます。ドメインが自サイト以外になっている JS・CSS が候補です。
次に、それぞれのファイルが**誰の wp_enqueue_script() / wp_enqueue_style() で登録されたものか**を調べます。WordPressではハンドル名(登録時の名前)単位で属性を追加するため、ハンドル名が分からないと設定できません。ページのソースを見ると、CSSの <link> タグには id="ハンドル名-css"、JSの <script> タグには id="ハンドル名-js" が付いていることが多く、ここからハンドル名を確認できます。
プラグインが読み込んでいるファイルにSRIを付けると、プラグイン更新でURL(バージョン)が変わったときにハッシュが合わなくなり、そのファイルが読み込まれなくなります。最初は自分のテーマで読み込んでいるライブラリだけを対象にするのが安全です。
手順2:ハッシュ値を計算する
SRIのハッシュは、ファイルの中身をSHA-256・SHA-384・SHA-512のいずれかでハッシュ化し、その結果をBase64にした値です。一般的には sha384 が使われます。integrity 属性には sha384- の後ろにBase64の値をつなげて書きます。
サーバーやローカルPCに curl と openssl があれば、次のコマンドで計算できます。
# CDN上のファイルを取得し、SHA-384 のハッシュを Base64 で出力する
curl -sL https://cdn.example.com/libs/example-lib/2.1.0/example-lib.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -A
出力された文字列の先頭に sha384- を付けたものが integrity 属性の値です。ここでよくある間違いが2つあります。
- **
-binaryを付け忘れる**:付けないと16進数の文字列がBase64化されてしまい、正しい値になりません。 - ブラウザが受け取るファイルと別のものを計算する:手元にダウンロードしたファイルと実際に配信されるファイルが同じとは限りません。計算は配信URLそのものに対して行うと食い違いが起きにくくなります。
ライブラリの公式サイトやCDNによっては、integrity の値をあらかじめ掲載していることがあります。その値を使う場合も、一度は上のコマンドで自分で計算して一致を確かめておくと安心です。
手順3:WordPressで integrity 属性を付ける
WordPressの wp_enqueue_script() には integrity を直接指定する引数がありません。そこで、タグが出力される直前に通る script_loader_tag(JS用)と style_loader_tag(CSS用)のフィルターで属性を差し込みます。
子テーマの functions.php か自作プラグインに次のように書きます。URLとハッシュは例なので、手順2で計算した値に置き換えてください。編集前に functions.php のバックアップを取っておきます。
<?php
/**
* SRI を付けるハンドルとハッシュの対応表
*/
function mysite_sri_hashes() {
return array(
'script' => array(
'example-lib' => 'sha384-REPLACE_WITH_YOUR_HASH',
),
'style' => array(
'example-lib-css' => 'sha384-REPLACE_WITH_YOUR_HASH',
),
);
}
/**
* 外部ライブラリを読み込む(バージョン固定の URL、ver は null で付けない)
*/
function mysite_enqueue_cdn_assets() {
wp_enqueue_style(
'example-lib-css',
'https://cdn.example.com/libs/example-lib/2.1.0/example-lib.min.css',
array(),
null
);
wp_enqueue_script(
'example-lib',
'https://cdn.example.com/libs/example-lib/2.1.0/example-lib.min.js',
array(),
null,
array( 'in_footer' => true )
);
}
add_action( 'wp_enqueue_scripts', 'mysite_enqueue_cdn_assets' );
/**
* <script> に integrity と crossorigin を付ける
*/
function mysite_add_sri_to_script( $tag, $handle, $src ) {
$hashes = mysite_sri_hashes();
if ( empty( $hashes['script'][ $handle ] ) ) {
return $tag;
}
$attr = sprintf( ' integrity="%s" crossorigin="anonymous"', esc_attr( $hashes['script'][ $handle ] ) );
// src を持つタグだけに付ける(前後に出力されるインラインスクリプトには付けない)
return str_replace( ' src=', $attr . ' src=', $tag );
}
add_filter( 'script_loader_tag', 'mysite_add_sri_to_script', 10, 3 );
/**
* <link rel="stylesheet"> に integrity と crossorigin を付ける
*/
function mysite_add_sri_to_style( $html, $handle, $href, $media ) {
$hashes = mysite_sri_hashes();
if ( empty( $hashes['style'][ $handle ] ) ) {
return $html;
}
$attr = sprintf( ' integrity="%s" crossorigin="anonymous"', esc_attr( $hashes['style'][ $handle ] ) );
return str_replace( ' href=', $attr . ' href=', $html );
}
add_filter( 'style_loader_tag', 'mysite_add_sri_to_style', 10, 4 );
コードのポイント
- **バージョン番号を
nullにしている理由**:WordPressは通常、URLの末尾に?ver=6.xのようなクエリを付けます。多くのCDNはクエリを無視して同じファイルを返しますが、クエリによって別の応答を返す配信元もあります。外部ファイルはURL自体でバージョンを固定し、クエリを付けないほうが確実性が上がります。 - **
str_replace()でsrc=を置換している理由**:script_loader_tagに渡される$tagには、wp_add_inline_script()で追加されたインラインスクリプトや翻訳用スクリプトが含まれることがあります。<scriptを単純に置換するとそれらにも属性が付いてしまうため、srcを持つタグだけを狙っています。 - **
crossorigin="anonymous"が必要な理由**:別ドメインのファイルの中身をブラウザが検証するには、CORSで取得する必要があります。この属性がないと、ハッシュが正しくても読み込みがブロックされます。また、配信元がAccess-Control-Allow-Originヘッダーを返していることも条件です。主要な公開CDNは対応していますが、自前のCDNや外部ストレージから配信している場合はヘッダー設定を確認してください。
手順4:動作を確認する
設定したらページキャッシュを削除し、ブラウザで表示を確認します。
- ページのソースを表示し、対象の
<script>や<link>にintegrityとcrossoriginが付いているかを確認します。 - 開発者ツールの「コンソール」タブにエラーが出ていないかを見ます。ハッシュが合わない場合、Chrome では「integrity 属性に有効なダイジェストが見つからず、リソースがブロックされた」という趣旨のエラーが表示されます。
- 「ネットワーク」タブで対象ファイルが正常に読み込まれていること、ライブラリを使っている機能(スライダーやフォームなど)が動くことを確認します。
あえてハッシュを1文字変えて読み込みが止まることを確かめると、SRIが実際に効いていることを確認できます。確認が終わったら元の値に戻すのを忘れないでください。
読み込みが止まったときの切り分け
SRIを入れた後にライブラリが動かなくなったら、次の順で原因を探します。
- コンソールにダイジェスト不一致のエラーが出ている:ハッシュの計算ミスか、配信されるファイルが変わっています。URLがバージョン固定になっているか(
latestのような可変URLになっていないか)を確認し、ハッシュを計算し直します。 - CORS関連のエラーが出ている:
crossorigin属性が付いていない、または配信元がAccess-Control-Allow-Originを返していません。 - 自サイトのファイルに付けて崩れた:CDNや最適化プラグインがファイルを結合・圧縮・書き換えすると中身が変わり、ハッシュが一致しなくなります。自サイトのファイルはSRIの対象から外すのが現実的です。
- プラグイン更新後に動かなくなった:プラグインが読み込むライブラリのバージョンが上がった可能性があります。対象から外すか、ハッシュを更新します。
SRIでブロックされたファイルは代わりのものが読み込まれるわけではないため、重要なライブラリが止まるとサイトの機能に影響します。本番に入れる前にステージング環境で確認し、ライブラリを更新するときは「URLとハッシュを同時に更新する」ことを運用手順に含めてください。
CSPと組み合わせる
SRIは個々のファイルの中身を守る仕組みで、「どのドメインからの読み込みを許すか」はContent Security Policy(CSP)で制御します。CSPで読み込み元を必要なCDNだけに絞り、そのCDNのファイルにSRIを付けると、想定外の読み込み元と、許可した読み込み元での改ざんの両方に備えられます。
まとめ
- SRIは外部CDNから読み込むファイルの改ざんを、ブラウザ側のハッシュ照合で防ぐ仕組みです。
- WordPressでは
script_loader_tag/style_loader_tagフィルターでintegrityとcrossorigin="anonymous"を付けます。 - ハッシュは
openssl dgst -sha384 -binary | openssl base64 -Aで計算し、URLはバージョン固定にします。 - 自サイトのファイルや中身が変わるタグには付けず、ライブラリ更新時はURLとハッシュを一緒に更新します。
- 読み込み元の制限はCSPで行い、SRIと組み合わせると守りが厚くなります。