ApacheでContent Security Policyを設定したメモ。
httpd.confで設定する例。
Header set Content-Security-Policy "default-src 'self'; script-src 'self', 'unsafe-inline', www.example.com"
とりあえずレポートだけ見たい場合の例。
Header set Content-Security-Policy-Report-Only \
"default-src 'none';\
script-src 'self';\
object-src 'none';\
img-src 'self';\
media-src 'none';\
frame-src 'none';\
font-src 'none';\
connect-src 'none';\
style-src 'self';\
report-uri /csp-report.php?v=1"
レポートでどのポリシーに違反したのか見やすくするため、xxxx-srcを全部書いた。
何がエラーになるか分かるように基本厳し目に、使わなそうなのはとりあえずnoneにしている。
report-uriのパラメータ(v=1)は後述。
右辺の書き方については下記参照。
CSP policy directives - Security | MDN
はまりそうなとこだけ書くと、
- 'none'・'self'・'unsafe-inline'・'unsafe-eval'はシングルクォーテーションも含めて書く必要がある。
- データスキーム(data:)はコロンも含めて書く。
(mod_pagespeedを使っていると、気づかない間にデータスキームが使われていたりする。)
ちなみに、スキームはデータに限らず、"https:"など特定のスキーム限定を指定できるみたい。 - URLはスキームを省略可(例:www.example.com)。省略した場合、元のページと同じスキームと同じもののみ許可する。
ポートも同様。 - URLで別のスキームを許可したい場合、別途記載が必要。
例:元のページがhttpで、読み込むCSSがhttpsの場合、httpsのURLも書く必要がある。
ワイルドカードが使えるという説明もあったが、下記のどちらも駄目だった(ブラウザによる?)
- *://www.example.com
- *//www.example.com
- URLのサブドメインはワイルドカード(*)が使える。
サブドメインをワイルドカードにすると、サブサブ(...略)ドメインまでワイルドに適用される。
ただしサブドメイン無しには適用されないらしい。
レポートをログに書き出すPHPの例。
<?php
if (!$_GET || $_GET['v'] < 1) {
exit;
}
$report = json_decode(file_get_contents('php://input'), true);
$log = date('[Y-m-d H:i:s] ') . $_SERVER['HTTP_USER_AGENT'] . ' ';
$log .= var_export($report['csp-report'], true) . "\n";
error_log($log, 3, '/var/log/csp-report.log');
Content-Security-Policy-Report-Onlyの例でreport-uriにパラメータを付けたのは、このPHPの2行目で古いポリシーを無視するため。
ブラウザによってはキャッシュしてしまうようで、古いポリシーに基づき送信してくることがあり、ノイズになるので。
ポリシーを変えたら、パラメータの数字とPHPの2行目の数字を両方インクリメントする。
ログファイルはApacheが書き込めるように権限設定しておく。
(2015/3/2 追記)
logrotateも追加しておく。(phpのlogrotate設定をコピーして作成。)
/var/log/csp-report.log { missingok notifempty}
(追記終わり)
また、ModSecurityを使っている場合はレポートがModSecurityではじかれないように注意。
ログを見ていると、違反URLとして報告される"about"はabout:blankだと思うけど、"asset"って何だろう?
ちなみに、Gmailのポリシーはこんな感じだった。
script-src https://*.talkgadget.google.com 'self' 'unsafe-inline' 'unsafe-eval' https://talkgadget.google.com https://www.googleapis.com https://www-gm-opensocial.googleusercontent.com https://docs.google.com https://www.google.com https://s.ytimg.com https://www.youtube.com https://ssl.google-analytics.com https://apis.google.com https://clients1.google.com https://ssl.gstatic.com https://www.gstatic.com blob:;frame-src https://*.talkgadget.google.com https://www.gstatic.com 'self' https://accounts.google.com https://apis.google.com https://clients6.google.com https://content.googleapis.com https://mail-attachment.googleusercontent.com https://www.google.com https://docs.google.com https://drive.google.com https://*.googleusercontent.com https://feedback.googleusercontent.com https://talkgadget.google.com https://isolated.mail.google.com https://www-gm-opensocial.googleusercontent.com https://plus.google.com https://wallet.google.com https://www.youtube.com https://clients5.google.com https://ci3.googleusercontent.com;object-src https://mail-attachment.googleusercontent.com;report-uri /mail/cspreport
見やすく整形。
script-src
https://*.talkgadget.google.com
'self'
'unsafe-inline'
'unsafe-eval'
https://talkgadget.google.com
https://www.googleapis.com
https://www-gm-opensocial.googleusercontent.com
https://docs.google.com
https://www.google.com
https://s.ytimg.com
https://www.youtube.com
https://ssl.google-analytics.com
https://apis.google.com
https://clients1.google.com
https://ssl.gstatic.com
https://www.gstatic.com
blob:
frame-src
https://*.talkgadget.google.com
https://www.gstatic.com
'self'
https://accounts.google.com
https://apis.google.com
https://clients6.google.com
https://content.googleapis.com
https://mail-attachment.googleusercontent.com
https://www.google.com
https://docs.google.com
https://drive.google.com
https://*.googleusercontent.com
https://feedback.googleusercontent.com
https://talkgadget.google.com
https://isolated.mail.google.com
https://www-gm-opensocial.googleusercontent.com
https://plus.google.com
https://wallet.google.com
https://www.youtube.com
https://clients5.google.com
https://ci3.googleusercontent.com
object-src
https://mail-attachment.googleusercontent.com
report-uri
/mail/cspreport
scriptは'unsafe-inline'も'unsafe-eval'も許可してしまっているが、imgも含めて*を使っていないのは立派。
style等は(defaultも)指定していないが、不要という判断か、指定すると差し障りがあるのか、どちらだろう?
また、1バイトを削るのにもこだわるGoogleがわざわざこれだけの文字列を送信するのは、セキュリティの方が重要だからということだろう。
サブドメインをワイルドカードでまとめれば随分減りそうだが、Googleともなると使っているサブドメインも膨大なので、セキュリティが担保しづらくなるのだろう。
policy-uriを指定してポリシーをXMLでやりとりしてキャッシュを効かせれば総通信料は減りそうだが、柔軟性とのトレードオフか。
report-uriも送信料を増やすが、これ無しは運用的にきっと難しいだろう。