2026年11月3日、PythonのWebフレームワークDjangoのセキュリティリリースとして、6.1.3、6.0.10、5.2.19の3つが公開されました。10月の6.1.2に続く、およそ1か月ぶりのセキュリティ修正です。
6.1.3では、深刻度が中程度の問題が1件、低い問題が5件、あわせて6件の脆弱性が直されています。その中には、キャッシュの仕組みが原因で、別の人に向けたページが見えてしまうおそれのある問題も含まれていました。
あわせて、6.1.3はPython 3.15にも対応しました。今回は、何が直ったのかを、6.1.2と6.1.3で同じアプリを動かしながら見ていきます。
まずは何が公開されたのかを整理しよう¶
最初に、公開された3つの版を表にまとめます。内容は、Djangoの公式リポジトリにある、それぞれの版のリリースノートにもとづいています。
| 版 | 直った脆弱性 | そのほか |
|---|---|---|
| 6.1.3 | 6件(中程度1件、低5件) | Python 3.15に対応、不具合の修正 |
| 6.0.10 | 6件(中程度1件、低5件) | - |
| 5.2.19 | 5件(中程度1件、低4件) | - |
PyPIの記録によると、3つとも11月3日に配布が始まっています。6.1系が必要とするPythonは、3.12以上です。
前回、10月に公開されたセキュリティリリースについては、こちらで紹介しました。【関連記事】Djangoが4件の脆弱性を修正。6.1.2・6.0.9・5.2.18のセキュリティリリース
深刻度とは¶
リリースノートでは、それぞれの問題に、Djangoのセキュリティ方針にもとづく深刻度が付けられています。今回は、中程度を表すmoderateと、低いを表すlowの2種類です。
深刻度が低いからといって、放っておいてよいわけではありません。自分のアプリの使い方によっては、大きな影響が出ることもあります。
直った6件の脆弱性¶
続いて、6.1.3で直った6件を表にまとめます。番号は、脆弱性を識別するためのCVE番号です。
| CVE番号 | 対象 | 深刻度 | 中身 |
|---|---|---|---|
| CVE-2026-108147 | ファイルのアップロード | 低 | base64で送られたファイルを、上限を無視してメモリに溜めてしまう |
| CVE-2026-111211 | 地図の入力欄(GIS) | 低 | 入力された値を、そのままログに書いてしまう |
| CVE-2026-111362 | GeoJSONの読み込み(GIS) | 中程度 | 外部のURLやファイルを読みに行かされる |
| CVE-2026-113593 | キャッシュ | 低 | ヘッダーがない場合と空の場合を区別せず、別の人のページを返してしまう |
| CVE-2026-113733 | 管理画面 | 低 | 閲覧だけの権限の人に、パスワードのハッシュ値が見えてしまう |
| CVE-2026-114582 | 座標系の読み込み(GIS) | 低 | 外部のURLやファイルを読みに行かされる |
5.2.19には、このうち管理画面のパスワードのハッシュ値の問題を除く、5件の修正が入っています。GISとは、地図や位置の情報を扱うための、Djangoの追加機能django.contrib.gisのことです。
Djangoそのものの基本は、こちらで紹介しています。【関連記事】PythonのDjangoとは?
キャッシュから別の人のページが見えてしまう問題¶
ここからは、初心者にも関係しそうな問題を、詳しく見ていきましょう。まずは、キャッシュの問題です。
キャッシュとは、一度作ったページを取っておき、次に同じページを求められたときに、作り直さずに使い回す仕組みです。Djangoでは、cache_pageという仕組みで、手軽にページをキャッシュできます。
Varyヘッダーとは¶
ただし、同じURLでも、送られてきたヘッダーによって中身が変わるページもあります。たとえば、言語の設定によって、日本語のページと英語のページを出し分ける場合です。
そんなときは、Varyというヘッダーで、どのヘッダーによって中身が変わるのかを伝えます。キャッシュは、そのヘッダーの値ごとに、別々にページを取っておきます。
何が問題だったのか¶
リリースノートによると、これまでのDjangoは、Varyに書かれたヘッダーが送られてこなかった場合と、空の値で送られてきた場合に、同じキャッシュのキーを作っていました。その結果、片方に向けて取っておいたページが、もう片方に返されてしまうことがありました。
リリースノートでは、これが非公開のデータが漏れることにつながりうると説明されています。
6.1.2と6.1.3で再現してみた¶
実際に試してみましょう。X-Memberというヘッダーが送られてきたら、空でも会員向けの中身を返すページを用意しました。
from django.http import HttpResponse
from django.utils.cache import patch_vary_headers
from django.views.decorators.cache import cache_page
@cache_page(60)
def mypage(request):
# X-Memberヘッダーが送られてきたら(空でも)会員向けの中身を返す
if "X-Member" in request.headers:
body = "会員さま専用: ポイント残高 1,200pt"
else:
body = "ようこそ、ゲストさん"
res = HttpResponse(body)
patch_vary_headers(res, ["X-Member"])
return res
まず会員が空のヘッダー付きでページを開き、続いてゲストがヘッダーなしで同じページを開く、という順番で、テスト用のClientからアクセスしました。6.1.2と6.1.3を、それぞれPython 3.12の仮想環境に入れて実行した結果です。
Django 6.1.2
会員(空のヘッダー): 会員さま専用: ポイント残高 1,200pt
ゲスト(ヘッダーなし): 会員さま専用: ポイント残高 1,200pt
Django 6.1.3
会員(空のヘッダー): 会員さま専用: ポイント残高 1,200pt
ゲスト(ヘッダーなし): ようこそ、ゲストさん
6.1.2では、ゲストにも、会員向けに取っておいたページがそのまま返されてしまいました。6.1.3では、ヘッダーがない場合と空の場合が区別され、ゲストにはゲスト向けのページが正しく返されています。
この例は説明のために単純にしたものですが、実際のアプリでは、ヘッダーの値でユーザーの情報を出し分けることがあります。キャッシュを使っている方は、早めに上げておきましょう。
上げたあとに起きること¶
リリースノートによると、上げたあとは、ヘッダーなしの場合に取っておいたキャッシュが使われなくなり、次のアクセスで作り直されます。一時的にキャッシュが効かなくなるので、アクセスの多いサイトでは、少しの間サーバーの負担が増えるかもしれません。
ファイルのアップロードで、メモリを使い果たすおそれ¶
2つ目は、ファイルのアップロードの問題です。Djangoには、アップロードされたファイルを、FILE_UPLOAD_MAX_MEMORY_SIZEという大きさまではメモリに置き、それを超えたら一時ファイルに書き出す仕組みがあります。
リリースノートによると、Content-Transfer-Encodingにbase64を指定して送られたファイルは、この上限を無視して、全部メモリに溜められていました。大きなファイルを送り付けられると、メモリを使い果たすおそれがあったわけです。
フォームで受け取るデータに上限を設ける大切さは、FastAPIの記事でも紹介しました。【関連記事】FastAPIのフォームを支えるpython-multipart 0.0.33が公開
管理画面と、GISの問題¶
残りの4件も、簡単に見ておきましょう。
閲覧だけの権限の人に、パスワードのハッシュ値が見えた¶
管理画面では、モデルを見るだけの権限と、変更できる権限を分けて与えられます。リリースノートによると、見るだけの権限を持つ人に、パスワードのハッシュ値がまるごと表示されてしまうことがありました。
起きていたのは、AUTH_USER_MODELに指定していないユーザーのモデルを使っている場合などです。ハッシュ値は、パスワードを元に戻しにくい形に変えたものですが、見せてよい情報ではありません。
GISの3件¶
残りの3件は、地図を扱うGISの機能に関するものです。うち2件は、利用者が送ったデータをきっかけに、サーバーが外部のURLに通信したり、ファイルを読んだりさせられる問題でした。
このような攻撃は、サーバーサイドリクエストフォージェリ、略してSSRFと呼ばれます。もう1件は、入力された値に含まれる改行などが、そのままログに書かれてしまう問題です。
リリースノートによると、座標系の問題を完全に防ぐには、GDALという地図用のライブラリが3.9以上である必要があります。新しいシステムチェックgis.W001が、GDALが3.9より古いときに警告してくれます。
そのほかの変更¶
6.1.3には、セキュリティ以外の変更も含まれています。リリースノートから、主なものを紹介します。
| 変更 | 中身 |
|---|---|
| Python 3.15への対応 | 6.1.3から、Python 3.15に対応 |
| YAMLへの書き出し | sort_keysを渡すと落ちる、6.1での不具合を修正 |
| フォームのlegend_tag | 必須の印のクラスが、二重に付くことがある不具合を修正 |
| makemessages | Windowsで、日本語などのファイル名を処理すると落ちる不具合を修正 |
Python 3.15で、Djangoを使ったアプリを動かしたかった方には、待っていた知らせかもしれません。3.15の新機能については、こちらで紹介しています。【関連記事】Python 3.15が正式リリース
上げる前にやっておきたいこと¶
最後に、Djangoを上げる前に確かめておきたいことを整理します。
| 順番 | 確認すること |
|---|---|
| 1 | 6.1系、6.0系、5.2系のどれを使っているか |
| 2 | それぞれ6.1.3、6.0.10、5.2.19に上げる |
| 3 | cache_pageやキャッシュのミドルウェアを使っているなら、上げたあとの負担に備える |
| 4 | GISを使っているなら、GDALの版を確かめる |
今使っているDjangoの版は、python -m django --versionで確かめられます。6.1.3の仮想環境で実行すると、6.1.3と表示されました。
上げるときは、pip install --upgrade "django>=6.1.3,<6.2"のように、今の系列の中で最新版にするのが安全です。系列をまたいで上げると、セキュリティとは関係のない変更まで入ってしまうからです。
私は10年ほどエンジニアとして開発に関わってきましたが、キャッシュの設定ミスで、ログインした人の名前が別の人の画面に出てしまう事故を見たことがあります。キャッシュは速さと引き換えに、誰に何を見せるかの判断を難しくする仕組みだと、そのとき痛感しました。
まとめ¶
2026年11月3日、Djangoの6.1.3、6.0.10、5.2.19が公開されました。キャッシュから別の人に向けたページが見えてしまうおそれのある問題や、ファイルのアップロードでメモリを使い果たすおそれのある問題など、最大6件の脆弱性が直されています。
6.1.3では、Python 3.15にも対応しました。Djangoを使っている方は、自分の使っている系列の最新版へ、早めに上げておきましょう。
ここまでお読みいただきありがとうございました。