Conceptos básicos de seguridad
La seguridad de PostgreSQL en producción comienza con un modelo de amenazas: quién puede acceder a la red, cómo se emiten las credenciales, si la inyección SQL es posible y qué pueden leer o cambiar los usuarios internos. PostgreSQL 18.4 proporciona primitivas sólidas (SCRAM, SSL, RLS, roles), pero los valores predeterminados están optimizados para el desarrollo local, no para la nube expuesta a Internet.
Receta
-- Rol de aplicación de mínimo privilegio (ejecutar como administrador una vez)
CREATE ROLE app_api LOGIN PASSWORD 'rotate-me' NOSUPERUSER NOCREATEDB NOCREATEROLE;
GRANT CONNECT ON DATABASE orders TO app_api;
GRANT USAGE ON SCHEMA public TO app_api;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_api;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE ON TABLES TO app_api;# Extracto de pg_hba.conf: solo TLS + SCRAM para subredes de aplicaciones
hostssl orders app_api 10.20.0.0/16 scram-sha-256Cuándo usar esto:
- Incorporación de nuevos servicios antes del primer despliegue en producción.
- Revisión post-incidente cuando las credenciales o los límites de red no estaban claros.
- Preparación para el cumplimiento (SOC2, HIPAA) que requiere controles de acceso documentados.
Ejemplo de trabajo
Reforzar una API SaaS de base de datos única: separar roles, revocar valores predeterminados públicos, forzar TLS.
-- 1. Revocar valores predeterminados peligrosos en la nueva base de datos
REVOKE ALL ON DATABASE orders FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
-- 2. Rol de aplicación: solo DML
CREATE ROLE app_api LOGIN PASSWORD 'use-vault-generated-secret'
NOSUPERUSER NOCREATEDB NOCREATEROLE;
GRANT CONNECT ON DATABASE orders TO app_api;
GRANT USAGE ON SCHEMA public TO app_api;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_api;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_api;
-- 3. Rol de migración: DDL solo en CI (no en tiempo de ejecución de la aplicación)
CREATE ROLE migrator LOGIN PASSWORD 'ci-only-secret' NOSUPERUSER;
GRANT CONNECT ON DATABASE orders TO migrator;
GRANT CREATE ON SCHEMA public TO migrator;
GRANT ALL ON ALL TABLES IN SCHEMA public TO migrator;
-- 4. Analista de solo lectura (BI)
CREATE ROLE analyst_ro LOGIN PASSWORD 'bi-secret' NOSUPERUSER;
GRANT CONNECT ON DATABASE orders TO analyst_ro;
GRANT USAGE ON SCHEMA public TO analyst_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_ro;# postgresql.conf (gestionado o autoalojado)
ssl = on
password_encryption = scram-sha-256
log_connections = on
log_disconnections = onLo que esto demuestra:
PUBLICno es de confianza por defecto.- La aplicación en tiempo de ejecución utiliza
app_apisolo DML, no superusuario. - DDL aislado a
migratorutilizado en pipelines de despliegue. - TLS y SCRAM son básicos, no extras opcionales.
Inmersión profunda
Modelo de amenazas de un vistazo
| Amenaza | Vector | Control principal |
|---|---|---|
| Exposición de red | Puerto 5432 abierto a Internet | Subred privada, grupos de seguridad, solo hostssl |
| Robo de credenciales | .env filtrado, contraseña compartida | Secretos SCRAM por servicio, rotación, bóveda |
| Inyección SQL | Consultas no parametrizadas | Sentencias preparadas, vinculación de parámetros ORM |
| Usuario interno / sobreprivilegio | GRANT ALL, superusuario compartido | Separación de roles, RLS para datos de inquilino |
| Datos en reposo | Instantánea de disco robada | Cifrado de volumen / KMS en la nube |
Cómo funciona
- Autenticación (
pg_hba.conf+pg_authid): el cliente demuestra su identidad; SCRAM almacena un verificador con sal, no una contraseña en texto plano. - Autorización (
GRANT/REVOKE, RLS): después del inicio de sesión, cada sentencia se verifica contra los privilegios y políticas de los objetos. - Transporte (SSL/TLS): cifra los bytes en tránsito;
verify-fulltambién valida el nombre de host del certificado del servidor. - Auditoría (registro, pgaudit): registra quién se conectó y qué objetos se tocaron para investigaciones.
Notas de PostgreSQL
-- Confirmar el rol efectivo y search_path al inicio de la sesión
SELECT current_user, session_user, current_setting('search_path');
-- Listar concesiones peligrosas
SELECT grantee, privilege_type, table_schema, table_name
FROM information_schema.role_table_grants
WHERE grantee IN ('PUBLIC', 'app_api')
ORDER BY 1, 2;Trampas
- Usar el superusuario
postgresen la configuración de la aplicación - un compromiso posee el clúster. Solución: rolapp_apidedicado con concesiones mínimas. - Dejar concesiones
PUBLICen nuevas bases de datos - cualquier inicio de sesión puede leer/escribir hasta que se revoque. Solución:REVOKE ALL ON DATABASE ... FROM PUBLICen el runbook de aprovisionamiento. trustomd5enpg_hba.conf- sin cifrado o contraseña débil en tránsito. Solución:hostssl+scram-sha-256para todos los roles no locales.- SQL construido mediante concatenación de cadenas - inyección clásica incluso con herramientas "internas". Solución: marcadores
$1en cada controlador; nunca interpolar entrada del usuario. - Misma contraseña en staging y producción - una brecha en staging se convierte en una brecha en producción. Solución: roles y secretos separados por entorno; bloquear credenciales de producción en CI no de producción.
search_pathdemasiado amplio - secuestro de objetos a través de un esquema malicioso. Solución: establecersearch_pathpor rol:ALTER ROLE app_api SET search_path = app, public;
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Autenticación IAM/base de datos (RDS IAM, Cloud SQL IAM) | Sin contraseñas de base de datos de larga duración en aplicaciones | El controlador y la ruta de red admiten tokens IAM |
Autenticación de cliente por certificado (cert en pg_hba) | mTLS entre servicios conocidos | Muchos clientes de corta duración (dolor de rotación) |
| Seguridad a nivel de fila (RLS) | Esquema compartido multi-inquilino | Inquilino único con división simple de roles es suficiente |
| Solo política de red (sin roles de base de datos) | Sandboxes de desarrollo temporales | Cualquier dato de producción con requisitos de cumplimiento |
Preguntas frecuentes
¿Es PostgreSQL seguro por defecto?
No. Los valores predeterminados favorecen el desarrollo local. La producción requiere pg_hba.conf, SCRAM, TLS, concesiones de roles y registro explícitos.
¿Quién debe tener superusuario?
Administradores de "break-glass" y automatización que solo gestiona extensiones. Los tiempos de ejecución de aplicaciones nunca deben usar superusuario.
¿Cómo detecto intentos de inyección SQL?
Registre log_statement = 'ddl' para cambios de esquema, habilite el registro de conexiones y alerte sobre tasas de error inusuales del rol de la aplicación.
¿RLS reemplaza la autorización de la aplicación?
No. RLS es defensa en profundidad cuando la aplicación se conecta con un rol de base de datos compartido. Las comprobaciones a nivel de aplicación siguen siendo importantes para la experiencia del usuario y las reglas de negocio.
¿Qué pasa con las extensiones y la seguridad?
Instale extensiones solo de una lista de permitidos. CREATE EXTENSION requiere privilegios elevados; controle a través del rol migrator.
¿Con qué frecuencia rotar contraseñas?
90 días para programas de cumplimiento, o inmediatamente al finalizar la contratación de un ingeniero y en caso de sospecha de fuga. Prefiera la autenticación IAM/token cuando esté disponible.
¿Deben los analistas tener acceso de escritura?
Raramente. El patrón habitual es analyst_ro de solo lectura con enmascaramiento de columnas o vistas que excluyen PII.
¿Cómo aseguro Postgres gestionado?
Usted todavía es responsable de las reglas de red, IAM, extensiones y diseño de consultas. Consulte Conceptos básicos de Postgres gestionado.
¿Qué registros son mínimos para la seguridad?
log_connections, log_disconnections, autenticación fallida (a través de errores de conexión) y cambios DDL. Amplíe con pgaudit para el cumplimiento.
¿Puedo bloquear `COPY TO PROGRAM`?
Sí. No conceda superusuario; revoque pg_execute_server_program de los roles de aplicación en PostgreSQL 15+.
Relacionado
- Configuración de SSL/TLS - cifrar conexiones
- Autenticación SCRAM - hash de contraseñas
- pgaudit y registro de cumplimiento - pistas de auditoría
- Cifrado en reposo - protección de disco y de instantáneas
- Mejores prácticas de seguridad - lista de verificación operativa
Versiones de la pila: Esta página se escribió para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x y PostGIS 3.5+.