Salta al contenuto principale

Aggiornare PostgreSQL: le 52 falle chiuse il 12 novembre

PostgreSQL 18.7 e le altre minor chiudono 52 vulnerabilità, due sulla row level security. Le query per controllare versione, permessi e tabelle esposte.

Un piccolo elefante con le orecchie aperte e la proboscide alzata, in piedi tra le zampe della madre su un terreno di sabbia ed erba secca
Indice dei contenuti

Il 12 novembre il gruppo che sviluppa PostgreSQL ha pubblicato le versioni 18.7, 17.12, 16.16, 15.20 e 14.25. Non è la solita minor con qualche bug sistemato: l’annuncio ufficiale parla di 52 vulnerabilità di sicurezza chiuse in un colpo, più di 140 bug. Qui sotto ci sono tre query da lanciare su ciascun database: le ho provate su un PostgreSQL 16.15, cioè proprio una versione ancora da aggiornare.

Aggiornare PostgreSQL a una minor è in genere un lavoro da dieci minuti. Questa volta ci sono passaggi in più dopo il riavvio, e la 14 non riceve più patch.

Cosa è uscito il 12 novembre

L’elenco delle CVE (il codice pubblico che identifica ogni vulnerabilità) è lungo, e molte voci hanno lo stesso finale: “executes arbitrary code”. Il punteggio CVSS, la scala da 0 a 10 che misura la gravità, arriva a 8.8 per decine di voci. Le aree toccate sono diverse: conversioni di tipo, plperl e pltcl, pg_dump e pg_restore, dblink, la replica logica.

Nello stesso annuncio c’è scritto che la 14.25 è l’ultima release di PostgreSQL 14, ora fuori supporto e senza più correzioni, nemmeno di sicurezza. Il rilascio generale di PostgreSQL 19 è invece previsto per il 19 novembre 2026.

Una falla che secondo chi l’ha studiata ha 22 anni

La voce più raccontata è la CVE-2026-6460. La scheda ufficiale dice che una confusione di tipo permette a chi crea oggetti nel database di eseguire codice arbitrario come l’utente del sistema operativo che fa girare PostgreSQL. La strada principale è ALTER ... SET DATA TYPE, cioè cambiare il tipo di una colonna o di un campo, ma la scheda cita anche altri percorsi DDL (i comandi che creano e modificano la struttura: tabelle, tipi, colonne).

Il ricercatore Mehmet Ince ha raccontato come ci è arrivato in un post del 9 novembre, parte di una serie sui rischi sistemici del Postgres gestito. Secondo lui è una zero-day rimasta nel cuore di PostgreSQL per 22 anni. Lui stesso precisa che non è una scoperta solo sua: PostgreSQL l’ha trattata come una CVE ombrello per un’intera famiglia di problemi sui tipi composti, segnalati da più ricercatori. Nel post scrive anche di aver studiato gli interni di Postgres facendosi fare da insegnante da un agente AI.

Il dettaglio che conta per chi gestisce un database sta nel vettore CVSS: PR:L, privilegi bassi. Non serve essere superutente. Basta un ruolo che possa creare oggetti, e da lì si arriva a comandi eseguiti sulla macchina del database. E creare un oggetto è più facile di quanto sembri, come si vede due query più sotto.

Che versione di PostgreSQL stai usando

La prima query confronta la versione del server con la minima corretta per la sua major. server_version_num restituisce un intero: 160015 significa 16.15.

SELECT current_setting('server_version_num')::int AS versione,
       CASE current_setting('server_version_num')::int / 10000
         WHEN 18 THEN 180007
         WHEN 17 THEN 170012
         WHEN 16 THEN 160016
         WHEN 15 THEN 150020
         WHEN 14 THEN 140025
       END AS minima_corretta;

Sul mio server di prova esce 160015 | 160016: da aggiornare. Se minima_corretta è vuota, la major è fuori dall’elenco: o più vecchia della 14, e allora non riceve correzioni da tempo, o è la 19 in prova.

Sul mio Ubuntu il pacchetto si chiama 16.15-0ubuntu0.24.04.1 e la query restituisce 16.15: il numero corrisponde. Se invece usi un pacchetto che applica le correzioni senza cambiare versione, come succede per PHP su alcune distribuzioni (ne ho scritto in come capire se il tuo PHP ha davvero la patch), la conferma sta nel changelog del pacchetto.

Chi può creare oggetti nel tuo database

Visto che la CVE-2026-6460 parte da chi crea oggetti, viene da chiedersi chi può farlo. La risposta onesta è: quasi chiunque si connetta. Fino alla versione 14 chiunque poteva creare tabelle nello schema public per impostazione predefinita. La 15 ha tolto quel permesso, ma le note di rilascio della 15 avvertono che aggiornare un cluster o ripristinare un dump conserva i permessi esistenti su public. Un database nato su una versione vecchia e migrato negli anni può averli ancora.

SELECT has_schema_privilege('public', 'public', 'CREATE')
       AS chiunque_crea_in_public;

Il primo public è lo pseudo-ruolo che comprende tutti, il secondo è lo schema. Se esce t, qualsiasi utente che si connette può creare oggetti lì, e il permesso si toglie con REVOKE CREATE ON SCHEMA public FROM PUBLIC; dopo aver controllato che nessuna migrazione ne abbia bisogno.

Un f però non ti mette al riparo. Ho creato un ruolo nuovo, senza nessun permesso su public, e ho lanciato CREATE TYPE pg_temp.prova AS (a int);: il tipo è stato creato nello schema temporaneo della sessione, che per impostazione predefinita è aperto a tutti. Questa query è igiene dei permessi, non una difesa dalla CVE-2026-6460. Contro quella serve l’aggiornamento, e conviene trattare ogni ruolo che si connette come capace di creare oggetti.

