In November 2024, PostgreSQL shipped a patch for CVE-2024-10978, a privilege escalation bug affecting all supported major versions. A user with SET ROLE permission could execute queries as a more privileged role than intended. It was patched in 17.1, 16.5, 15.9, 14.14, and 13.17. If your app is still on an older minor version, anyone with limited database access may have already escalated their privileges.
That's the kind of vulnerability PostgreSQL takes seriously. The project has a strong security track record and a dedicated security team. The real danger with Postgres isn't the database engine itself. It's the five lines in pg_hba.conf that your cloud provider set four years ago and no one has looked at since.
TL;DR
PostgreSQL is safe when configured correctly. The engine is mature, actively patched, and used by companies of every size. The risks are almost always configuration: trust auth in pg_hba.conf, port 5432 open to the internet, superuser with no password, outdated minor versions, and no data-at-rest encryption check. Fix those five and you've closed the gap.
Our Verdict
What PostgreSQL Gets Right
- 30+ years of active development and security patches
- Dedicated security team with responsible disclosure process
- Fine-grained role-based access control built in
- SSL/TLS support for connections out of the box
- Row-level security (RLS) for multi-tenant data isolation
- Extensive audit logging via pgaudit extension
- Major cloud managed services (RDS, Cloud SQL, Supabase, Neon) handle patching
Where Deployments Go Wrong
- Default pg_hba.conf uses
trustauth on some distros - Port 5432 gets left open to 0.0.0.0 during provisioning
- The
postgressuperuser often has no password on fresh installs - Data at rest encryption is not built in (relies on OS/storage layer)
- Connection string with credentials ends up in source control
- No automatic patching on self-managed installs
The pg_hba.conf Problem
pg_hba.conf (host-based authentication) is the file that decides who can connect to your database and whether they need a password. The dangerous entry looks like this:
# TYPE DATABASE USER ADDRESS METHOD
local all all trust
host all all 127.0.0.1 trust
trust means: connect with no password, as any user, including postgres (the superuser). Some distributions ship this config by default. If your server has any other services running (a web app, a background worker, a compromised dependency), any of them can connect to Postgres as superuser with zero authentication.
The fix is two lines:
# TYPE DATABASE USER ADDRESS METHOD
local all postgres peer
local all all md5
host all all 127.0.0.1 scram-sha-256
host all all ::1/128 scram-sha-256
scram-sha-256 is the correct method for password authentication in PostgreSQL 10+. md5 is the minimum for older versions. Never use trust on any connection type in a production environment.
Port 5432 on Shodan
Shodan indexed approximately 1.2 million PostgreSQL instances directly accessible on port 5432 in 2024. The majority were not intentionally public. They were databases provisioned quickly where someone opened the port for a debug session and never closed it, or where a cloud security group defaulted to 0.0.0.0/0.
PostgreSQL is not designed to be a public-facing service. It has no built-in rate limiting, no brute-force protection on connection attempts, and no web application firewall layer. The correct architecture:
- Cloud managed (RDS, Cloud SQL, Supabase, Neon): Keep the database in a private subnet. Connect through your app server or a VPN. Never add a public endpoint unless you have an explicit reason.
- Self-hosted: Bind PostgreSQL to
127.0.0.1inpostgresql.conf(listen_addresses = 'localhost'). Use SSH tunnels or a bastion host for remote DBA access.
Check right now: grep listen_addresses /etc/postgresql/*/main/postgresql.conf. If it shows '*' or your server's public IP, PostgreSQL is accepting connections from anywhere.
CVE-2024-10978 and Keeping Up With Patches
PostgreSQL releases minor versions roughly every quarter. Minor releases contain only bug fixes and security patches. There is almost no reason to stay behind on minor versions.
Check your version:
SELECT version();
-- Returns something like:
-- PostgreSQL 16.3 on x86_64-pc-linux-gnu...
As of November 2024, supported minor versions with CVE-2024-10978 patched:
| Major Version | Minimum Safe Minor |
|---|---|
| PostgreSQL 17 | 17.1 |
| PostgreSQL 16 | 16.5 |
| PostgreSQL 15 | 15.9 |
| PostgreSQL 14 | 14.14 |
| PostgreSQL 13 | 13.17 |
PostgreSQL 12 and below reached end-of-life in November 2024. If you're on 12, migrate now. You're no longer getting security patches.
Role-Based Access and the Least Privilege Rule
Most apps connect to PostgreSQL with a superuser. This is the equivalent of running your web server as root. If your app's database credentials leak or get injected, the attacker has full access to every database on the server, the ability to read server files, and the ability to create new users.
Create a dedicated application user with only the permissions your app needs:
-- Create app user with a strong password
CREATE USER myapp_user WITH PASSWORD 'use-a-real-random-password-here';
-- Grant only what the app needs
GRANT CONNECT ON DATABASE myapp TO myapp_user;
GRANT USAGE ON SCHEMA public TO myapp_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myapp_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO myapp_user;
-- For future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myapp_user;
Your app should never connect as postgres. The superuser should only be used for schema migrations and DBA tasks.
Data at Rest and Connection Security
PostgreSQL does not encrypt data at rest. Your data sits in plaintext on disk. This matters if:
- Someone gets physical or filesystem access to the server
- You're on a cloud provider and the storage volume isn't encrypted
- You export backups to S3 without server-side encryption
Most major cloud providers encrypt EBS/storage volumes by default, but verify this explicitly. For sensitive fields (SSNs, health data, payment tokens), use pgcrypto for column-level encryption as a defense-in-depth layer.
For connections, make sure SSL is required:
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
And in your connection string, add ?sslmode=require or ?sslmode=verify-full if you have a CA certificate.
Hardening Checklist
PostgreSQL Security Hardening
Is PostgreSQL safe to use in production?
Yes, PostgreSQL's core engine is mature and well-audited. The risk comes from misconfiguration: default trust authentication in pg_hba.conf, port 5432 exposed to the internet, superuser passwords left blank, and outdated versions missing security patches. Address those four and you've removed 90% of the real-world risk.
What is the CVE-2024-10978 PostgreSQL vulnerability?
CVE-2024-10978 is a privilege escalation bug where a user with SET ROLE access could bypass intended permission boundaries and execute queries as a more privileged role. It was patched in PostgreSQL 17.1, 16.5, 15.9, 14.14, and 13.17 released in November 2024. If you're on an older minor version, upgrade immediately.
Is port 5432 safe to expose to the internet?
No. PostgreSQL is not designed to sit directly on the public internet. Use a private VPC, a bastion host, or an SSH tunnel for remote access. Shodan indexed approximately 1.2 million PostgreSQL instances directly accessible on port 5432 in 2024, most of them unintentionally exposed.
What is pg_hba.conf and why does it matter?
pg_hba.conf (host-based authentication) controls who can connect to PostgreSQL and how they authenticate. The dangerous setting is 'trust', which allows connections without a password. Default installations on some Linux distros ship with 'trust' for local socket connections, meaning any process on the same server can connect as any database user including the superuser.
Does PostgreSQL encrypt data at rest?
PostgreSQL itself does not encrypt data at rest. You need full-disk encryption at the OS or storage level (most cloud providers enable this by default) or a column-level encryption extension like pgcrypto for specific sensitive fields. Verify your cloud provider's encryption settings rather than assuming it's active.
Using PostgreSQL?
Scan your app for exposed credentials, missing encryption headers, and common database misconfigurations.