Content Security Policy

とは

HTML文書中に外部から何らかの方法でいやーんなものを埋め込まれないようにするセキュリティ対策。XSS(クロスサイト・スクリプティング)等の対策。

現在レベル2でレベル3が策定中...なんだが、仕様書のトップが草案段階のレベル3になっててアレレレー状態。

デフォルトは許可(でないと勝手に制限かかるし)。

推奨される設定

XSSの脅威を減らすために、script-srcobject-src、でなければdefault-srcの指定をすべきとされている。加えて、それらには'unsafe-inline'とdata:スキームソースは含まないべき。data:スキームについてはdataスキームでHTMLファイルやCSSファイルに画像データを埋め込むで少し解説している。

適用方法

2通りあって、HTTPヘッダーを使う方法とHTMLのmetaを使う方法がある。

既に読み込まれたものには適用されないので注意。
例えば、Link HTTPヘッダーで読み込んだコンテンツにmeta http-equiv="Content-Security-Policy"で後追いで適用されない。
例えば、HTMLソース上でmeta http-equiv="Content-Security-Policy"より前に登場したコンテンツには適用されない。

HTTPヘッダー

Content-Security-Policy: [シリアライズド・ポリシー]
Content-Security-Policy-Report-Only: [シリアライズド・ポリシー]

それぞれ複数ばらして送っても良い。もし同じディレクティブがあった場合は、両方のディレクティブが適用される。つまりAND条件に近い。
例えば以下の場合:

Content-Security-Policy: default-src 'self' http://example.com; connect-src 'none'; script-src http://example.net
Content-Security-Policy: connect-src http://example.com/; script-src http://example.com/; img-src http://example.net http://example.com

Content-Security-Policy-Report-Only HTTPヘッダーは名前の通り、レポートだけして適用はされない。

meta要素

<meta http-equiv="Content-Security-Policy" content="[シリアライズド・ポリシー]">

report-uri, report-to, frame-ancestors, sandbox, disown-openerは指定できない。

Content-Security-Policy-Report-Onlyはmetaでは指定できない。

構文

ポリシーの構文

[シリアライズド・ポリシー]の構文。正規表現的に書くと...

ソースリストの構文

ディレクティブ値として最も頻出する構文。空白区切りで[ソース表現]を並べる。それぞれのリソースはOR条件、つまり、どれかに合致していれば許可、どれにも合致していなければ不許可となる。ただし、一部'strict-dynamic'のような特殊なものがある。

シングルクォートで囲んでいたりいなかったりする事に注意。

ディレクティブ ie

以下ディレクティブについて説明していく。ディレクティブ名の右にレベル、構文、ブラウザの対応状況の順で記載している。

フェッチ

そのタイプのリソースをロードする条件を指定する。いつくかフォールダウンがあり、以下の通り右側にあるディレクティブから左側にあるディレクティブにフォールダウンする:

複数回登場するものにはカッコを付けている。

child-src  Lv.2-  [ソースリスト]firefox
frame-src + worker-src。それらが無い場合にこのディレクティブにフォールダウンする。
connect-src  Lv.1-  [ソースリスト]safari
APIを使って呼ぶリソースが対象。XMLHttpRequest、WebSocket、EventSource、navigator.sendBeacon、a ping
default-src  Lv.1-  [ソースリスト]
すべてのフェッチディレクティブのフォールダウン。他のフェッチディレクティブの指定が無い場合はこの値をとる。指定があるディレクティブはその値が優先され、default-srcの値は無視される。
font-src  Lv.1-  [ソースリスト]
@font-face
frame-src  Lv.1  未来  [ソースリスト]
ネストされたブラウジングコンテキスト、つまりiframe。レベル2で廃止されたがレベル3で復帰予定。
img-src  Lv.1-  [ソースリスト]
画像。imgpicture、SVGのimage、CSSの*-image
manifest-src  Lv.3  未来  [ソースリスト]
現段階で草案のWeb App Manifest
media-src  Lv.1-  [ソースリスト]
audiovideotrack
object-src  Lv.1-  [ソースリスト]ios
objectembed
script-src  Lv.1-  [ソースリスト]
script、on系イベント属性、javascript:スキーム、XSLTのインラインスクリプトブロック。
style-src  Lv.1-  [ソースリスト]
link rel=stylesheet、CSSの@importLink HTTPヘッダーでインポートされるCSS、style、style属性。
worker-src  Lv.3  未来  [ソースリスト]safariios
WorkerSharedWorkerServiceWorker
prefetch-src  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid
Resource Hintsprefetchまたはprerenderで読み込まれるリソース。
script-src-elem  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid
すべての外部読み込みスクリプトとscript内のスクリプトが対象。属性で指定するイベントハンドラは対象外で、script-src-attrで指定する。

スクリプトの評価はしないので、'unsafe-eval'は指定してもしなくても同じ(実行される)。

script-src-attr  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid

属性で指定するイベントハンドラが対象。

style-src-elem  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid

すべての外部読み込みCSSとstyle内のCSSが対象。style属性は対象外で、style-src-attrで指定する。

style-src-attr  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid
属性で指定するイベントハンドラが対象。

ドキュメント

文書やWorker環境のプロパティを指定。

