Webアプリケーションでは、JavaScript、外部API、CDN、Webフォント、分析ツールなど、多数の外部リソースを利用するケースが一般的です。
便利になる一方で、外部リソースの増加は、XSS(Cross-Site Scripting)をはじめとするクライアントサイドのセキュリティリスクを高める要因にもなります。
そこで役立つのが Content Security Policy(CSP) です。
CSPを適切に設定すると、ブラウザが読み込めるスクリプトや画像、通信先などを制限でき、Webアプリケーションの防御を強化できます。
本記事では、CSPをいきなり厳格に設定するのではなく、既存サイトにも導入しやすいように、段階的な実装方法を紹介します。
CSPとは?
CSPは、ブラウザに対して
「どのリソースを読み込んでよいか」
を指定するセキュリティポリシーです。
通常はHTTPレスポンスヘッダーとして設定します。
最もシンプルな例は以下です。
Content-Security-Policy: default-src 'self';
'self' は、現在のWebサイトと同じオリジンからのリソースのみを許可する指定です。
非常に強力ですが、既存サイトにいきなり適用すると、外部CSSやJavaScriptが動かなくなる可能性があります。
そのため、実務では段階的な導入が重要です。
まずはReport-Onlyから始める
既存サイトへCSPを導入する場合、最初からリソースをブロックするのは少し危険です。
そこで利用できるのが、
Content-Security-Policy-Report-Only
です。
例えば、
Content-Security-Policy-Report-Only:
default-src 'self';
と設定すると、ポリシー違反が発生しても実際にはブロックされません。
ブラウザのDeveloper Toolsなどで、どのリソースが違反しているかを確認できます。
Report-Onlyのメリット
- 既存機能を壊しにくい
- 外部リソースの洗い出しができる
- 本番環境で影響を確認できる
- CSP導入前の調査に利用できる
既存システムでは、まずReport-Onlyで状況を把握するのがおすすめです。
よく使うCSPディレクティブ
default-src
他のディレクティブで指定されていないリソースの基本ルールです。
default-src 'self';
CSPを設計するときのベースになります。
script-src
JavaScriptの読み込み元を制限します。
script-src 'self';
外部CDNを利用する場合は、必要なドメインだけを追加します。
script-src 'self' https://cdn.example.com;
許可するドメインを必要以上に増やさないことが重要です。
style-src
CSSの読み込み元を指定します。
style-src 'self';
外部CSSを利用する場合は、対象ドメインを明示します。
style-src 'self' https://fonts.googleapis.com;
img-src
画像の読み込み元を制御します。
img-src 'self' https: data:;
CDNや外部画像サービスを利用している場合は、構成に合わせて調整します。
connect-src
fetch、XMLHttpRequest、WebSocketなどの通信先を制御します。
connect-src 'self' https://api.example.com;
SPAやリアルタイム通信を利用するWebサービスでは、特に重要なディレクティブです。
font-src
Webフォントの読み込み元を指定します。
font-src 'self' https://fonts.gstatic.com;
Google Fontsなどを利用する場合は、CSSとフォントファイルの配信元が異なることがあるため注意が必要です。
危険なunsafe-inlineをできるだけ避ける
CSPを導入すると、インラインJavaScriptがブロックされる場合があります。
例えば以下のようなコードです。
<script>
console.log("Hello");
</script>
簡単な対応として、
script-src 'self' 'unsafe-inline';
を指定することもできます。
しかし、'unsafe-inline' を許可するとCSPの防御効果が弱くなります。
可能であれば、nonce やハッシュを利用する方が安全です。
nonceを利用する
nonce は、許可されたインラインスクリプトだけを実行するための仕組みです。
HTTPヘッダー:
Content-Security-Policy:
script-src 'self' 'nonce-a8f3d91';
HTML側:
<script nonce="a8f3d91">
console.log("allowed");
</script>
一致するnonceを持つスクリプトだけが実行されます。
実際のアプリケーションでは、固定値ではなくレスポンスごとにランダムな値を生成する必要があります。
Node.jsでCSPを設定する例
Expressを利用している場合、レスポンスヘッダーとしてCSPを設定できます。
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
[
"default-src 'self'",
"script-src 'self'",
"style-src 'self'",
"img-src 'self' https: data:",
"connect-src 'self' https://api.example.com"
].join("; ")
);
next();
});
本番環境では、フレームワークやセキュリティミドルウェアを利用して一元管理すると、設定ミスを減らしやすくなります。
CSP導入時によく壊れるもの
CSPを適用した直後に、サイトの一部が動かなくなることがあります。
特によくあるのは以下です。
外部JavaScript
分析ツール、タグマネージャー、外部SDKなどがブロックされる場合があります。
Webフォント
Google Fontsなどの配信元が許可されていないと、フォントが読み込めません。
API通信
connect-src にAPIドメインが登録されていないと、通信が失敗します。
インラインスタイル
既存システムでは、HTML内に直接スタイルが書かれているケースもあります。
おすすめの導入手順
実務では以下の順番で進めると安全です。
- 現在利用している外部リソースを確認する
-
Content-Security-Policy-Report-Onlyを設定する - ブラウザコンソールで違反を確認する
- 必要なドメインだけ許可する
-
unsafe-inlineの削減を進める - nonceやhashを導入する
- 通常の
Content-Security-Policyへ切り替える - リリース後も定期的にポリシーを確認する
CSPは一度設定して終わりではありません。
新しいAPIや外部サービスを追加したときには、ポリシーの見直しも必要になります。
デジタルプラットフォームで考えるCSP
ユーザーが継続的に操作するオンラインプラットフォームでは、多数のJavaScriptやAPI通信を利用することがあります。
例えば、JLPH のようなデジタルプラットフォームを技術的なケースとして考えると、フロントエンドで利用するリソースを明確に管理し、必要な通信先やスクリプトだけを許可する設計は、Webセキュリティを維持するうえで重要な考え方です。
サービスの機能が増えるほど外部依存も増えやすいため、CSPを「設定ファイル」ではなく「リソース利用ポリシー」として管理すると運用しやすくなります。
CSPだけではXSS対策は完結しない
CSPは非常に有効なセキュリティ機能ですが、CSPだけですべてのXSSを防げるわけではありません。
以下の対策と組み合わせることが重要です。
- ユーザー入力の適切な検証
- HTML出力時のエスケープ
- 危険なDOM APIの利用を減らす
- 依存ライブラリを定期的に更新する
- HttpOnly / Secure / SameSite Cookieを利用する
- HTTPSを利用する
セキュリティでは、1つの対策だけに依存しない「多層防御」が基本になります。
まとめ
Content Security Policyは、Webアプリケーションで利用可能なリソースをブラウザ側で制御できる強力なセキュリティ機能です。
特に既存システムへ導入する場合は、
- Report-Onlyから始める
- 必要な外部リソースを洗い出す
- 許可リストを最小限にする
-
unsafe-inlineを減らす - nonceを活用する
- 継続的に設定を見直す
という流れがおすすめです。
CSPを単なるXSS対策としてではなく、「Webアプリケーションがどのリソースを利用できるかを管理する仕組み」として考えると、より安全で保守しやすいWebサービス設計につながります。