CSPレポート(Mixed Contentの問題)をGoogle Analyticsに集約する
Content Security Policy(CSP) ではさまざまな条件でコンテンツに制約をかけることができます。
そのCSPの中でも実際に制約をかけずにテストして、その結果を特定のURLにPOSTする Content-Security-Policy-Report-Only
という仕組みがあります。
CSPにはHTTPSじゃないと画像やJavaScriptを読み込めなくする制約も設定できるので、HTTPからHTTPSに移行する際などに役立ちます。 例えば、リソースはすべてHTTPSから読み込まないと行けないというCSPの設定は次のようにかけます。
Content-Security-Policy: default-src https:
実際にこのCSPをレスポンスHTTPヘッダに設定するとCSPに対応しているブラウザは、HTTPSではない画像やJavaScriptなどをブロックします。
実際にブロックされると使えなくなって困るので、サイトの管理者はそのようなリソースが埋め込まれていないかを Content-Security-Policy-Report-Only
でチェックすることができます。(実際にアクセスしたブラウザがCSP違反があるならレポート先のURLにデータをPOSTする)
Content-Security-Policy-Report-Only: default-src https: report-to https://example.com/csp-report
のようなレスポンスHTTPヘッダを設定することで、ユーザーがページにアクセスした時に、https://example.com/csp-report
へPOSTでその情報を投げてくれます。(ブラウザが必要な情報を勝手に送信します)
ここではreport-to
と書いていますが、古いブラウザはreport-uri
だったりするので注意します。
このCSPについては次の記事が詳しいです。
- コンテンツ セキュリティ ポリシー | Web | Google Developers
- Content Security Policy (CSP) - HTTP | MDN
- CSP Report 収集と実レポートの考察 | blog.jxck.io
- Content Security Policy(CSP) 対応と report-uri.io でのレポート収集 | blog.jxck.io
このCSPレポートの仕組みは便利ですがそのデータの集約先をどうするかという問題がついてきます。 CSPレポートは PV * CSP違反のリソース数 となるため適当にやると膨大なデータが飛んでくる可能性があります。 このCSPレポートを収集できるサービスとしてreport-uri.comやsentry.ioなどがあります。
どちらもそれだけのために使うのも微妙だなーと思いCSPについて調べていたところ、CSPの制約違反はreport-to
だけではなくJavaScriptのイベントとして取得する方法を見つけました。
securitypolicyviolation
イベントではCSPの制約違反をした時にSecurityPolicyViolationEventオブジェクトと共に呼び出されます。
document.addEventListener("securitypolicyviolation", (e) => {
console.log(e.blockedURI);
console.log(e.violatedDirective);
console.log(e.originalPolicy);
});
このイベントを使えば、report-to
(report-uri
)の指定以外の方法でも任意の場所にCSPレポートを送ることができそうです。
こういったPVに関連するデータの集積といったらGoogle Analyticsがよく使われていると思います。 このサイトでもGoogle Analyticsを使っていたので、CSPレポートをGoogle Analyticsに送るanalytics.jsのプラグインを作りました。
csp-report-to-google-analytics
- ソースコード: azu/csp-report-to-google-analytics: CSP report to Google Analytics.
- CDN: https://unpkg.com/csp-report-to-google-analytics/dist/csp-report-to-google-analytics.min.js
- unpkgから直接読み込めます
このanalytics.jsのプラグインはCSPレポートをイベントとしてGoogle Analyticsに送信します。
導入方法は簡単で既にanalytics.jsを導入している人は、
<script async src='https://unpkg.com/csp-report-to-google-analytics/dist/csp-report-to-google-analytics.min.js'></script>
でプラグインを別途読み込み、ga('require', 'csp-report');
でプラグインanalytics.jsに適応するだけです。
<!-- Google Analytics -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
// require csp-report-to-google-analytics plugin
ga('require', 'csp-report');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
<!-- End Google Analytics -->
<!-- Load csp-report-to-google-analytics plugin -->
<script async src='https://unpkg.com/csp-report-to-google-analytics/dist/csp-report-to-google-analytics.min.js'></script>
今のGoogle Analyticsはgtag.jsがデフォルトみたいですが、gtag.jsはプラグインの仕組みをサポートしていないためanalytics.jsでしか動きません。
あとは待っていればCSPレポートがGoogle Analyticsのイベント画面に表示されます。 (SecurityPolicyViolationEventに対応しているブラウザでアクセスする必要があります。)
efcl.infoでのテスト
このサイトでもHTTPSに移行するためにこのプラグインを導入してみました。
CSPは次のようなディレクティブが書かれています。
(http://www.google-analytics.com/*
を許可しているのは、Google Analyticsがビーコンに画像を使っていて再帰的に掛かりそうな問題があるため。これBeacon APIにすれば解消するのかも)
Content-Security-Policy-Report-Only: default-src https: http://www.google-analytics.com/* 'unsafe-eval' 'unsafe-inline';
実際に取得できたCSPレポートはGoogle Analyticsの管理画面で見ることができます。 Google Analyticsのイベント トラッキングを使っているため自動的にページなどに紐付いたデータとなっています。
このデータはHTTPSへの移行する時のMixed Content探しの指標データなどとして利用できると思います。
もっとしっかりと分析したい人はAWSのAPI Gatewayで作成したAPIをreport-to
(report-uri
)に指定し、Amazon Kinesis Data Firehoseにデータを流し、S3やRedshiftなどでデータを分析するのがいいと思います。
Google Analyticsのイベントは送ることができるデータに制約があるので、csp-report-to-google-analytics では重要そうなデータしか送っていません。
まとめ
csp-report-to-google-analyticsを使ったCSPレポートをGoogle Analyticsに送る方法を紹介しました。
CSPのレスポンスHTTPヘッダを指定する必要がありますが、結構気軽にできるので面白いかもしれません。
残念ながら <meta>
タグでは Content-Security-Policy-Report-Only
が利用できません。
(通常のCSPは設定できますが実際にコンテンツが表示されなくなるので、目的に合わない)
そのため、この手法にはHTTPヘッダを設定できるサーバが必要です。
このサイトはGitHub PagesからNetlifyに移すことで、この問題を回避しています。
お知らせ欄
次に書くかもしれない記事候補
興味がありましたらIssues · efcl/efcl.github.ioからご意見下さい