La actualización de PostgreSQL con 52 CVE es un proyecto de migración, no un parche. Y PostgreSQL 14 llegó a su fin

La actualización de PostgreSQL con 52 CVE es un proyecto de migración, no un parche. Y PostgreSQL 14 llegó a su fin

El 12 de noviembre, el proyecto PostgreSQL publicó las versiones menores 18.7, 17.12, 16.16, 15.20 y 14.25, además de una segunda release candidate de PostgreSQL 19. En conjunto cierran 52 CVE y corrigen más de 140 errores. Para una versión de mantenimiento, es una cifra enorme, y esta le exige más que el habitual «actualice el paquete, reinicie y a casa».

Hay dos factores más que hacen diferente esta ronda. PostgreSQL 14.25 es la última versión de la rama 14, así que 14 llega al final de su vida útil y no recibirá más correcciones de seguridad ni de errores. Y la corrección del fallo principal, CVE-2026-6460, cambia la forma en que el servidor se comunica con las extensiones. Un parche rutinario se convierte así en una actualización coordinada de servidor y extensiones, seguida de cierta reindexación y de un reinicio de las copias de seguridad.

Qué es CVE-2026-6460

Es una confusión de tipos que puede desencadenarse tras un ALTER ... SET DATA TYPE. Un usuario con permiso para crear objetos puede aprovecharla para ejecutar código arbitrario como la cuenta del sistema operativo bajo la que corre la base de datos. El aviso oficial le asigna 8,8 en CVSS 3.1: accesible por red, complejidad baja, privilegios bajos y sin interacción del usuario. Se ven afectadas todas las versiones anteriores a 18.7, 17.12, 16.16, 15.20 y 14.25.

«Requiere privilegios de creación de objetos» suena a una condición previa tranquilizadora. No creo que lo sea. En nuestras auditorías de pentesting, la configuración más habitual es un rol de aplicación que es propietario de su propio esquema. Las migraciones se ejecutan con ese rol, la aplicación se conecta con ese rol, y por tanto la cuenta en la que acabe una inyección SQL normalmente puede crear tablas. Ese es justo el perfil de atacante que necesita este CVE.

El alcance del daño es lo que más me preocupa. En un Postgres autoalojado, el usuario del sistema operativo que ejecuta el servidor puede leer todo el directorio de datos, es decir, todas las bases de datos del clúster, no solo la que usa la aplicación comprometida. En entornos Docker, a menudo ve además los secretos montados en el contenedor.

El problema de las extensiones

La corrección toca elementos internos contra los que se compilan las extensiones. DatumGetHeapTupleHeader() pasó de ser una macro a una función real del servidor, y heap_getattr() ahora llama a heap_datum_check(). Por ello, los empaquetadores de PGDG reconstruyeron todas las extensiones contra las nuevas versiones menores, y recomiendan actualizar el servidor y sus extensiones a la vez.

Por nuestra experiencia con PHP y Docker, aquí es donde suele fallar: alguien fija la imagen base de Postgres a una etiqueta nueva, pero los paquetes de extensiones se instalan en otra capa con sus propias versiones fijadas, o al revés. Actualice ambos en el mismo cambio, reconstruya la imagen y pruébela antes de que llegue a producción. Si compila extensiones desde el código fuente, recompílelas contra las nuevas cabeceras. Una versión menor normalmente no pide eso.

El trabajo posterior a la actualización

El anuncio de la versión enumera varias correcciones que solo sirven una vez que se reconstruyen ciertos elementos:

  • Los índices GIN que sospeche corruptos necesitan un reindex.
  • Los índices GiST de pg_trgm creados con gist_trgm_ops necesitan un reindex.
  • Los índices B-tree sobre columnas bpchar declaradas sin longitud necesitan un reindex.
  • Cualquier tabla que muestre errores «missing chunk number» necesita REINDEX TABLE.
  • Si usa copias incrementales, haga una nueva copia completa y base en ella todas las incrementales posteriores. Varios de los errores corregidos podían producir incrementales corruptas.

