記憶に残ったことを忘れないために色々メモとしてのこしていきます

Content Security Policy (CSP) について

背景

外部ライブラリを特定のサイトに組み込むとき、リクエストが飛んでいないことがあった。コンソールをみると "Content Security Policy"なるエラーがでていた。実は以前"Content Security Policy"関連のレスポンス表示を見たことがあったのだけど、あまり調べずにそのまま放置していた。いい機会なので今回CSP (Content Security Policy) について少し調べてみた。

CSPとは?

コンテンツセキュリティポリシー (CSP) は、クロスサイトスクリプティング (Cross-site-scripting) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。これらの攻撃はデータの窃取からサイトの改ざん、マルウェアの拡散に至るまで、様々な目的に用いられます。

引用: https://developer.mozilla.org/ja/docs/Web/HTTP/CSP

つまり、特定のサイトに下記のような制約を加えることで、CSSやJavaScriptの実行方法とリソースの読み込み先をホワイトリスト的に管理することができるようだ。

  • 外部リソース (JavaScript, CSS, 画像など) の読み込み
  • inlineスクリプトの実行
  • evalの実行
  • inlineスタイルの適用


仕組み

1. ヘッダーの指定によるリクエストの制御

2. 設定方法

HTTP Response Headerに下記を追加する
ポリシー には、サイトに適用するCSPディレクティブ (CSPの内容を指定するための要素) から構成される文字列を指定する

Content-Security-Policy: ポリシー

CSP HTTP Header

Content-Security-Policy: ディレクティブ名 ソース; ディレクティブ名 ソース;

; で区切られている点がポイント。

CSP HTTP Headerの記述例

Content-Security-Policy: img-src 'self' https://example.com

上記の場合は、画像リソース用ディレクティブ img-src に対しての設定例。サイト自身のオリジン selfhttps://example.com からの画像かfaviconのみが有効となり、それ以外のオリジンからは、コンテンツとして受けつけないようになる。

ディレクティブ例
script-src

<script> で読み込まれる外部リソースの制御だけでなく、inline等の onclick などの実行も制御する。

<script src="https://not-example.com/js/library.js"></script>

<button id="btn" onclick="doSomething()"></button>
🖕上記はブロックされるため、下記のように記述する必要がある
document.getElementById("btn").addEventListener("click", doSomething);


リソースや unsafe-inline 以外にも nonce も利用できる。

Content-Security-Policy: script-src 'nonce-2726c7f26c'


上記ディレクティブの場合は、下記の処理は実行できる (同一のnonce値である場合に許可される)

<script nonce="2726c7f26c">
  const inline = 1;
  // …
</script>


また、今後増えつつありそうなWASMの処理も下記のように明示的に許可をする必要がある

Content-Security-Policy: script-src 'wasm-unsafe-eval'


connect-src

a や JS のfetch等のメソッドによって読み込まれるリソースを制限。例として、下記はブロックされ、実行できない。

<a ping="https://not-example.com">
<script>
  const xhr = new XMLHttpRequest();
  xhr.open("GET", "https://not-example.com/");
  xhr.send();

  const ws = new WebSocket("https://not-example.com/");

  const es = new EventSource("https://not-example.com/");

  navigator.sendBeacon("https://not-example.com/", {
    /* … */
  });
</script></a>

img-src

画像やfaviconに対する有効なソースを定義する

style-src

CSS関連のリソース制御

Content-Security-Policy: style-src https://example.com/


上記ディレクティブの場合、下記はブロックされる

<link href="https://not-example.com/styles/main.css" rel="stylesheet" />

<style>
  #inline-style {
    background: red;
  }
</style>

<style>
  @import url("https://not-example.com/styles/print.css") print;
</style>

<div style="display:none">Foo</div>

document.querySelector('div').setAttribute('style', 'display:none;');
document.querySelector('div').style.cssText = 'display:none;';
🖕上記はブロックされるため、下記のように記述する必要がある
document.querySelector('div').style.display = 'none';

script-src と同様に <style>nonce を利用することも可能。

default-src

別のディレクティブに対するフォールバックとして機能する。例えば、 script-src が明示的に指定されていないとき本ディレクティブが制御する。