👋

Cloudflare PageShield Positive Blocking と LogPush

2023/10/25に公開

今日は先日投稿した以下の記事の続きです。
https://zenn.dev/kameoncloud/articles/9045a12cfe5365

前回の記事ではScript Monitoringとして、実行されるスクリプト(jsファイル)を監視し、変更履歴をトラックする機能をご紹介しました。

一方今回ご紹介するPositive Blockingではスクリプトが指定された場所から読み込まれたかどうかを見張り、不正な場所から読み込まれるスクリプトをBlockします。Script Monitoringは5~10%程度のサンプリングで機能しますが、このPositive Blockingはルールに違反した場合100%スクリプトの実行を停止させます。

このルールはCSP ディレクティブにより実装されます。

CSP ディレクティブとは

chatGPT先生によるとCSP ディレクティブの定義は以下の通りです。

CSP (Content Security Policy) は、クロスサイトスクリプティング (XSS) やデータインジェクション攻撃などの特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。HTTP レスポンスヘッダーに設定されるディレクティブであり、ウェブサイトが読み込むことができるリソースの情報を指定します。
特定の種類のリソースやポリシーの適用範囲をそれぞれ表すディレクティブを列挙することでポリシーを表現します。CSP を有効にするには、ウェブサーバーから Content-Security-Policy HTTP ヘッダーを返すように設定する必要があります。

単純に言うとブラウザがサーバからHTMLなどのアセットを読み込む際に、ヘッダー情報にスクリプト読み込んでよい場所を指定します。これにより、HTMLが書き換えられ外部の不正なスクリプトを読み込もうとした場合の挙動を制御することが可能です。

Content-Security-Policy: default-src 'self'; img-src https://*; child-src 'none';

これが一例です。この場合、default-src ディレクティブにより、同一オリジンからのリソースの読み込みを許可し、img-src ディレクティブにより、HTTPS 経由での画像リソースの読み込みを許可し、child-src ディレクティブにより、子フレームの読み込みを禁止します。

PageShiledはCloudflareのエッジでそれを動的に付与してクライアントにレスポンスを戻すことで、不正なサイトからのスクリプトの読み込みをBlockします。

PositiveBlockingをやってみる

この機能はEntepriseアカウントに対するPaid-Add-Onになりますので、テストを行いたい場合、Cloudflareまで連絡いただくか、@kameoncloudまでご連絡ください。

まずは以下の手順を完了させます。
https://zenn.dev/kameoncloud/articles/9045a12cfe5365
次に、Policiesのタブをクリックします。

Create Policyを押します。最初に同じドメインから読み込まれるスクリプトの実行を許可するポリシーを作ってみます。Webサイトはhttps://pageshield.a.harunobukameda.com/にホスティングされているものとします。

条件式を追加します。

次にリクエストがこの条件に合致した場合適応されるディレクティブを設定します。

この設定は同じオリジンからのみスクリプトの実行を許可します。

スクリプトの実行を許可します。ここの設定はAllowLogの2種類しかなく、Denyがないので少しややこしいかもしれません。上記で設定したCSPディレクティブに合致している場合のみスクリプトの実行を許可するのがAllowで(合致しない場合、つまり別のドメインからスクリプトを読み込もうとした場合は拒否されます)、スクリプトは実行させるがログを出力するのがLogです。

一旦Allowで設定を行います。

暫く待った後Webにアクセスします。
script.jsは当然CSP ディレクティブに合致しているので実行されています。
では再度Policyを設定します。今度はすべてのScriptの禁止を行います。

先ほどのPoliciesのEditを押し、CSPディレクティブをDeleteします。

noneを選んで保存します。
これにより全てのスクリプトの実行を止めます。数分待って再度Webにアクセスしてみます。


リクエストが違反していることが検知されます。
ブラウザのデベロッパーツールでも以下のエラーが出ていることがわかります。

Application→Report APIでは、エラーをCloudflareレポートエンドポイントに情報をアップロードしようとしていることがわかります。

{blockedURL: "https://pageshield.a.harunobukameda.com/script.js", disposition: "enforce",…}
blockedURL
: 
"https://pageshield.a.harunobukameda.com/script.js"
disposition
: 
"enforce"
documentURL
: 
"https://pageshield.a.harunobukameda.com/hello.html"
effectiveDirective
: 
"script-src-elem"
originalPolicy
: 
"default-src 'none'; report-uri https://csp-reporting.cloudflare.com/cdn-cgi/script_monitor/report?m=_s3FYxr8dqJwkSo19sgOaUlHMKS.rKLh_2O5amf_a7U-1698218867-0-AeIsMFyY_I5gMCWY4iRI0Pt84aGpug5RI9KXgX5j1cc1UZG1Ck9lr_FAm8ptWUwd8rWT_cUICKf-jDFSVK7O3MOV4fovGOXXaDzHagpr2dTpMtV3qgmVOfs2bBgcg4Ocw5CKxQZTLtFcqdygi-wvDt9pH-UXK5HlEUBTiwp30TYQpb8GtSIF_fJMwwb5NUm2WUFCOp065qWJ-LJccvmtjwdsOF2JqKyCpDmUNobs8PGO; report-to cf-zwnwbdkridlgkgvi"
referrer
: 
""
sample
: 
""
statusCode
: 
200

また読み込んだHTMLのResponse Headerにも値が付与されています。

では最後にアクションをLogにしてみます。
エラーは吐いていますが、スクリプトは実行されます。

LogPush

ではこの違反レポートをLogPush機能でログに吐き出します。
手順自体はこちらにまとまっています。
https://zenn.dev/kameoncloud/articles/f441eebf32e851
設定の際のイベントソースで以下を選択します。

Reportが出力されない場合。ブラウザのデベロッパーツールでReportAPIを開くと個別のエラーレポートがブラウザ側で停止しているケースがあります。

Queueは送ろうとしている状態。MarkedForRemovalは送れず削除がされた状態、です。
Successの場合以下のようなデータが出力されます。

{"Action":"log","Host":"pageshield.a.harunobukameda.com","PageURL":"https://pageshield.a.harunobukameda.com/hello.html","PolicyID":"8161d4a5343345158c3b2cd3fbc5d57d","Timestamp":"2023-10-25T07:48:59Z","URL":"https://pageshield.a.harunobukameda.com/script.js","URLContainsCDNCGIPath":false,"URLHost":"pageshield.a.harunobukameda.com"}

この機能はChromeのまだExperimentalのAPIを活用しているため発生するようです。このログ送信ステータスがSuccessとなった場合ログが出力されます。
100%ログが出力されるわけではないためご注意ください。

Discussion