LoginSignup
9
6

More than 1 year has passed since last update.

CSPとは

Last updated at Posted at 2022-05-03

CSP(Content-Security-Policy)とは

ブラウザで動作する外部リソースやインラインソースに対するXSSやコンテンツインジェクションに対するセキュリティの仕組み。
許可された範囲外の外部リソースやインラインソースの利用をブラウザレベルで拒否する。

HTML配信時のレスポンスヘッダーに、Content-Security-Policyを配信することで動作する。

Content-Security-Policyでは、ディレクティブと呼ばれるリソースの利用を制限する命令によって実現する。

ディレクティブは、階層構造を持っており、上位は下位が存在しないときに採用される。

以降は、https://w3c.github.io/webappsec-csp を参照した結果を書いていく。

用語

ディレクティブ

命令

インラインソース

HTML上に記述されるソース。

JavaScriptの例

<script>
console.log('aaaa');
</script>

StyleSheetの例

<div style='font-weight: bold;'>aaa</div>

ディレクティブのフォールバック関係

下から宣言有無を確認し、無い場合、上流へ参照していく仕組みとその関係。

image.png

両方ある場合は下流が優先する。継承のような考え方はない。
部分的に適用も可能。
例えば、script-srcのみを制限して、img-src, style-srcはインラインソース、外部ソースの範囲に対して制限を設けない等。
このため、default-srcの指定を行うと、実質すべてのディレクティブの宣言になるため、影響が大きい。

ディレクティブの記述方法

ヘッダー名をContent-Security-Policyとし、
値を以下のように記載する。

Content-Security-Policy: [<ディレクティブ> [<ソース>]]
※[]は繰り返し可能な範囲を指す。

各ディレクティブの機能

Fetch Directives

ディレクティブ名 概要 value
child-src Documentに記載されたiframescriptタグ内に記載していた外部Workerの読み込みを指定範囲のみに制限する。 serialized-source-list
connect-src fetch,XHR,EventSource,sendBeacon,aタグのping,WebSocketを対象に接続先を制限。 serialized-source-list
default-src すべての未定義時のフォールバック先 serialized-source-list
font-src 外部フォントの読み込みを制限。 serialized-source-list
frame-src Nestされたコンテンツ(iframe)を読み込む際の制限。 serialized-source-list
img-src imgの読み込み制限。 serialized-source-list
manifest-src WebApplicationManifest(<link>)の読み込み制限。 serialized-source-list
media-src <audio>,<video>の読み込み制限。 serialized-source-list
object-src プラグインコンテンツ(object,embed)の読み込み制限。 serialized-source-list
prefetch-src <link rel="prefetch" .../>による先読み制限。 serialized-source-list
script-src-elem scriptタグのsrcやインラインスクリプトを対象。
onclickのようなイベントハンドラーは対象外
unsafe-evalを使えない。
worker-srcのフォールバックにならない。
serialized-source-list
script-src-attr イベントハンドラー(onlick,onchange...)にのみ有効なscript-src serialized-source-list
style-src-elem インラインスタイル以外を対象 serialized-source-list
style-src-attr インラインスタイルのみ対象 serialized-source-list
worker-src WebWorkerのソースを対象。 serialized-source-list

Other Directives

ディレクティブ名 概要 value
webrtc WebRTCを介して接続を確立できるかどうかを制限します。 'allow'
'block'

Document Directives

ディレクティブ名 概要 value
base-uri ドキュメントのBase-URIを制御する。 serialized-source-list
sandbox sandbox属性を持つiframeタグにおいて、許容するsandbox属性のトークンを指定する。 "" / token *( required-ascii-whitespace token )

Navigation Directives

ディレクティブ名 概要 value
form-action フォーム送信のターゲットとして許容できるものを指定する。 serialized-source-list
frame-ancestors <frame>,<embed>,<object>,<iframe>などのフレーム表示タグにおける表示許容ソースを指定する。 ancestor-source-list=ancestor-source *(ancestor-source)
ancestor-source= scheme-source / host-source / "'self'"
navigate-to Navigate先の制限。 serialized-source-list