Para localizar rápidamente los índices de trigramas:

SELECT i.indexrelid::regclass AS index_name
FROM pg_index i
JOIN pg_opclass o ON o.oid = ANY (i.indclass::oid[])
WHERE o.opcname = 'gist_trgm_ops';

Si solo hace un punto de esa lista, que sea la copia de seguridad. Una incremental corrupta no da señales de problema hasta el día en que restaura desde ella, y ese es el peor momento para enterarse. Reindexar tablas grandes consume tiempo y E/S, así que prográmelo en una ventana tranquila. No lo añada simplemente al final de la tanda de parches.

Actualice también las herramientas cliente

El servidor no es lo único que hay que parchear. CVE-2026-116318 es una inyección SQL en pg_dump a través de los nombres de restricciones not-null, y CVE-2026-116330 cubre que el script de restauración en formato tar no bloquea la inyección de comandos de psql. El aviso de la versión también enumera CVE-2026-116314, ejecución de código arbitrario en pg_restore (CVSS 8,8), y CVE-2026-116313, por el que vacuumdb --missing-stats-only permite al propietario de una base de datos ejecutar código como quien ejecute vacuumdb (CVSS 7,2). Si alguien más puede crear y nombrar objetos en sus bases de datos, actualice las herramientas cliente junto con el servidor.

Piense dónde se ejecutan realmente esas herramientas: contenedores cron de copias de seguridad, runners de CI, el portátil de operaciones que alguien usa para restauraciones puntuales. Cuando desarrollamos aplicaciones cloud-native para clientes empresariales, el sidecar de copias de seguridad es casi siempre la imagen más descuidada del conjunto, fijada a la versión de cliente vigente cuando alguien escribió por primera vez el cron. Y además se ejecuta con credenciales amplias.

En un servicio gestionado, compruebe qué ha desplegado su proveedor. Aunque el servidor lo gestionen por usted, las herramientas cliente, la reindexación y la cadena de copias pueden seguir siendo cosa suya.

PostgreSQL 14 ha terminado

14.25 es el último parche que recibirá la rama 14. Si todavía la usa, aplique 14.25 ya y empiece a planificar la actualización mayor este trimestre. No lo deje para el año que viene. Para nuestros clientes empresariales suizos con requisitos de cumplimiento, una versión de base de datos sin soporte es por sí sola un hallazgo de auditoría, haya explotado alguien algo o no.

Vemos que la 14 perdura sobre todo detrás de aplicaciones móviles. En nuestros proyectos nativos de iOS y Android para clientes de salud e IoT, las apps se publican cada pocas semanas mientras que la base de datos de la API se montó una vez, hace años, y nadie la ha tocado desde entonces porque «funciona». Si eso describe su backend, este es el empujón que necesita. Apunte a la 18, la rama estable más reciente. La 19 sigue siendo release candidate y debe quedarse en un entorno de staging, no delante de sus usuarios. Hemos hecho estas migraciones como parte de nuestro trabajo de desarrollo, y la actualización de la base de datos en sí rara vez es lo difícil. Lo difícil es encontrar todos los servicios que se conectan a ella.

Qué haríamos esta semana

  1. Inventariar cada instancia de Postgres y su versión, incluidas las olvidadas en staging y en CI.
  2. Actualizar servidor y extensiones a la vez a 18.7, 17.12, 16.16, 15.20 o 14.25.
  3. Actualizar pg_dump, pg_restore y vacuumdb allí donde se ejecuten.
  4. Hacer una copia completa nueva si usa incrementales.
  5. Reindexar los índices GIN, GiST de trigramas y B-tree sobre bpchar afectados.
  6. Revisar qué roles pueden crear objetos y retirar ese derecho a cualquier rol de aplicación que no lo necesite.
  7. Poner en el calendario una fecha para abandonar PostgreSQL 14.

Si le suena un clúster PostgreSQL 14 que nadie quiere asumir, hablemos.

database-securityexpert-analysispostgresqltech-newsupgrade-planning