Djangoの脆弱性を調べる動機は、たいてい2つに分かれます。1つはDjango本体に公表されたCVEへの対応、もう1つは自分が書いたコードと設定に空いた穴です。両者は点検の手順も、直すべき場所も、判断の基準も別物。ここでは公式のセキュリティアーカイブを2026年8月28日時点で機械集計した数字を出発点に、本体のパッチ運用、標準で守られる範囲の境界、設定で空く穴、依存パッケージの点検までを実装の粒度で並べます。
まとめ:Djangoの脆弱性対応で先に決める4つの判断
結論を先に置きます。Djangoの脆弱性対応で最初に決めるのは、(1)どのバージョンに乗っているか、(2)セキュリティリリースの通知をどこで受けるか、(3)自作コードのどこが標準の保護から外れているか、(4)依存パッケージの点検をどの工程に置くか、の4点です。この4つを決めずに個別のCVEを追うと、対応が単発で終わります。
数字で見ると、Django本体の修正は止まりません。公式セキュリティアーカイブを集計すると、2026年は2月3日から8月4日まで7回のリリースで28件が公表されています。2025年は14件だったので、公表ペースは倍近い。つまり「一度上げたから当面は安心」という前提は成り立たず、月次で確認する運用が前提になります。
そしてもう1つ、日本語の解説記事がほぼ書いていない事実があります。ORMを使っていてもSQLインジェクションのCVEは出続けており、過去12か月で7件。ORMはSQLインジェクション対策そのものではなく、パッチを当て続けることを前提にした仕組みです。フレームワーク本体の構造についてはDjangoとMVTパターンの入門解説を前提とし、ここではセキュリティの工程だけを扱います。
Djangoのセキュリティリリース運用と追いかけるべきバージョンの範囲
個別のCVEを追う前に、リリースの出方とサポートの範囲を押さえます。ここが決まると、対応の要不要が機械的に判定できるようになります。
2026年は2月から8月まで毎月公表された修正の件数を実測する
Django公式のセキュリティアーカイブは、公表日とCVE番号を見出しにして時系列の逆順で並んでいます。2026年8月28日時点で機械集計したところ、2026年の公表は2月3日・3月3日・4月7日・5月5日・6月3日・7月7日・8月4日の7回、合計28件でした。2月から8月まで、1か月も飛ばさずに出ています。アーカイブ全体で見ると2007年以降のユニークなCVEは165件。
直近の2026年8月4日のリリースは Django 6.0.8 と 5.2.17 で、4件が修正されました。深刻度 high が1件(CVE-2026-15307・空間ルックアップ経由のサーバー側ファイル書き込みとリクエスト偽造)、moderate が2件(CVE-2026-15830・入れ子のジオメトリコレクションによるDoS、CVE-2026-15920・管理画面のURLField値によるXSS)、low が1件(CVE-2026-15337・言語コードのDoS)という内訳です。
ここで見落とされがちなのは、内容の偏りです。件数の多くはDoSと情報露出で、一部はGIS関連に集中しています。空間ルックアップやジオメトリ処理に関わるものは、django.contrib.gisを使っていないプロジェクトには影響しません。件数に驚くのではなく、自分の構成に該当するかを切り分ける癖をつけてください。
セキュリティサポートの対象と4.2 LTSが期限切れになった現在地
Django公式のポリシーでは、セキュリティ修正が入るのはmain開発ブランチ、直近2つのリリースシリーズ、そしてLTSリリースだけです。それ以外への遡及修正は行われません。2026年8月28日時点の公式ダウンロードページの表を整理すると、次のようになります。
| リリース系列 | 最新リリース | 拡張サポート終了 |
|---|---|---|
| 5.2 LTS | 5.2.17 | 2028年4月 |
| 6.0 | 6.0.8 | 2027年4月 |
| 6.1 | 6.1 | 2027年12月 |
| 4.2 LTS | 4.2.30 | 2026年4月7日で終了 |
重い意味を持つのは最終行です。4.2 LTSは2026年4月7日に拡張サポートが終わり、公式ページの「セキュリティ更新もバグ修正も受けない」という区分へ移りました。4.2で止まっている環境は、それ以降に公表された20件近い修正がどれも当たっていない状態になります。CVEを1件ずつ見て判断する段階ではなく、移行そのものが対応です。
事前通知の7日とdjango-announceで修正を先に知る導線
Djangoのセキュリティリリースは抜き打ちではありません。公式のセキュリティポリシーでは、リリースのおよそ7日前に django-announce メーリングリストへ、リリース日時と深刻度が事前に告知されます。同じタイミングで提携するディストリビュータやベンダーにはPGP署名付きで詳細が通知される仕組み。実際に悪用が確認されている場合には、この7日が短縮されることもあります。
運用に落とすなら、購読先は3つで足ります。django-announceで予告を受け、公式ブログでリリース当日の詳細を確認し、依存パッケージ側はGitHubのセキュリティアラートで受ける。事前告知の時点で深刻度がhighと分かれば、リリース日に作業枠を空けておけます。逆に low だけなら、次の定例リリース作業まで待つ判断ができる。この「先に知って枠を取る」動きができるかどうかで、緊急対応の回数が変わります。
Djangoが標準で防ぐ範囲と、その前提が崩れる書き方の見分け方
Djangoは主要な攻撃への対策を標準で持っています。問題は、その保護が効かなくなる書き方がどれも「よく書かれる形」だという点です。
テンプレートの自動エスケープが素通りする3つの書き方とその条件
Djangoテンプレートは変数の出力時にHTMLの特殊文字を自動エスケープします。ただし公式ドキュメント自身が万能ではないと書いており、外れる経路は主に3つ。1つ目は safe フィルタと autoescape off ブロックで、明示的にエスケープを止める書き方です。2つ目は mark_safe() で、Python側で組み立てた文字列を安全だと宣言してテンプレートへ渡す形。3つ目が見落とされやすく、属性値をクォートで囲まずに変数を埋めた場合です。
特にリスクが高いのは、管理画面やCMS的な入力欄でHTMLの入力を許している場合。保存された文字列を safe で出せば、保存した側の権限がそのまま実行権限になります。JavaScriptへ値を渡すときは文字列連結ではなく json_script フィルタを使い、エスケープを止める場所はレビューで数えられる程度に絞り込んでください。json_scriptの出力とJS側での受け取り方はDjangoテンプレートの書き方:継承とコンテキスト・JSへの値渡しで解説しています。言語に依存しない実装原則はセキュアコーディングの10原則と主要脆弱性のコード対策にまとめています。
CsrfViewMiddlewareの守備範囲とcsrf_exemptを外す条件
CSRF対策は CsrfViewMiddleware が担当し、POSTなど状態を変えるリクエストでトークンを検証します。HTTPS環境では Origin と Referer ヘッダの照合も加わる仕組み。ここが崩れるのは csrf_exempt を付けたときで、外部サービスからのWebhook受信で付けたまま放置される例が多く見られます。Webhookなら署名検証を必ず併用し、CSRF検証を外した理由をコード上のコメントで残してください。
もう1つの穴はサブドメインです。公式は、制御していないサブドメインが存在する場合にCSRF保護が限定的になると明記しています。ユーザーごとにサブドメインを払い出すサービスでは、Cookieのドメイン指定を広げすぎない設計が要ります。CSRF_COOKIE_SECURE の既定値は False なので、HTTPSで運用するなら明示的に True にする。フォーム送信でCSRF検証に失敗するときの切り分けはInvalid CSRF tokenの意味と直し方で扱っています。
ORMのパラメータ化が届かないrawとextraの扱いの線引き
QuerySetが生成するSQLは、値がプレースホルダとして渡されるためSQLインジェクションを受けません。公式が開発者の責任として挙げるのは raw()、extra()、RawSQL の3つです。この3つでは、パラメータをPython側の文字列フォーマットで埋め込んだ瞬間に穴が空きます。
実務での線引きはこうです。値のバインドは必ずドライバへ任せ、params 引数を使う。テーブル名や列名のようにプレースホルダで渡せない箇所にユーザー入力を混ぜるのは避け、必要なら許可リストで突き合わせます。order_by に画面から来た文字列をそのまま渡す実装も同種の危険がある形。生SQLへ落とす判断そのものは遅延評価と集計の順序から見るORMのクエリ設計で整理しています。
ORMを使えばSQLインジェクションは防げるという前提を実測で崩す
ここからが本記事の芯です。日本語の解説記事はORMを使えばSQLインジェクションは防げるという結論で章を締めますが、公式アーカイブの実測はそれを支持しません。
過去12か月に出たSQLインジェクションのCVEを時系列で並べる
2025年9月から2026年8月までの公式セキュリティアーカイブから、SQLインジェクションに分類される修正だけを抜き出すと7件あります。いずれも自作の生SQLではなく、ORMの内部で起きたものです。
| 公表日 | CVE | 該当箇所 |
|---|---|---|
| 2025年9月3日 | CVE-2025-57833 | FilteredRelationの列別名 |
| 2025年10月1日 | CVE-2025-59681 | annotateとaggregate |
| 2025年11月5日 | CVE-2025-64459 | QuerySetの_connector引数 |
| 2025年12月2日 | CVE-2025-13372 | PostgreSQLの列別名 |
| 2026年2月3日 | CVE-2026-1207 | PostGISのラスタ検索 |
| 2026年2月3日 | CVE-2026-1287 | 制御文字を含む列別名 |
| 2026年2月3日 | CVE-2026-1312 | order_byとFilteredRelation |
並べると傾向が見えます。7件のうち4件が列別名(カラムエイリアス)に関するもので、FilteredRelation と annotate のように、開発者が名前を付けてクエリを組み立てる箇所に集中しています。名前は値と違ってプレースホルダで渡せないため、パラメータ化の枠外に出るという構造上の理由がある。データベースごとの差も出ており、CVE-2025-59681 はMySQLとMariaDB、CVE-2025-13372 はPostgreSQL、CVE-2026-1207 はPostGISに固有でした。
ORMは防御そのものではなく前提だと置き直したときの運用の変化
この事実の意味はORMが危ないということではありません。ORMはSQLインジェクションを構造的に減らす仕組みであり、7件すべてが公式によって修正されています。変えるべきなのは位置づけです。ORMは対策の完了地点ではなく、パッチを当て続けることで初めて成立する前提だと置き直します。
置き直すと、運用の焦点が3つ動きます。第1に、コードレビューで見る対象が「生SQLを書いていないか」から「アノテーションや別名にユーザー入力由来の文字列が流れ込んでいないか」へ広がる。第2に、パッチ適用の優先度が上がります。深刻度highに分類されるのはリモートコード実行とSQLインジェクションだけなので、SQLインジェクション修正を含むリリースは即日適用の対象です。第3に、データベース製品ごとの該当判定が要ります。MySQLだけを使っているならPostGIS関連は無関係で、逆にPostGISを使っているなら該当CVEは3件に増える。
設定と構成で空く穴:check –deployが拾わない範囲
本体を上げても、設定と構成で空いた穴は塞がりません。標準の検査で拾える範囲と、その外側を分けます。
DEBUGとALLOWED_HOSTSから始めるデプロイ設定の点検順序
manage.py check --deploy は、本番向け設定の抜けを機械的に洗い出すコマンドです。検査対象は SECRET_KEY と DEBUG、環境依存の ALLOWED_HOSTS や STATIC_ROOT、そしてHTTPS関連の CSRF_COOKIE_SECURE と SESSION_COOKIE_SECURE の3系統。DEBUG の既定値は False ですが、開発用の設定ファイルを本番へ持ち込んで True のまま公開すると、例外画面にソースコード・ローカル変数・設定値が並びます。
ALLOWED_HOSTS の既定値は空リストで、DEBUG を False にしたとき空のままなら全リクエストが400で弾かれます。ここで注意したいのが迂回経路で、公式はリクエストのメタ情報から HTTP_HOST を直接読むと ALLOWED_HOSTS の検証を通らないと明記しています。ホスト名を使う処理では request.get_host() に統一してください。本番設定とロードバランサ配下でのHTTPS判定はgunicornとnginx構成を前提にしたDjangoのデプロイ手順で詳しく扱っています。
キャッシュ層に集中する情報露出とVaryヘッダの取り扱いの注意
2026年に公表された情報露出のCVEを並べると、5件がキャッシュ層に集まっています。CVE-2026-6907(Varyヘッダのアスタリスク指定の扱いの誤り)、CVE-2026-8404(Cache-Controlディレクティブの大文字小文字)、CVE-2026-35193(Vary に Authorization が欠ける問題)、CVE-2026-48587(Varyヘッダの空白パディング)、CVE-2026-48588(キャッシュされたSet-Cookie応答)です。いずれも UpdateCacheMiddleware とHTTPキャッシュのヘッダ解釈に関わります。
怖いのは症状の出方です。攻撃らしい痕跡が残らず、「他人の画面が見えた」という問い合わせで初めて表面化します。しかも check --deploy はこの系統を拾いません。設定の抜けではなく、ミドルウェアとキャッシュ制御ヘッダの相互作用だからです。サイト全体キャッシュ、あるいはCDNやリバースプロキシで応答をキャッシュしている構成なら、この5件の該当可否を個別に確認し、ログイン後のページがキャッシュ対象から外れているかも同時に見直してください。
管理画面が攻撃面になる条件と、公開範囲を絞るための具体の設定
Django管理画面は、2026年だけで3件のCVEの舞台になりました。CVE-2026-4277(GenericInlineModelAdmin の権限乱用)、CVE-2026-4292(ModelAdmin.list_editable の権限乱用)、CVE-2026-15920(URLField値のXSS)です。前者2件は権限の乱用で、変更権限を持たないはずのユーザーが編集できてしまう種類の欠陥でした。
ここから導ける設計判断は明快です。管理画面をインターネットへ素で晒す構成をやめる。URLを推測されにくい文字列に変えるだけでは対策になりません。実効性があるのは、接続元IPの制限、VPNや踏み台の内側への配置、そして多要素認証の追加です。加えてスタッフ権限の付与を絞り、list_editable のように一覧から直接書き換える機能は必要な画面だけに限定する。権限設計とModelAdminの範囲はadmin標準の範囲とModelAdminによる権限の実装で整理しています。
依存パッケージの脆弱性点検をリリース工程へ組み込む手順と判定基準
Django本体を追えても、依存パッケージが残ります。手作業で追う対象ではないので、工程に載せて自動で落とします。
pip-auditとOSV-Scannerで突き合わせる先のデータベース
Pythonの依存点検で使われるのは主に3系統です。pip-audit(2026年8月時点で2.10.1・Python 3.10以上)はPyPAのアドバイザリデータベースとPyPIのAPIに問い合わせ、インストール済み環境や requirements ファイルを対象に検査します。safety(3.8.1・Python 3.9以上)は独自のデータベースを持つ商用寄りの選択肢。OSV-ScannerはGoogleのOSV形式に集約された横断データベースを参照し、Python以外の言語も同じ手順で扱えます。
使い分けの軸は対象範囲です。Pythonだけのリポジトリなら pip-audit を継続的インテグレーションに1行足すのが最も軽い。コンテナイメージやフロントエンドの依存も見たいなら、言語横断のスキャナへ寄せます。更新の起票まで自動化するならdependabot.ymlの設定とRenovateとの違いを参照し、プルリクエストとして届く形にしてください。ツール群の位置づけと選定の考え方はSCA(ソフトウェア構成分析)の仕組みとSAST・SBOMとの違いにまとめています。
検出結果のどれでリリースを止めるかをCVSSと到達可能性で決める
スキャナを入れた直後に必ず起きるのが、検出件数の多さで運用が止まる現象です。全件を直すルールにすると、リリースが進まなくなって結局スキャン自体が無視される。落とす基準は2軸で決めてください。1つはCVSSの深刻度、もう1つは到達可能性です。
実務で機能する線引きはこうです。CVSSが9.0以上、または該当コードが本番の実行経路から到達する場合はリリースを止める。到達しない依存(開発用ツール、使っていないオプション機能)は起票して次の定例に回す。CVSSの読み方とv4.0での変更点は脆弱性の深刻度スコアCVSSの見方と計算方法で解説しています。到達可能性の判定を人手でやると属人化するため、呼び出し経路まで見るタイプのスキャナへ寄せるのが省力です。自社に判定を回す体制が無い段階なら、脆弱性診断・セキュリティ診断で外側から一度棚卸しし、恒常運用に載せる範囲を切り出す進め方もあります。
パッチ適用の判断基準:即日・週次・見送りを分ける条件の言い切り
最後に、対応の速度を条件で言い切ります。判断を毎回悩まないための線引きです。
即日で上げる条件と、次の定例まで待ってよい条件の分かれ目の判断
即日で上げるのは、深刻度highが含まれるリリースです。Djangoのポリシーでhighに分類されるのはリモートコード実行とSQLインジェクションだけなので、判定は迷いません。パッチリリースは同一フィーチャーリリース内で100%互換だと公式が明言しており、6.0.7から6.0.8のような上げ方でアプリ側の変更は原則不要。テストを流して当日中に出せます。
moderate は週次の定例で足ります。ただしXSSとCSRFは、外部に公開している画面が該当機能を使っているかで前倒しを判断する。lowは次の定例で構いませんが、DoS系は公開APIを持つサービスだと実害の距離が近いため、レート制限の有無で1段階上げてください。
見送ってよいのは、該当機能を使っていないと確認できたときだけです。django.contrib.gis を入れていないのにGIS関連のCVEを追う必要はありません。ただし推測で外すと後で刺さるため、判定の根拠はチケットに1行残す。次に同じ系統が出たときの判断が数分で済みます。
4.2に残っている場合と6.1へ上げる場合の移行コストの見積もり
4.2 LTSに残っている環境は、パッチ適用の判断以前の状態です。拡張サポートが2026年4月7日で終わっているため、以降のCVEはどれも修正が届きません。移行先は5.2 LTS(拡張サポート2028年4月まで)が現実解で、4.2から5.2への差分は2つのフィーチャーリリースぶん。作業量が読める場所は、削除された設定名(STATICFILES_STORAGE と DEFAULT_FILE_STORAGE は5.1で削除)、非推奨警告の解消、依存パッケージ側のDjango対応バージョンの3つです。中間リリースを一段ずつ踏む具体的な順序とPythonとの対応表はDjangoのEOLとサポート期限一覧にまとめています。
最新の6.1へ一気に上げる判断は、Pythonのバージョンで決まります。6.1はPython 3.12以上を要求するため、実行環境のPythonが古いなら、Django移行の前にランタイム更新が入ります。逆にPython 3.12以上へ既に乗っているなら、6.1は拡張サポートが2027年12月までと短い点を踏まえ、次のLTSである6.2(2027年4月・拡張サポート2030年4月まで)までの中継ぎとして扱うのが素直です。
長期の運用計画を立てるなら、2028年からのバージョン方針の変更も織り込んでください。表記がYYYY形式になり毎年1月リリースへ変わり、すべてのフィーチャーリリースが3年サポートを受けます。LTSだけを狙って3年ごとに大きく上げる運用は2028年で終わり、年1回の更新をリズムにする形へ寄っていきます。
よくある質問
検索の多い5つの質問に、実測値と条件で答えます。
Djangoは脆弱性が多いフレームワークですか?
件数だけを見ると2026年は7回のリリースで28件と多く見えますが、これは開発チームが自ら見つけて公表している数です。報告窓口・90日の解決目標・7日前の事前通知という運用が明文化されており、公表の頻度は放置されている状態より安全な兆候として読んでください。判断すべきは件数ではなく、自分の構成が該当するかどうかです。
Django 4.2をまだ使っていますが、そのままでも問題ありませんか?
問題があります。4.2 LTSは2026年4月7日に拡張サポートが終了し、公式ページでセキュリティ更新もバグ修正も受けない区分に移りました。それ以降に公表されたCVEはどれも修正が届きません。移行先は5.2 LTS(拡張サポート2028年4月まで)が第一候補で、削除された設定名の置き換えと依存パッケージの対応確認から着手します。
ORMだけを使っていればSQLインジェクションは防げますか?
防げません。過去12か月だけでORM内部に起因するSQLインジェクションのCVEが7件出ており、4件は列別名(FilteredRelation や annotate)に関わるものでした。ORMは危険を大きく減らす仕組みですが、パッチを当て続けて初めて成立します。加えて raw()・extra()・RawSQL は開発者側の責任範囲なので、値のバインドは params 引数へ任せてください。
セキュリティリリースの情報はどこで受け取ればよいですか?
公式の django-announce メーリングリストが起点です。リリースのおよそ7日前に、日時と深刻度が事前告知されます。当日の詳細はDjango公式ブログとセキュリティアーカイブで確認でき、依存パッケージ側はGitHubのセキュリティアラートやDependabotで受け取る形。事前告知の段階で深刻度highと分かれば、リリース日に作業枠を確保できます。
脆弱性診断はDjangoアプリにも必要ですか?
フレームワークの標準機能では守れない層があるため、必要です。標準で守られるのはXSS・CSRF・SQLインジェクションといった典型的な経路で、権限設計の誤り、業務ロジックの抜け、他人のデータが見える種類の不備は検出できません。前述のキャッシュ層の情報露出のように check --deploy が拾わない領域もあります。外部公開しているサービスでは、自動スキャンと診断を役割で分けて併用してください。
関連記事
- Djangoとは?Python製Webフレームワークの特徴・MVT・始め方を入門解説:前提となる本体の構造
- Django ORMのクエリ設計|遅延評価と集計の順序・生SQLへ落とす判断:生SQLへ落とすときの線引き
- Djangoのデプロイ手順:gunicorn+nginx構成と本番設定・配置先の選び方:本番設定とHTTPS判定の詳細
- Django管理画面の設計:admin標準の範囲とModelAdmin・権限の実装:管理画面の権限を絞る設計
- セキュアコーディングとは?10原則と主要脆弱性のコード対策を実装目線で解説:言語非依存の実装原則