Reporting Directives

ディレクティブ名 概要 value
report-uri CSP違反時の通知先 uri-reference *( required-ascii-whitespace uri-reference )
report-to レポートグループを指す。 token

sourceとは

許容するリソースを指す。
指定方法は以下のとおり。

source 概要 記述方法
self 配信元オリジンからのものを許可。 'self'
none 一切許可しない 'none'
unsafe-inline インラインソースを許容する。
<script>
console.(123)
</script>
'unsafe-inline'
unsafe-eval 文字列をソースとしたスクリプト実行を許容する。いわゆるevalを許容する。 'unsafe-eval'
strict-dynamic nonceと併用。許容したソースから派生して追加読み込みされたソースを許容する。インラインソースも許容。 'strict-dynamic'
unsafe-hashes unsafe-inlineよりセキュリティレベルの高い施策。外部化できないようなイベントハンドラーのハッシュを列挙することで対応する。hash-sourceと併用する。 'unsafe-hashes'
report-sample 違反しているコードのサンプルをレポートに含める。 'report-sample'
unsafe-allow-redirects navigate-toでリストされたリダイレクト先以外も許容する? 'unsafe-allow-redirects'
wasm-unsafe-eval WASM関連の以下を許可する。
new WebAssembly.Module()
WebAssembly.compile()
WebAssembly.compileStreaming()
WebAssembly.instantiate()
WebAssembly.instantiateStreaming()
'wasm-unsafe-eval'
host-source オリジン一致、ファイル名まで一致 https://example.com
https://example.con/js/static.js
scheme-source URIスキームレベルで許容する。 http:
https:
domain ドメイン、サブドメイン example.com
*.example.com
nonce-source ページリクエスト単位で発行したノンス、ナンス値。not only once値。インラインソースも許容。 nonce-{base64エンコードされたランダムな値}
hash-source ハッシュ値。SHA-256,384,512。 sha256-{ソースを左記計算法で計算したハッシュ値}
sha384-{ソースを左記計算法で計算したハッシュ値}
sha512-{ソースを左記計算法で計算したハッシュ値}

実際

注目株や分かりづらいものを動作確認していく。

  • 'strict-dynamic'ソース
  • navigate-toディレクティブ
  • 'unsafe-allow-redirects'ソース
  • 'script-src-elem'ディレクティブ
  • 'script-src-attr'ディレクティブ

環境

name version
OS Ubuntu 20.04.3 LTS
Server express(NodeJS) 4.16.1
Template engine pug(NodeJS) 3.0.2

'strict-dynamic'ソース

検証内容

  • インラインスクリプトAをnonceで許容
  • Aにて、動的にインラインスクリプトBをHTMLへ追記

HTML

<!DOCTYPE html>
<html>
<head>
    <title>csp-test-page</title>
    <link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body><h1>csp-test-page</h1>
<p>Welcome to csp-test-page</p>
<p>Server Date Tue May 03 2022 23:48:15 GMT+0900 (日本標準時)</p>
<script nonce="297f55f3-1211-4957-9d11-c4e54cf42bb6">console.log("inline script readed")
const externalScript = document.createElement("script")
externalScript.innerHTML = "console.log('inner script')"
document.body.append(externalScript)</script>
<br><a href="./">index page</a></body>
</html>

初期表示及びレスポンスヘッダー

image.png

初期表示後のコンソール

image.png

補足

スクリプトBを外部リソース化して、src属性で参照する形でも同様。

navigate-toディレクティブ

まだ、どこのブラウザも未サポート(2022/5/4時点)。
image.png
参照:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/navigate-to

HTML

TODO

初期表示及びレスポンスヘッダー

TODO

初期表示後のコンソール

TODO

