とんかつ時々あんどーなつ

〜たとえ低空でも飛行していられるように〜

Content Security Policy を調べる

Content Security Policy とは

Content Security Policy (CSP) は、クロスサイトスクリプティング (XSS) を始めとした content injection 攻撃を検知し、緩和するためのセキュリティ機能である。 CSP を利用することで、許可していない script や stylesheet の読み込みや実行をブロックできる。

CSP の基本的な機能はすでにモダンブラウザで利用可能である。

なぜ CSP が必要なのか

Web には Same-Origin Policy (SOP)というセキュリティモデルが存在する。これは異なる origin にあるリソースへのアクセスを制限する仕組みである。 これにより悪意のあるサイトからの攻撃を防ぐことができる。

一方で SOP は同一 origin の場合はリソースアクセスへの制限がかからない。この穴を突いた攻撃に Cross-Site Scripting (XSS) がある。

たとえば掲示板のようなサービスがあると仮定する。そこでは任意の HTML タグを使った自由な装飾が可能だった場合、悪意のある script タグを埋め込むことができる。 それを掲示板サービスから動かしても SOP の制限には引っかからないため攻撃が成立してしまう。

このような XSS 攻撃などの影響を軽減する手法の一つが CSP である。

どのように CSP を有効化するか

CSP を有効にするには次の 2 通りの方法がある。

  • Content-Security-Policy ヘッダーを HTTP レスポンスに含める
  • <meta> 要素を使う

meta 要素を使った場合の最もシンプルな設定は次のとおりである。

<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />

※ http-equiv は HTTP ヘッダーと同等の機能を提供する

content に指定するものを directive set と呼ぶ。 これは directive の集合であり、ひとつの directive は name と value の組み合わせとなる。name を空にすることはできない。

上の例では 1 つの directive が指定されており、このときの name が default-src で value が 'self' ということになる。これはすべてのリソースは自身と同じドメインからのみ取得を許可することを示す。

directive の種類

directive には様々な種類がある。 大きな分類としては 4 つあり、それぞれ次のとおりである。

分類 説明
Fetch directive リソースの取得を制御
Document directive ドキュメントの状態を制御
Navigation directive ナビゲーションを制御
Reporting directive レポートを制御

XSS 攻撃などの影響を軽減する目的には Fetch directive が適しているため、今回は Fetch directive に限定して説明する。

Fetch directive の種類

Fetch directive に属する directive も複数存在する。

Content-Security-Policy - HTTP | MDN

例えばスクリプトの実行を制限したい場合は script-src という directive を使う。同様にスタイルなら style-src を、画像なら img-src を指定する。 制限したいリソースの種別ごとに様々な directive があるため、用途に合わせて選択するのがよい。

また default-src という特別な directive もある。これは他の directive が指定されていない場合のフォールバックとして使われる。

例えば、次の 2 つは同じ意味を持つ。

Content-Security-Policy: default-src 'self'; script-src https://example.com
Content-Security-Policy: connect-src 'self';
                         font-src 'self';
                         frame-src 'self';
                         img-src 'self';
                         manifest-src 'self';
                         media-src 'self';
                         object-src 'self';
                         script-src https://example.com;
                         style-src 'self';
                         worker-src 'self'

他の directive が付与されている場合、default-src が適用されないという点も注意が必要である。

source list

次に directive の value について説明する。これは source list と呼ばれている。

CSP ソース値 - HTTP | MDN

source list には大きく 6 つの種類がある。

  1. keyword
    • 'self''none' が該当する。'self' は自身と同じ origin のみを許可し、'none' は何も許可しないことを示す。
  2. serialized URL
    • https://example.com などのことで、指定された URL を許可する。
  3. scheme
    • https のようなスキームで指定する。
  4. host
    • こちらはスキームに関係なく example.com などのようにホストで指定する。
  5. nonce
    • 'nonce-ch4hvvbHDpv7xCSvXCs3BrNggHdTzxUA' といったような 'nonce-' で始まる文字列で指定する。
  6. hash
    • 'sha256-abcd...' といった hash で指定する。

実際に動かしてみる

サンプルアプリケーションを用意して動かしてみる。アプリケーションは Angular で作成した。 初期状態の Angular アプリケーションに以下の設定を追加する。

<head>
  ....
  <meta http-equiv="Content-Security-Policy" content="default-src 'self';" />
</head>

初期状態のアプリケーションと meta 要素を追加したあとのアプリケーションの様子をそれぞれ比較する。

初期状態のアプリケーション meta 要素を追加したときのアプリケーション

スタイルが大きく崩れているのがわかる。初期状態のアプリケーションはインラインスタイルで書かれたコードがある。 default-src 'self' はインラインスタイルも制限しているためエラーになっている。

インラインスタイルを許可するためには style-src 'self' 'unsafe-inline' を指定するとよい。この directive によってアプリケーションの見た目がもとに戻ることを確認できる。

しかし、これには問題がある。'unsafe-inline' を使って許可をすると、開発者が意図的に書いたインラインスタイルなのか、外部から意図せず埋め込まれたインラインスタイルなのか判断ができない。 意図したインラインリソースのみを許可する場合は nonce を使う必要がある。

Angular には nonce を設定するために ngCspNonce 属性が用意されている。ngCspNonce を使って次のように書き換える。

<head>
  ....
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'nonce-randomNonce'" />
</head>
<body>
  <app-root ngCspNonce="randomNonce"></app-root>
</body>

これで意図したインラインスタイルのみを許可することができる。実運用ではこの nonce 値は推測されない値を使うのがよい。

他にも Angular Material を使うときなどは link 要素で読み込んだり、内部的にフォントが必要になったりするため style-src-elemfont-src directive を使って必要なものを許可していくことになる。

レポート機能

CSP にはポリシーに違反があった場合にレポーティングする機能がある。これには Reporting directive である report-to を使用する。また違反があったときに制御はせずに、レポートだけをする Content-Security-Policy-Report-Only ヘッダーも存在する。

しかし、レポート機能は meta 要素では使用できないため注意が必要である。

まとめ

CSP を始めるうえで基礎的なことを調べてまとめた。より堅牢なアプリケーションの開発を意識していきたい。

関連リンク