CSP は「このページで何をしていいか」をブラウザに教えるルールブック
CSP(Content Security Policy)は、
「このページでは、こういうリソースだけ読み込んでいい/こういうスクリプトだけ実行していい」
とブラウザに約束させる仕組みです。
イメージとしては、
「この部屋では、入っていい人・持ち込んでいい物をリストで決めておく」
そんなセキュリティゲートです。
特に効いてくるのは、XSS などの攻撃に対してです。
スクリプトを埋め込まれても、
「そのスクリプトはポリシー違反だから実行しない」と
ブラウザ側で止められるようになります。
CSP の基本的な指定方法と“最初の一歩”
どこで設定するのか
CSP は、主に次のどちらかで指定します。
HTTP レスポンスヘッダで指定するContent-Security-Policy: ...
HTML の <meta> タグで指定する<meta http-equiv="Content-Security-Policy" content="...">
本番サイトでは、基本的に「レスポンスヘッダ」で設定します。
ここではイメージを掴むために、文字列としてどう書くかに集中します。
一番シンプルな例
まずは、超シンプルな CSP から。
Content-Security-Policy: default-src 'self';
意味はこうです。
このページで読み込んでいいリソース(スクリプト・画像・CSS など)は
「自分自身のオリジン(ドメイン)」だけに限定する
つまり、
自分のサーバー上の JS / CSS / 画像は OK
CDN や他ドメインからの読み込みは NG
になります。
これだけでも、
「よく分からない外部スクリプトが勝手に読み込まれる」
といった事故をかなり防げます。
スクリプトを絞り込む script-src の考え方
「どこからのスクリプトなら実行していいか」を決める
XSS 対策として特に重要なのが script-src です。
例えば、こう書きます。
Content-Security-Policy: script-src 'self';
意味は、
このページで実行していい JavaScript は
「自分のオリジンから配信されたファイルだけ」
ということです。
ここで重要なのは、
インラインスクリプト(<script>... の中身)や、onclick="..." のような HTML 属性内の JS は、デフォルトで禁止になる
という点です。
つまり、こんな HTML は CSP 違反になります。
<script>
console.log("インラインスクリプト");
</script>
<button onclick="alert('クリック')">ボタン</button>
CSP 的には、
「HTML の中に直接 JS を書くのは危険だからやめてね」
というメッセージです。
じゃあどう書けばいいのか
CSP をちゃんと効かせる前提では、
「スクリプトは全部 .js ファイルに分離する」 のが基本になります。
<script src="/js/app.js"></script>
そして app.js の中に、
イベントリスナーなどをちゃんと書きます。
document.querySelector("#btn").addEventListener("click", () => {
alert("クリック");
});
JavaScriptこれは、セキュリティだけでなく
コードの整理という意味でも良い習慣です。
インラインスクリプトをどうしても使いたいときの nonce
「このスクリプトだけは特別に許可する」という仕組み
現実のプロジェクトでは、
「どうしてもインラインスクリプトを使いたい」
という場面もあります。
そのときに使えるのが nonce です。
流れはこうです。
サーバーがランダムな文字列(nonce)を生成する
CSP ヘッダに script-src 'self' 'nonce-ランダム文字列' を書く
同じ nonce を <script> タグの属性として付ける
ブラウザは、
「CSP に書かれた nonce と一致するものだけインライン実行を許可」
という動きをします。
例を見てみましょう。
レスポンスヘッダ:
Content-Security-Policy: script-src 'self' 'nonce-abc123';
HTML:
<script nonce="abc123">
console.log("このインラインスクリプトは許可される");
</script>
この場合、nonce="abc123" が CSP の 'nonce-abc123' と一致するので、
このインラインスクリプトは実行されます。
逆に、攻撃者が勝手に <script>alert(1)</script> を埋め込んでも、
nonce が付いていないので実行されません。
ここでの重要ポイントは、
nonce は毎回ランダムに変える(固定値にしない)
サーバー側で生成して、ヘッダと HTML の両方に埋め込む
ということです。
レポートモードで「まずは壊さずに様子を見る」
いきなり本番でガチガチにすると、普通の機能も壊れる
CSP をいきなり厳しくすると、
「自分のサイトの正しい機能」まで壊してしまうことがあります。
例えば、
CDN から読み込んでいるライブラリがブロックされる
インラインで書いていた小さなスクリプトが動かなくなる
などです。
そこで便利なのが、
レポート専用モード です。
Content-Security-Policy の代わりにContent-Security-Policy-Report-Only を使うと、
「違反はログに出すけど、実際にはブロックしない」
という動きになります。
例:
Content-Security-Policy-Report-Only: default-src 'self';
これを設定しておくと、
本来ならブロックされる外部リソース
インラインスクリプト
などがあったときに、
ブラウザのコンソールに警告が出ます。
これを見ながら、
どのリソースを許可する必要があるか
どのインラインスクリプトを修正すべきか
を洗い出していき、
準備が整ったら本番の Content-Security-Policy に切り替える、
という流れが安全です。
よく使うディレクティブのざっくりイメージ
default-src と個別の *-src
CSP にはいろいろな「〜src」がありますが、
初心者のうちは次だけ覚えておけば十分です。
default-src
何も指定されていない種類のリソースのデフォルトルール
script-src
スクリプト(JavaScript)の読み込み・実行ルール
style-src
CSS の読み込みルール
img-src
画像の読み込みルール
例えば、こう書くとします。
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' https://images.example.com;
意味はこうです。
基本は全部 ‘self’(自分のオリジン)だけ
スクリプトだけは、自分+cdn.example.com を許可
画像だけは、自分+images.example.com を許可
という感じで、
「種類ごとに、どこから読み込んでいいかを細かく決められる」
のが CSP の強さです。
初心者として CSP でまず掴んでほしいこと
CSP は、仕様を全部覚える必要はありません。
まずは「考え方」と「最初の一歩」だけで十分です。
この感覚を持っておいてください。
CSP は「このページで何をしていいか」をブラウザに宣言するルールブックdefault-src 'self' で「基本は自分のサーバーだけ」をベースにするscript-src で「どのスクリプトを実行していいか」を絞る(XSS 対策の要)
インラインスクリプトは禁止が基本。どうしても必要なら nonce で「この一個だけ特別に許可」
いきなり本番で厳しくせず、まずは Content-Security-Policy-Report-Only で様子を見る
CSP は、
「コードの書き方」だけでは防ぎきれない攻撃を、
ブラウザ側のルールで一段ガードしてくれる仕組みです。
あなたがセキュリティを考えるときに、
「コードだけじゃなくて、ブラウザにルールを課すという手もある」
と発想できるようになったら、
それだけでエンジニアとしての視界が一段広がっています。
