DjangoでのContent-Security-Policy設定方法
はじめに
年末年始の休暇に 体系的に学ぶ 安全なWebアプリケーションの作り方 という本を読みました。
その中で OWASP ZAP という脆弱性診断ツールの使い方が紹介されていました。
そこで、以前開発したサービスに対してOWASP ZAPでの脆弱性診断を行ったところ、Medium以上のリスクが一つ見つかり、それはContent-Security-Policy(CSP)のヘッダが未設定というものでした。
そのサービスはDjangoを使って開発したので、それ向けのCSP設定ツールであるDjango-CSPを導入し、見つけたリスクに対処しました。
以前開発したものは TCP Exposer というサービスで、プライベートIPアドレスしか持たないローカルサーバーに、インターネットからアクセスできるようにします。
こちらもぜひ使ってください。
Content-Security-Policy(CSP) とは
以下MDNからの抜粋です。
コンテンツセキュリティポリシー (CSP) は、クロスサイトスクリプティング (Cross-site_scripting) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するために追加できるセキュリティレイヤーです。 これらの攻撃はデータの窃取からサイトの改ざん、マルウェアの拡散に至るまで、様々な目的に用いられます。
(中略)
CSP を有効にするには、ウェブサーバーから Content-Security-Policy HTTP ヘッダーを返すように設定する必要があります(X-Content-Security-Policy ヘッダーに関する記述が時々ありますが、これは古いバージョンのものであり、今日このヘッダーを指定する必要はありません)。
サイトの管理者は、コンテンツ(JavaScript, CSS, 画像など)ごとに信頼するドメインを設定します。
ポリシーに合わないコンテンツの場合は、ダウンロードだったり、表示だったり、実行だったりをブラウザは実施しません。
これによって、WEBサイトの管理者が意図していないコンテンツを使った攻撃を防ぎます。
例えば、任意のドメインからの画像読み込みを許可し、スクリプトは信頼するドメインを許可し、それ以外のコンテンツはサイト自身のドメイン(サブドメインを除く)のみを許可したい場合は以下のように設定します。
Content-Security-Policy: default-src 'self'; img-src *; script-src foo.example.com
CSPヘッダーの説明はMDNのドキュメントを参照ください。
DjangoでのCSP設定
Django-CSPというDjango向けミドルウェアを使ってCSPを設定しました。
ドキュメントの量は多くないので、導入を検討する際には一通り目を通すと良いと思います。
Django-CSPの選定理由
選定理由は以下のとおりです。
- 開発したサービスはDjangoというフレームワークを使っていた。
- Django CSP というキーワードで検索すると、Django-CSPばかり言及されていた。
- GitHubを見るとFirefoxの開発元であるMozillaが開発している。
使い方
pipを使って以下のようにインストールします。
pip install django-csp
設定するには、Djangoプロジェクトのsettings.pyに以下のような記述をします。
import django-csp
などは不要です。
# ...
MIDDLEWARE = [
# ...
"csp.middleware.CSPMiddleware",
# ...
]
# ...
# 以下の設定は例なので、自分の環境に合わせて変更必要
CSP_DEFAULT_SRC = ("'self'", "www.google-analytics.com")
CSP_SCRIPT_SRC = ("'self'", "www.googletagmanager.com")
CSP_INCLUDE_NONCE_IN = ("script-src",)
# ポリシーの効果を監視する (ただし強制はしない) ことによりポリシーを試行する場合はTrue。デフォルトはFalse
# CSP_REPORT_ONLY = True
CSP_DEFAULT_SRCやCSP_SCRIPT_SRCはそれぞれContent-Security-Policyヘッダーのdefault-srcとscript-srcに対応します。
CSP_INCLUDE_NONCE_IN = ("script-src",)
の設定は、インラインスクリプト(<script>
タグ内に直接書くJavaScriptコード)を書く際に使用します。
例えば以下のようにnonceを設定しなければ、直接書いたスクリプトは実行されません。
<script nonce="{{request.csp_nonce}}">
var hello="world";
</script>
詳しくは公式ドキュメントを参照ください。
CSP_DEFAULT_SRCのwww.google-analytics.com、CSP_SCRIPT_SRCのwww.googletagmanager.com、CSP_INCLUDE_NONCE_INの設定はGoogleAnalyticsのために設定しました。
まとめ
自作したサービスに対して、セキュリティ向上のため、Django-CSPを使ってContent-Security-Policyを設定しました。
Discussion