CSPビルダー
実在するページのアドレスを教えてください。HTMLを読み、スクリプト、スタイル、画像、フォント、フレームを読み込んでいる場所をすべて一覧にして、レポート専用モードで試せるポリシーを書きます。このモードなら何も壊れません。
自分のページのための招待客リスト
ブラウザはページに言われたことを何でも実行します。HTMLに「あのサーバーのこのスクリプトを実行せよ」とあれば実行します。これが危険になるのは、誰か別の人があなたのページに1行を書き加えることに成功した日です。掃除されていないコメント欄、乗っ取られたプラグイン、広告などを通じてです。ブラウザには、その行とあなたの行を見分ける手段がありません。
Content-Security-Policy(CSP)はレスポンスヘッダーで、このページがどこから物を読み込んでよいかを、あらかじめブラウザに伝えます。招待客リストのようなもので、リソースの種類ごとに1行ずつ書きます。
default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' data: https://images.example.net;
object-src 'none'各行がディレクティブです。'self'はページ自身のオリジンを意味します。リストにないものはすべて拒否され、その拒否はブラウザのコンソールに記録されます。
このページが下書きを作る方法
当サーバーはアドレスを一度だけ取得し、ブラウザと同じようにHTMLを解析しますが、何も実行しません。見つけたアドレスはすべてオリジン(スキームとホスト、パスなし)まで切り詰めて分類します。
| ディレクティブ | 読み取り元 |
|---|---|
script-src | <script src>、モジュールのプリロード、インラインのローダーコード内に書かれたスクリプトのアドレス |
style-src | スタイルシートのリンクと@importルール |
img-src、font-src | 画像タグ、srcset、アイコン、ポスター画像、プリロード、およびインラインCSSとサイト自身のオリジンにある最初の4つのスタイルシート内のurl() |
frame-src、media-src、object-src、manifest-src | フレーム、音声と動画、プラグイン、マニフェストのリンク |
form-action、base-uri | フォームの送信先と<base>タグ |
connect-src | preconnectのヒントと、ページ内で見つかった一部サービス(Google Analytics、Google Fonts、Cloudflare Web Analytics)の公表されたエンドポイントのみ |
下書きはdefault-src 'self'から始め、ディレクティブごとに1行を加え、フォールバックのない扉を閉じます。object-src 'none'、base-uri 'self'、frame-ancestors 'self'、そしてHTTPSのページではupgrade-insecure-requestsです。ページがすでにヘッダーやmetaタグでポリシーを送っている場合は、それを表示し、ページが読み込むものと比較します。
難しいところ:インラインコード
注入されるコードはほぼ必ずインラインです。ページに書き込まれた<script>ブロックやonclick属性という形をとります。ですから、意味のあるポリシーはインラインコードを禁止します。困ったことに、ほとんどのサイトは自前のインラインコードをたくさん抱えています。このツールはスクリプトブロック、イベントハンドラー属性、javascript:リンク、スタイルブロック、style=属性を数え、二つの下書きを出します。
- 厳格版の下書きには
'unsafe-inline'がありません。最終的に目指すべき形ですが、上で数えたインラインコードは動かなくなります。 - 移行版の下書きは、ページが必要とする箇所に
'unsafe-inline'を加えます。読み込み元のサイトは引き続き制限しますが、注入されたインラインスクリプトはもう止められません。そのため「弱い」と表示されます。
一方から他方へ移る方法は三つあります。コードをファイルに移す、サーバーがレスポンスごとのnonceを各タグに付ける、各ブロックのSHA-256ハッシュをポリシーに列挙する、のいずれかです。このツールは見つけたスクリプトブロックのハッシュを計算します。ハッシュが役立つのは、テキストが一切変わらないブロックだけです。
まずは必ずレポート専用で
下書きはContent-Security-Policy-Report-Onlyとして送ってください。ブラウザは何も強制せず、ブロックしたであろうものを記録します。コンソールを開いたままサイトを一通り見て回り、正当に足りないものを加え、記録が静かになるまで繰り返します。それからヘッダー名をContent-Security-Policyに変えます。Apache、nginx、metaタグ用の設定例を用意しています。metaタグの形式では、レポート専用モードもframe-ancestorsも使えません。
下書きは1ページを一度だけ、何も実行せずに読んだ結果です。読み込み後にスクリプトが送るリクエスト(アクセス解析のビーコン、チャットウィジェット、決済フレーム、遅延読み込みの画像)、ログインした訪問者に表示されるもの、サイトのほかのページは見えません。読み込み元を追加することになると思ってください。ページが現在送っているヘッダーの評価には、セキュリティヘッダー評価を使ってください。
よくある質問
Content-Security-Policyでサイトが壊れることはありますか?
強制モードのポリシーは、リストにないものをすべてブロックします。ですから間違ったポリシーなら壊れます。レポート専用のポリシーは記録するだけなので、何も壊せません。まずそこから始め、主要なページでコンソールに違反が出なくなったら強制モードに切り替えてください。
unsafe-inlineを含むポリシーに意味はありますか?
ある程度はあります。スクリプト、フレーム、フォーム送信の行き先は引き続き制限し、プラグインをブロックし、<base>タグの乗っ取りを防ぎます。ただ、ページにスクリプトブロックを書き込める攻撃者は止められず、それこそがポリシーを持つ一番の理由です。途中の一段階として使ってください。
nonceとハッシュの違いは?
nonceはサーバーがレスポンスごとに生成するランダムな値で、信頼するすべてのscriptタグに同じ値を付けます。ハッシュは特定のコードブロック一つの指紋で、ポリシーに列挙します。nonceはリクエストごとに組み立てるページに向いています。ハッシュは、キャッシュされうるページにある少数の静的なコードに向いています。
生成されたポリシーにパスが含まれないのはなぜですか?
CSPではhttps://cdn.example.com/lib/のようなパスも書けますが、リダイレクト後には無視され、ファイルが移動した途端に動かなくなります。実際に運用されているポリシーの多くはオリジンを列挙しています。公開CDNのように多くの提供元のファイルを置くオリジンなら、integrityハッシュで正確なファイルも固定してください。
WordPressなどのCMSでCSPを設定できますか?
はい。ウェブサーバーの設定か、ヘッダーを送るセキュリティプラグインで設定できます。テーマとプラグインは長いインラインスクリプトとスタイルの一覧を伴うので、レポート専用ヘッダーと移行版の下書きから始め、プラグインを一つずつ見ながらインラインコードを減らしていってください。
ポリシーが何をブロックしたかはどこで見られますか?
ブラウザの開発者ツールのConsoleタブです。違反ごとにディレクティブとブロックされたアドレスが表示されます。訪問者から集めるには、収集先を指すreport-uriまたはreport-toディレクティブを加えてください。