Djangoの脆弱性CVE-2019–3498について解説

明けましておめでとうございます。筒井@ryu22eです。今年もよろしくお願いします。
さて、年明け早々に何ですが、今回の記事はDjangoの脆弱性の話です。2019年1月4日のリリースで修正されたCVE-2019–3498について解説します。

Django公式サイトのリリース記事については以下を参照してください。
Django security releases issued: 2.1.5, 2.0.10, and 1.11.18 | Weblog | Django

影響を受けるバージョン

以下のバージョンが影響を受けます。

  • Django master branch
  • Django 1.11
  • Django 2.0
  • Django 2.1

以下のバージョンで修正されています。

  • Django 1.11.18
  • Django 2.0.10
  • Django 2.1.5

脆弱性の内容

handler404 がデフォルト値のdjango.views.defaults.page_not_found()の場合、404ページに攻撃者が偽装したコンテンツを挿入することができます。
自分で書いたビューを指定している場合は、「 request.pathをURLエンコードせずにテンプレート上で表示させる」というコードになっていないか注意してください。
判断が難しければ、後述の「脆弱性を利用した攻撃の例」を参考に検証してください。
該当する場合は、後述の「どのように修正されたか」を参考に自分でコードを修正してください。

脆弱性を利用した攻撃の例

実際にサンプルコードを作って試してみましょう。この記事では Django==1.11.17 を使っています。

まず、django-admin startproject でプロジェクトを作成します。
ローカル環境でもデフォルトの404ページを表示させるために、 DEBUG には FalseALLOWED_HOSTS には ['*'] を設定してください。あとは ./manage.py migrate && ./manage.py runserver で開発サーバーを立ち上げます。

404ページは以下のように表示されるはずです。

URLの部分をちょっと工夫すると、以下のように表示させることができます。あまりいい例文が思いつかなかったのですが、 %20 をスペースとして表示できるので、工夫次第で自然な文章が作れそうです。

どのように修正されたか

デフォルトの404ページに「URLの一部が画面上に表示される」という仕様が廃止されました。先述のサンプルコードをDjango 1.11.18で動かしてみると、以下のように表示されます。

また、表示に使われていたコンテキスト変数 request_pathrequest.pathの内容が入っている)はURLエンコードされるようになり、 %20はそのまま %20として表示されるようになりました。
もし、テンプレートファイルだけ入れ替えて request_pathを使うようにしても、偽装コンテンツを挿入されることはありません。
実際のコードは以下のリンクを参照してください。