SICUREZZA / DJANGO / ARCHITETTURA
Una release di sicurezza di un framework viene letta spesso in due modi: chi gestisce l’infrastruttura cerca la versione da installare, chi sviluppa scorre i CVE e prova a capire se la propria applicazione è esposta. È necessario, ma non è la parte più interessante.
Il 6 ottobre 2026 il progetto Django ha pubblicato le versioni 6.1.2, 6.0.9 e 5.2.18 per correggere quattro problemi di sicurezza: due denial of service, una request forgery legata ai raster di GeoDjango e un abuso di privilegi nei model formset con primary key modificabili. Django classifica il primo problema come low e gli altri tre come moderate.
Presi uno alla volta sembrano bug lontani tra loro. Uno riguarda codici lingua troppo lunghi, uno il parsing degli header HTTP, uno GDAL e i file VRT, uno il binding dei form. In realtà raccontano la stessa storia: l’astrazione funziona finché ricordiamo dove termina il suo confine di fiducia.
Un framework riduce la quantità di complessità che dobbiamo vedere. Non riduce automaticamente la quantità di complessità che dobbiamo proteggere.
Il primo confine è la memoria: anche una cache può diventare input
Il CVE-2026-77050 riguarda get_supported_language_variant(). Django usava i codici lingua come chiavi di una cache in memoria prima di limitarne la lunghezza. Molti valori distinti e molto lunghi potevano quindi far crescere il consumo di memoria del processo. La correzione rifiuta o tronca i codici oltre 500 caratteri prima del lookup in cache.
La lezione non è “le stringhe lunghe sono pericolose”. È che una cache trasforma un input apparentemente effimero in stato persistente, almeno per la vita del processo. Appena una porzione di input diventa una chiave, una cardinalità, un oggetto trattenuto o un elemento indicizzato, il suo costo non coincide più con il costo della singola richiesta.
Questo è un pattern che vale ben oltre Django. Il controllo della lunghezza fatto dopo la memoizzazione è tecnicamente un controllo, ma arriva nel punto sbagliato. In sicurezza l’ordine delle operazioni è parte della policy.
Il secondo confine è il parser: lineare per noi, quadratico per la CPU
Il CVE-2026-84429 è più sottile. parse_header_parameters() poteva impiegare tempo quadratico con valori costruiti usando molti separatori dentro un parametro quotato. La funzione è raggiungibile anche da richieste non autenticate attraverso header come Accept o Content-Type, per esempio durante la content negotiation di HttpRequest.accepts().
Django aveva già un limite di lunghezza per singola chiamata, ma la nota di sicurezza chiarisce un dettaglio importante: quel limite non contiene necessariamente la dimensione combinata di header ripetuti. È un esempio perfetto di una difesa localmente sensata che non descrive il costo globale dell’operazione.
La correzione sposta il parsing sull’implementazione di email.message.Message di Python. Questo può cambiare il comportamento con alcuni header malformati o insoliti. Ed è un’altra cosa da non nascondere: una patch di sicurezza può modificare semantica e compatibilità, non solo chiudere una porta invisibile.
Il terzo confine è una libreria nativa che sa fare più di quanto sembra
Il CVE-2026-87890 riguarda i lookup spaziali. Django accettava raster forniti come bytes senza richiedere che fossero esplicitamente racchiusi in GDALRaster. Quei byte venivano aperti tramite il filesystem virtuale in memoria di GDAL, ma potevano contenere un documento VRT capace di riferirsi a una sorgente raster esterna. Risultato: durante la preparazione del lookup, GDAL poteva effettuare richieste di rete con l’identità del processo Django.
Questo problema è particolarmente istruttivo perché la vulnerabilità non nasce da una chiamata HTTP esplicita scritta nell’applicazione. Nasce dal fatto che un formato dati è anche un linguaggio di descrizione, e la libreria che lo interpreta possiede capacità operative ulteriori. “Sono solo byte” è vero fino al momento in cui quei byte vengono consegnati a un parser che può aprire risorse esterne.
La patch impone che i raster forniti come byte vengano prima avvolti in GDALRaster. Django segnala che il cambiamento è backward incompatible e ricorda che gli input non fidati vanno validati. C’è anche un dettaglio storico importante: il problema era sfuggito alla correzione di CVE-2026-15307. Le release note di Django 6.1.2 parlano infatti di una correzione per una mitigazione precedente insufficiente.
Il quarto confine è il binding: un form non è un’autorizzazione
Il CVE-2026-87975 riguarda i model formset quando la primary key può essere impostata attraverso il form. Dati POST contraffatti potevano permettere di eliminare istanze fuori dal queryset limitante oppure creare istanze attraverso formset edit-only. Il caso non coinvolge i modelli che usano la primary key predefinita BigAutoField, ma configurazioni con OneToOneField, parent link, chiavi naturali o UUID inclusi nei campi del form possono rientrare nello scenario descritto.
Qui il confine perso è concettuale. Il formset conosce quali record mostrare e modificare, ma se un identificatore editabile proveniente dalla richiesta può ridefinire l’oggetto a cui l’operazione si applica, il binding dei dati finisce per interferire con il perimetro di autorizzazione.
È una distinzione che vale in qualunque stack: validare che un identificatore abbia una forma corretta non significa verificare che l’utente abbia diritto di operare sull’oggetto identificato. Il dato può essere perfettamente valido e perfettamente fuori perimetro.
Quattro bug, quattro forme dello stesso errore architetturale
Se mettiamo i quattro casi uno accanto all’altro, emerge una tassonomia più utile dell’elenco dei CVE.
- Input che diventa stato: il codice lingua entra in cache prima di essere limitato.
- Input che diventa costo computazionale: un header formalmente piccolo attiva un parser con comportamento quadratico.
- Input che diventa capacità: un raster interpretato da GDAL può trasformarsi in accesso a una risorsa di rete.
- Input che diventa autorità: una primary key editabile può spostare l’operazione oltre il queryset previsto.
Il framework non “fallisce” perché offre astrazioni. Fa esattamente il suo lavoro: ci evita di reimplementare internazionalizzazione, parsing HTTP, GIS e form binding. Il rischio compare quando trattiamo l’astrazione come se cancellasse il livello sottostante invece di nasconderlo.
La patch va installata. Ma il modello mentale va aggiornato
La prima azione resta banale e urgente: chi usa versioni supportate deve aggiornare alle release corrette. Il progetto Django raccomanda esplicitamente di farlo il prima possibile. Le release note di Django 6.1.2 confermano un problema low, tre moderate e la correzione di una mitigazione insufficiente, oltre ad alcuni bugfix non di sicurezza.
Ma fermarsi alla patch significa perdere la parte riutilizzabile. Quando rivedo un’applicazione, mi interessa cercare proprio i passaggi in cui un valore cambia natura: da input a chiave di cache, da testo a struttura parsata, da file a istruzione per una libreria, da identificatore a selettore di un oggetto. Sono punti in cui il costo, la capacità o l’autorità del dato aumentano.
È lì che conviene mettere limiti prima della trasformazione, non dopo. È lì che bisogna chiedersi quale identità userà una libreria se decide di uscire dal processo. È lì che un queryset deve restare un vincolo di autorizzazione e non soltanto una comodità per costruire l’interfaccia.
Le astrazioni migliori non eliminano i confini: li rendono espliciti
Queste quattro correzioni non dimostrano che Django sia insicuro. Dimostrano qualcosa di più utile: anche un framework maturo continua a scoprire interazioni tra livelli che, presi singolarmente, sembrano innocui.
La sicurezza del software raramente vive in una funzione chiamata security(). Vive nei passaggi di consegna: tra richiesta e cache, tra stringa e parser, tra byte e libreria nativa, tra form e modello. Ogni volta che deleghiamo complessità a un’astrazione guadagniamo velocità di sviluppo. Il prezzo è ricordare quali poteri abbiamo delegato insieme alla complessità.
Per questo una security release è più interessante quando smette di essere una lista di numeri da aggiornare e diventa una mappa dei confini che il nostro codice tende a dimenticare. La patch chiude il bug di oggi. Quel modello mentale può evitare quello di domani.
Domande frequenti
Quali versioni di Django correggono i quattro problemi pubblicati il 6 ottobre 2026?
Django ha rilasciato le versioni 6.1.2, 6.0.9 e 5.2.18. Il progetto raccomanda agli utenti delle serie supportate di aggiornare il prima possibile.
Tutte e quattro le vulnerabilità hanno la stessa gravità?
No. Secondo la policy di sicurezza di Django, CVE-2026-77050 è classificata low; CVE-2026-84429, CVE-2026-87890 e CVE-2026-87975 sono classificate moderate.
Perché il problema GeoDjango e GDAL è un confine di fiducia?
Perché byte trattati come raster potevano contenere un documento VRT che riferiva una sorgente esterna, inducendo GDAL a effettuare richieste di rete con l’identità del processo Django. Il formato dati poteva quindi attivare una capacità operativa della libreria.
I model formset con BigAutoField predefinito sono coinvolti nel problema delle primary key editabili?
No. L’advisory Django specifica che i modelli con la primary key BigAutoField predefinita non sono interessati da CVE-2026-87975; lo scenario riguarda primary key impostabili attraverso il form, come alcuni OneToOneField, parent link, chiavi naturali o UUID inclusi nei campi.
