Django-Securityについて調べてみた

この記事は [Django Advent Calendar 2018](https://qiita.com/advent-calendar/2018/django) の18日目の記事です。
元々はDjangoの設計思想について書く予定で、書いてはみたものの深夜ノリで書いたせいか朝起きてみると見るも無残なクソ記事と化していたので改めて別テーマで書きました。

私の投稿記事がセキュリティ関係100%（一分の一）なので今回もセキュリティについて調査してみたところ、興味深いライブラリが見つかりましたので、それについて記述します。

#Django-Security
Django-Securityはサードパーティ製のDjango用ライブラリです。
[GitHub - sdelements/django-security: A collection of models, views, middlewares, and forms to help secure a Django project.](https://github.com/sdelements/django-security
)

Security Compassというソフトウェアセキュリティ会社の方々が作成されたようで、2011年から今現在（2018年）もなおメンテが行われています。

#Requirements (Version 0.9.8)
Python >= 2.7
Django >= 1.8

#License
git-hub上でのライセンスタグ付けは行われていませんでしたが、見たところいわゆる四条項BSDライセンス (4-clause BSD license) のようです。
>1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
   must display the following acknowledgement:
   This product includes software developed by SD Elements.
4. Neither the name of SD Elements nor the names of its contributors may be
   used to endorse or promote products derived from this software without
   specific prior written permission.

制作物には謝辞を入れる必要があるので注意しましょう。


#MiddleWare
##ContentNoSniff
**ブラウザが自動でMIMEタイプを判別する機能を使えないようにします。（推奨）**

ブラウザのMIMEタイプを自動判別する機能は、実際のMIMEタイプとは異なる判別する場合がありXSSの脆弱性をはらみます。この対策としてはブラウザに自動判別させずに、厳密に開発者がMIMEタイプを指定することで対応することが可能です。

ただし、この機能はDjango1.8以上で標準搭載されています。（デフォルトはFalseです）
SECURE_CONTENT_TYPE_NOSNIFFを変更することで機能の利用切り替えが行えます。

[設定 | Django documentation | Django](https://docs.djangoproject.com/ja/2.1/ref/settings/#secure-content-type-nosniff)

##ContentSecurityPolicyMiddleware
**CSP（ContentSecurityPolicy）をHTTPレスポンスに追加します。（推奨、設定に要注意）**

CSPを明示することで、こちらで用意したホワイトリストに記載しているプロトコル・ドメイン**以外**から受信したスクリプトを遮断することが可能となります。

##DoNotTrackMiddleware
**ユーザーの利用ブラウザについてDoNotTrack設定を読み取り、アプリケーションに渡します。（推奨、viewsやtemplate内で実装の必要あり）**

DoNotTrackは、自分の履歴を読み取られたくないユーザーがブラウザで設定するためのものですが、この設定自体に拘束力はなく、どうするかはアプリケーションの開発者次第となります。
例えば、アプリケーション内でユーザーの検索結果に応じたリスティング広告を配信している場合、DoNotTrackを設定しているユーザーには別途静的なコンテンツを提供することで優しい世界が生まれます。:earth_africa:


##LoginRequiredMiddleware
**サイト中のホワイトリストに記載されていないいくつかのページにユーザーがアクセスする場合、認証を必要とするようにします。**

この機能については通常のログイン・ログアウト処理と何が違うのかよく分かりませんでしたが、ログアウト時の処理をカスタマイズする機能も持っているようです。
通常のログイン・ログアウト以外にも複雑な認証を伴わせたい場合は役立つのかもしれません。


##MandatoryPasswordChangeMiddleware
**ユーザーのパスワードが期限切れの場合、該当ユーザーをパスワード変更フォームへリダイレクトさせます。**

パスワードが期限切れでもアクセスできるURLリストを利用することも可能です。

##NoConfidentialCachingMiddleware
**機密ページにNo-Cashe, No-Storeヘッダを追加します。**

今の時代、「CDN!JAMstack!Cache!」というようにキャッシュを利用する流れが強いですが、個人情報が表示されるページについてはキャッシュすべきではありません。
去年メルカリでもCDNに個人情報をキャッシュさせてしまい、別のユーザーに個人情報を公開してしまう問題が発生していました。
[CDN切り替え作業における、Web版メルカリの個人情報流出の原因につきまして - Mercari Engineering Blog](https://tech.mercari.com/entry/2017/06/22/204500)
ただし、上記メルカリの問題はブラウザではなくCDNの仕様に関する問題でしたので、この限りではありません。

基本的にはNo-Cashe, No-Storeヘッダを追加することで基本的にブラウザ側でキャッシュを行わないようにできるはずですが、このあたりはブラウザによって解釈が違うため、念のため

```html
Cache-Control: no-cache, no-store, must-revalidate
```

とするのがよさそうです。

正しく設定をすればどうにかなることではありますが、CDNで取り扱うページは誰が見ても問題がないようなページとするのが良いでしょう。
（もはやDjango-Security関係ねぇ :smoking: ）


##P3PPolicyMiddleware
**P3PポリシーをHTTPヘッダに指定属性のP3Pポリシーを追加します。**

P3Pは廃止されているので気にせず次へ。


##SessionExpiryPolicyMiddleware
**ブラウザを閉じたときやcookieに保存した有効期限に達した際にセッションを失効させます。**

JavaScriptを書かない人にはいいかもしれないですね。

##StrictTransportSecurityMiddleware
**SSL/TLSでの通信を強制し、plaintext fall-backを利用不可にします。（SSL/TLSサイトに推奨）**


Django1.8以上で標準搭載されている機能です。（デフォルトは0）
SECURE_HSTS_SECONDSを変更することでStrict-Transport-Securityレスポンスヘッダが追加されます。SECURE_HSTS_SECONDSの値がStrict-Transport-Securityのmax-ageとなります。
[設定 | Django documentation | Django](https://docs.djangoproject.com/ja/2.1/ref/settings/#secure-hsts-seconds)

plaintext fall-backの意味が分からなかったので、どなたかよければコメント欄から教えてください:raised_hand_tone2:


##XFrameOptionsMiddleware
**ウェブサイトのframingを無効化し、ClickJacking攻撃を緩和します。（推奨）**

ClickJackingは透明なframeやiframeタグを埋め込んでサイト本来のリンクの上に透明なリンクを被せて悪意のあるサイトへ遷移させるような攻撃手法を指します。
このミドルウェアではHTTPレスポンスにX-Frame-Optionsヘッダを追加することで、表示するframingを制御することができます。

ただしこの機能についてもDjango2.1ではミドルウェア（django.middleware.clickjacking.XFrameOptionsMiddleware）が標準で用意されています。
[クリックジャッキング対策 | Django documentation | Django](https://docs.djangoproject.com/ja/2.1/ref/clickjacking/)


##XssProtectMiddleware
**ブラウザにXSS対策を強制します。（推奨）**

HTTPヘッダにX-XSS-Protectionを追加することで、XSSフィルタリングを有効化します。ただし、現在のブラウザはインラインのJavaScriptを抑制することに強いため、この対策はCSPに対応していない古いブラウザ（例えばIE11）のためのものです。:blush:


#おわりに
古いライブラリなので機能としてはいくつかDjangoに搭載済のものもありましたが、ライブラリ開発者の観点からセキュリティを見ることは興味深く勉強になりました。
ついこの間はPayPayでクレジットカード不正利用の問題もありましたので、（あちらはBrute-force attackだったので今回の内容とは直接関係ありませんが）色々な観点からセキュリティを見ていくとよさそうです。