CSPとは
コンテンツセキュリティポリシーは簡単に説明するとクロスサイトスクリプティング (XSS) やデータインジェクション攻撃などのような、特定の種類の攻撃を緩和するためのセキュリティレイヤーでHTTPレスポンスヘッダーに指定して利用します。
CSPの目標
2021年6月29日W3Cワーキングドラフトによって以下のように定義されています。
- 開発者が細かく制御することでコンテンツインジェクション攻撃のリスクを軽減する
- 特定のリソースを埋め込むことができるオリジンを開発者が細かに制御できるようにし、コンテキストにリソースを埋め込む必要がある悪意のある攻撃のリスクを軽減する
- 開発者がアプリケーションの特権を減らすことを可能にするポリシーフレームワークを提供する
- 開発者が実際に悪用されている欠陥を検出できるようにするレポートメカニズムを提供する
CSPの設定方法
HTTPレスポンスヘッダーに以下のように設定すれば利用できます。
Content-Security-Policy: policy
上記のpolicyには本設定を行うサイトが適用したいCSPを表すディレクティブから構成される文字列を指定します。
他にもExpressでは直接HTTPレスポンスヘッダーを指定するのではなく、express-helmetというパッケージを利用する方法があります。
helmetjs/helmet: Help secure Express apps with ... - GitHub
CSPのディレクティブ
ディレクティブとはコンピュータプログラムのソースコードに記述される要素の一つで、そのコードを解釈・変換するソフトウェア(コンパイラやプリプロセッサなど)への指示や指定などを与えるためのもの。
要するにCSPにおいてはCSPの内容を指定するための要素の一つということです。
例えば、具体的なディレクティブの指定方法は下記のように記述します。
- HTTPヘッダー
Content-Security-Policy:[ディレクティブ名] [ソース][ソース]; [ディレクティブ] [ソース]; ...
Content-Security-Policy:img-src 'self' https://example.com/
- metaタグ
<meta http-equiv="Content-Security-Policy" content="[ディレクティブ] [ソース] [ソース]; [ディレクティブ] [ソース] [ソース]; ...">
<meta http-equiv="Content-Security-Policy" content="img-src 'self' https://example.com/">
上記の指定方法では設定されたサイト自身のオリジンと*https://example.com/*からの画像やfaviconのみがコンテンツとして有効となり、それ以外のオリジンからはコンテンツとして受け付けないようになります。
デフォルトポリシー
デフォルトではポリシーにより以下の項目が制限されます。
- data: URLでのコンテンツの埋め込み
- *<style>*要素でのCSSの設定
- インラインのstyle属性
- *<script>*要素内のインラインスクリプト
- onclickなどのインラインのイベント属性
- <div onClick="console.log('click')">Click</div>
- *<a href=’javascript:’>*のようなhtmlへのjavascriptの埋め込み
-
eval()やFunction()
- eval("console.log('output');");
- const output = new Function("console.log('output');");
- *setInterval()やsetTimeout()*内の文字列リテラルを渡したメソッド
- window.setTimeout("alert("Hello World!")", 500)
フェッチディレクティブ
フェッチディレクティブは、特定のリソース種別がロードされうる場所を制御します。
- connect-src
- scriptインターフェースによってロードされるURLを制限します。
- default-src
- 別のディレクティブに対する代替として提供します。例えば、指定されていないディレクティブには本ディレクティブで設定されたポリシーが適用されます。
- font-src
- @font-faceによってロードされるフォントに対する有効なソースを指定します。
- frame-src
- *<frame>や<iframe>*のような要素によってロードされる入れ子状のコンテンツの閲覧に対する有効なソースを指定します。
- img-src
- 画像やfaviconに対する有効なソースを定義します。
- manifest-src
- アプリケーションのマニフェストファイルに対する有効なソースを指定します。
- media-src
- <audio>や<video>、*<track>*要素によってロードするメディアに対する有効なソースを指定します。
- object-src
- *<object>や<embed>や<applet>*要素に対する有効なソースを指定します。
- prefetch-src
- 事前にフェッチされるか描画される有効なソースを指定します。
- script-src
- JavaScriptに対する有効なソースを指定します。
- script-src-elem
- JavaScriptの*<script>*要素に対する有効なソースを指定します。
- script-src-attr
- JavaScript のインラインイベントハンドラーに対する有効なソースを指定します。
- style-src
- スタイルシートに対する有効なソースを指定します。
- style-src-elem
- スタイルシートの*<style>および<link>要素にrel="stylesheet"*がついたもののに対する有効なソースを指定します。
- style-src-attr
- 個々のDOM要素に適用されるインラインスタイルの有効なソースを指定します。
- worker-src
- Worker、SharedWorker、ServiceWorkerスクリプトに対する有効なソースを指定します。
その他、ディレクティブの詳しい内容については引用元をご確認ください。
ソースの指定方法
名称 | 事例 |
---|---|
スキーム | data: https: http: blob: |
ホスト名 | example.com |
URI | https://example.com:443 |
ワイルドカード | *://*.example.com:* スキーム、ホスト名、ポートの左端のみ指定可能 |
ソースに指定可能なキーワード
キーワード | 事例 |
---|---|
'self' | 現在のオリジン |
'unsafe-inline' | インラインJavaScript及びCSSを許可 |
'unsafe-eval' | *eval()やfunction()*などの関数を許可 |
'none' | 全てを無効化(一般的には使用しない) |
デフォルトで制御されている通り、unsafe-inline及びunsafe-evalはモダンブラウザでは非推奨とされています。その代わりにnonce、もしくはhashを用います。
nonce
nonceはインラインスクリプトとインラインのイベント属性、インラインのstyle属性に対して、ホワイトリスト形式でCSPのポリシー上で許可する仕組みです。
まず、許可したいインラインスクリプトのnonce属性に、BASE64エンコードしたテキストを埋め込みます。
その後にCSPのscript-src、もしくはstyle-srcに対して*'nonce-BASE64_value'*の形式で値を設定します。
<script nonce="YWJjZGVmZw==">
// 何らかの処理
</script>
Content-Security-Policy: script-src 'nonce-YWJjZGVmZw==';
nonceはランダムな値で構わないので、randomBytes()などで生成したランダム文字列をBASE64に変換した値を利用すれば良いです。
hash
目的は先ほどのnonceと同様です。
まず、scriptタグもしくはstyleタグを除いた本体のハッシュ値を求めます。
(CSPではsha256及びsha384、sha512に対応)
その後にCSPのscript-src、もしくはstyle-srcに対して*'ハッシュアルゴリズム名-hash'*の形式で値を設定します。
Content-Security-Policy: script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=';
指定したポリシーのテスト
report-onlyモードで動作させることで指定したポリシーを検証することができます。このモードではポリシーによるコンテンツブロックは行われず、何らか違反があった場合は指定したURIへ報告される。
Content-Security-Policy-Report-Only: [ディレクトリ名] [ソース]; report-uri [uri]; report-to [json-field-value];
Content-Security-Policy-Report-Only: default-src https:;
report-uri https://example.com:443/csp-reports;
report-to { "group": "csp-endpoint", "max_age": 10886400, "endpoints": [{ "url": "https://example.com/csp-reports"}]},{"group": ...};
report-toが設定されている場合、report-uriは無視されるが、下位互換性を確保するために指定することが推奨されている。
ちなみにreport-onlyモードはmeta要素内ではサポートされていない。
最後に
expressのセキュリティを検討する際に調べていたところ、問題にぶち当たった箇所でしたので、出来るだけしっかりと調べてみました。意外と奥深い...
参考サイト
Content Security Policy Level 3 W3C Working Draft, 29 June 2021
Content Security Policy フェッチディレクティブの一覧 - MDN
CSP(コンテンツセキュリティポリシー)について調べてみた - techblog.securesky-tech.com