base-uri  Lv.2-  [ソースリスト]
base href
plugin-types  Lv.2-  [MIMEタイプ]( [MIMEタイプ])*firefoxios
objectembedの可能なMIMEタイプを空白区切りで列挙。大文字小文字を区別しない。読み込まれるリソースのMIMEタイプがディレクティブ値と合致していても、要素にtype属性の指定が無い場合は不許可になる。読み込まれるリソースのMIMEタイプとtype属性値が不一致の場合も不許可になる。
sandbox  Lv.1-  [iframeのsandboxのキーワード]
その文書にiframe sandboxのようなサンドボックス環境を適用する。
disown-opener  Lv.3  未来  (空欄)firefoxoperachromesafariedgeiosandroid
値は無し。指定するとwindow.openernullになる。a rel=noopenerを遷移元ではなく遷移先で指定する感じ。このディレクティブはmetaでは指定できない。

ナビゲーション

フォールダウンがあり、以下の通り右側にあるディレクティブから左側にあるディレクティブにフォールダウンする:

form-action  Lv.2-  [ソースリスト]
form actionやフォーム部品のformaction属性。
frame-ancestors  Lv.2-  [スキームソース]|[ホストソース]|'self'( [スキームソース]|[ホストソース]|'self')*
iframeobjectembedで読み込まれるコンテンツ側から祖先に許可する条件を指定。このディレクティブはmetaでは指定できない。

X-Frame-Options HTTPヘッダーとダブり。

navigate-to  Lv.3  未来  [ソースリスト]firefoxchromeoperasafariedgeiosandroid
a hrefform actionほか、スクリプトでの遷移も含めた遷移先の制御。

レポート

ブロックされた詳細を指定先に通知する。

report-uri  Lv.-2  [URL]
レポート先URLを指定。廃止予定。このディレクティブはmetaでは指定できない。
report-to  Lv.3  未来  [エンドポイントグループ名]firefoxoperachromesafariedgeiosandroid
Report-To HTTPヘッダーのエンドポイントグループ名を指定。Reporting API。このディレクティブは(おそらく)metaでは指定できない。

外部

CSP仕様書外で定義されたディレクティブ。

require-sri-for  未来  [トークン]( [トークン])*firefoxoperachromesafariedgeiosandroid

Subresource Integrity (SRI)を強制するコンテンツ種別を指定。scriptはスクリプト、styleはスタイル。

block-all-mixed-content  (空欄)edge
すべての混在コンテンツをブロック。文書がhttpsで提供されているが、スクリプトや画像がhttpで提供されている場合など。ブロック設定をした場合はその設定を子孫のブラウジングコンテキストに継承する。詳細はMixed Content (MIX)参照。
upgrade-insecure-requests  (空欄)operachromeandroid
安全でないリクエストのアップグレードを許可。詳細はUpgrade Insecure Requests参照。

キーワード

キーワードは大文字小文字を区別しない。また、すべてシングルクォートで囲む。

'none'  Lv.1-
そのリソースを不許可にする。
'self'  Lv.1-
[スキーム名]://[ホストパート][ポートパート]?までのマッチング。[ホストソース]同様にhttp→httpsへ上位互換する(レベル3以降)
'unsafe-inline'  Lv.1-
インラインのリソースを許可。src属性の無いscriptstyle、javascript:スキームで指定されたスクリプトなど。つまりHTML中にべた書きされているものが対象と思われるが、仕様書には明確な範囲の記述がどうも無さそう。

[ソースリスト][nonceソース][hashソース]が含まれている場合は無視される。

'unsafe-eval'  Lv.1-
JavaScriptのeval()を許可。
'strict-dynamic'  Lv.3-  未来safariedgeios
このキーワードが指定された場合、[スキームソース]/[ホストソース]/'self'/'unsafe-inline'は無視される。また、許可されたリソースから明示的に読み込まれた追加のリソースは許可される。
// 明示的に読み込まれる例(許可)
var s = document.createElement('script');
s.src = 'https://othercdn.not-example.net/dependency.js';
document.head.appendChild(s);
// 暗黙的に読み込まれる例(拒否)
document.write('<scr' + 'ipt src="/sadness.js"></scr' + 'ipt>');

これを含んでいる場合、'unsafe-hashed-attributes'を併記しないとon系イベント属性がブロックされる。

'unsafe-hashed-attributes'  Lv.3  未来firefoxoperachromesafariedgeiosandroid
scriptstyle以外のon系イベント属性やstyle属性でも[hashソース]でチェックする。
'unsafe-hashes'  Lv.3  未来firefoxchromeoperasafariedgeiosandroid
scriptstyle以外のon系イベント属性やstyle属性、javascript:スキームも[hashソース]でチェックする。
'report-sample'  Lv.3  未来firefoxsafariedgeios
インラインスクリプト、インラインスタイル、イベントハンドラ属性の違反箇所の先頭40文字をレポートに含める。外部ファイルの場合は含まれない。
'unsafe-allow-redirects'  Lv.3  未来firefoxchromeoperasafariedgeiosandroid
navigate-toディレクティブで有効。遷移先がリダイレクトの場合はスルーし、最終的な着地点のみをチェックする。

テストしたブラウザ

参考