'unsafe-allow-redirects'ソース

navigate-toがサポートされてから検証。

HTML

TODO

初期表示及びレスポンスヘッダー

TODO

初期表示後のコンソール

TODO

'script-src-elem'ディレクティブ

script-src-elem.html
└ インラインスクリプト(nonceで許容)

インラインスクリプトにconsole.log("inline script readed")を仕込む。
以下をレスポンスヘッダにしたページを配信する。

Content-Security-Policy: script-src-elem 'strict-dynamic' 'nonce-${nonce}';

※${nonce}は、実際にはランダムな文字列。今回はUUID。

HTML(実際にサーバサイドレンダリングされたもの)

<!DOCTYPE html>
<html>
<head>
    <title>csp-test-page</title>
    <link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body><h1>csp-test-page</h1>
<p>Welcome to csp-test-page</p>
<p>Server Date Tue May 03 2022 23:22:32 GMT+0900 (日本標準時)</p>
<p>nonce is 551f9bb7-c9b8-41b9-851b-43d065785fcd</p>
<script nonce="551f9bb7-c9b8-41b9-851b-43d065785fcd">console.log("inline script readed")</script>
<br><a href="./">index page</a></body>
</html>

初期表示およびレスポンスヘッダー

image.png

初期表示後のコンソール

image.png

'script-src-attr'ディレクティブ

イベントハンドラーにconsole.log('clicked.');を仕込む。

csp-script-src-attr.html
└ buttonタグ
  ・onclickにインラインスクリプト

buttonタグには、nonce属性は無いため、nonce-sourceによる許容は不可能。
script-src-attrでは、インラインスクリプトはunsafe-hashesもしくはunsafe-inlineが必要。
unsafe-inlineについては割愛する。

unsafe-hashesは、hash-sourceと併用する。

以下をレスポンスヘッダにしたページを配信する。

Content-Security-Policy: script-src-attr 'unsafe-hashes' 'sha256-Jhvs7RdTid8e4XmWHITVrVl1z2kekJ8UP2C22dB7VxI=';

HTML

<!DOCTYPE html>
<html>
<head>
    <title>csp-test-page</title>
    <link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body><h1>csp-test-page</h1>
<p>Welcome to csp-test-page</p>
<p>Server Date Tue May 03 2022 22:50:31 GMT+0900 (日本標準時)</p>
<button onclick="console.log('clicked.');">script-src-attr-test</button>
</body>
</html>

初期表示およびレスポンスヘッダー

image.png

ボタン押下後のコンソール

image.png

導入について

SSR(Server Side Rendering)アプリケーションとCSR(Client Side Rendering)アプリケーションにおいて評価してみる。

SSR

導入の効果はある程度あり、制御も比較的容易と考えられる。
常に必要かと言えば、サイトの公開範囲(企業内に閉じたサイトなら不要、とか)やサービスの性質による(金銭等を直接扱うようなサイトであれば必須、など)。

CSR(SPA)

ハードルが高い

まず、ハードルがある。
基本的にbundleされたJSを読み込むことになるため、strict-dynamicソースを活用する方向性になると考える。しかし、ホスティングをベースとして提供されることが基本であるため、nonce-sourceをCSPとして生成するタイミングと動的にhtmlへ設定するタイミングがない。
BFF層として、index.htmlを返すようなサーバサイド処理を設ければ可能ではある。
hash-sourceも同様の問題を抱えている。

ハードルはあるのだが、CSRというかSPAはインタラクティブな処理、CDNの利用や外部APIの利用も多い。
CSPで適切なセキュリティポリシーを敷くことで防げることは多いのではないだろうか。

BFF導入にはハードルがあると思うが、認証時のトークン等についても保管場所として活用することでBFFにセキュリティ要件を満たすための役割を担わせることである程度の効率化も期待できる。

参考ページ

https://web.dev/csp/
https://auth0.com/blog/deploying-csp-in-spa/

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6