Le due falle che aggirano la row level security

Qui il discorso tocca le web app che scrivo io. La row level security (RLS) è il meccanismo di Postgres che decide riga per riga chi vede cosa: in un gestionale fa in modo che ogni cliente legga solo i propri ordini anche se la tabella è una sola. Su GitWatchman e CycleTour, due progetti miei su Supabase, l’accesso ai dati passa da queste regole, come ho spiegato nell’articolo sull’area riservata clienti.

Nel pacchetto del 12 novembre ci sono due voci che la riguardano direttamente:

  • CVE-2026-120154, punteggio 4.3: un utente con i permessi INSERT, UPDATE e SELECT su una tabella può leggere tutte le righe nonostante le regole RLS, ripetendo un INSERT ... ON CONFLICT DO UPDATE. La scheda precisa che non permette scritture improprie.
  • CVE-2026-116311, punteggio 5.4: con DROP OWNED un ruolo può togliersi dalle policy restrittive e leggere o modificare righe che quelle policy dovevano negargli.

La prima mi interessa di più. INSERT ... ON CONFLICT DO UPDATE è quello che si chiama upsert: inserisci una riga, e se esiste già la aggiorni. Supabase lo espone anche dal client JavaScript con il metodo .upsert(). Non so dire se la falla sia sfruttabile attraverso l’API di Supabase: la scheda ufficiale non ne parla e non ho trovato un’analisi che lo dimostri. So però quali tabelle hanno la combinazione di permessi descritta, e quella si controlla:

SELECT c.oid::regclass AS tabella, r.rolname AS ruolo
FROM pg_class c
CROSS JOIN pg_roles r
WHERE c.relkind IN ('r', 'p')
  AND c.relrowsecurity
  AND r.rolname NOT LIKE 'pg\_%'
  AND NOT r.rolsuper
  AND NOT r.rolbypassrls
  AND has_table_privilege(r.oid, c.oid, 'SELECT')
  AND has_table_privilege(r.oid, c.oid, 'INSERT')
  AND has_table_privilege(r.oid, c.oid, 'UPDATE');

La query elenca le tabelle con RLS attiva (anche partizionate) e i ruoli che hanno tutti e tre i permessi, esclusi superutenti e ruoli con l’attributo BYPASSRLS, che le regole le saltano comunque. Un caso la query non lo distingue: il proprietario della tabella salta la RLS se non hai attivato FORCE ROW LEVEL SECURITY, quindi se nell’elenco compare il proprietario va letto con questo in mente. Nel mio test ho creato una tabella note con RLS e un ruolo authenticated con SELECT, INSERT e UPDATE: la query la trova. Se nell’elenco compare un ruolo usato da utenti che si registrano da soli, quella tabella è il motivo per non rimandare l’aggiornamento.

Aggiornare: binari, riavvio e i passaggi dopo

Le release minor sono cumulative e non richiedono dump, restore o pg_upgrade: secondo l’annuncio basta fermare PostgreSQL e aggiornarne i binari. Il riavvio c’è comunque, quindi va messo in un momento in cui l’app può restare ferma qualche minuto.

Questa volta l’annuncio elenca anche lavori da fare dopo, legati a bug corretti nella stessa release. I principali:

  1. Se sospetti che un indice GIN sia danneggiato, ricostruiscilo con REINDEX INDEX.
  2. Se usi l’estensione pg_trgm, ricostruisci gli indici GiST con l’operator class gist_trgm_ops.
  3. Gli indici B-tree su colonne bpchar senza lunghezza vanno ricostruiti; le colonne char(n) non sono toccate.
  4. Se leggendo una tabella compare l’errore “missing chunk number”, lancia REINDEX TABLE su quella tabella.
  5. Con le tabelle partizionate, l’annuncio dà una query per trovare i vincoli NOT NULL NO INHERIT da togliere o riparare.
  6. Con i backup incrementali, fai un nuovo backup completo e usalo come base per i successivi.

Chi ha saltato una o più minor precedenti deve leggere anche le loro note, perché i passaggi si sommano.

Su un servizio gestito l’aggiornamento passa dal fornitore. Per Supabase la documentazione sugli upgrade descrive un aggiornamento avviato da te, con lo stesso processo per major e minor: si parte dal pulsante “Upgrade project” nelle impostazioni generali del progetto, e durante l’operazione il progetto va offline. Non ho verificato da quando Supabase offra le versioni corrette: la prima query di questo articolo ti dice se il tuo progetto le ha già.

Se sei ancora su PostgreSQL 14

La 14.25 chiude le falle di novembre, ma è l’ultima. La prossima vulnerabilità che toccherà la 14 resterà aperta. Il passaggio a una major supportata non si fa cambiando i binari: serve pg_upgrade oppure un dump e un restore, con una prova completa dell’applicazione prima di farlo in produzione.

Ogni major è supportata per cinque anni dalla prima release, e la pagina delle versioni riporta le date: la 17 fino all’8 novembre 2029, la 18 fino al 14 novembre 2030. Io sceglierei la 18 e lascerei la 19 a quando avrà avuto la sua prima correzione, ma è una preferenza: dipende da quanto tempo hai per i test.

Da dove cominciare

Se hai più database da aggiornare, parti da quelli dove la terza query trova ruoli usati da utenti che si registrano da soli. Chi ha un’app su PostgreSQL 14 deve mettere in calendario la migrazione, non solo la patch: il costo di quel lavoro rientra nella manutenzione di cui parlo nell’articolo su quanto costa un gestionale personalizzato.

Se il tuo gestionale o la tua area riservata stanno su Postgres, scrivimi: guardo versione e permessi del database e ti dico cosa serve.

Prossimo passo

Hai un progetto in mente? Parliamone.

Continua a leggere