IS POSTGRESQL SAFE? SECURITY ANALYSIS | VIBEEVAL
Battle-Tested Security
PostgreSQL has been actively developed for over 35 years with continuous security auditing. Its native Row Level Security and powerful role system provide excellent foundations for secure applications. The PostgreSQL Global Development Group has a published security policy, a dedicated security@postgresql.org reporting channel, and a track record of patching disclosed CVEs within days.
What makes Postgres safe is also what trips developers up: it is not a managed service. The defaults assume you know what you are doing. pg_hba.conf ships permissive on some Linux distributions and restrictive on others. The superuser role can do anything. Extensions can run arbitrary code. Postgres gives you the primitives — you have to use them correctly.
Security Considerations
SQL Injection
Always use parameterized queries. PostgreSQL cannot protect against injection if you concatenate user input into queries.
# BAD: f-string interpolation
cur.execute(f"SELECT * FROM users WHERE email = '{email}'")
# GOOD: parameterized
cur.execute("SELECT * FROM users WHERE email = %s", (email,))
// BAD: template literal
const r = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
// GOOD: $1 placeholders
const r = await db.query("SELECT * FROM users WHERE id = $1", [userId]);
The same rule applies to dynamic identifiers (table or column names). Parameterization only escapes values — for identifiers, use a strict allowlist or format('%I', name) from pg_catalog.
Row Level Security
Enable and configure RLS for multi-tenant applications. It provides powerful row-level access control.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- In the application, set the tenant per request:
SET LOCAL app.tenant_id = '...';
Without SET LOCAL, the policy denies everything — which is the safe failure mode. The wrong failure mode is forgetting to enable RLS, in which case the policy never runs and tenants leak across each other.
Connection Configuration
Configure pg_hba.conf carefully. Require SSL, use strong authentication, and restrict network access:
# pg_hba.conf
hostssl all app_user 10.0.1.0/24 scram-sha-256
hostssl all all 0.0.0.0/0 reject
local all all peer
Avoid trust (no auth) and md5 (weak hashing) in production. SCRAM-SHA-256 is the modern default and resists offline cracking better than MD5.
Role Management
Use least-privilege roles. Don’t use the superuser role for application access:
CREATE ROLE app_runtime LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE production TO app_runtime;
GRANT USAGE ON SCHEMA public TO app_runtime;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_runtime;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_runtime;
Migrations get a separate role with DDL grants. Read-only consumers (analytics, BI) get a third role with SELECT only.
Extension Risk
Several PostgreSQL extensions execute code in the OS user that runs the database. plpython3u and plperlu (the u stands for “untrusted”) run arbitrary Python or Perl on the server. pg_read_server_files reads from disk. These belong only on tightly controlled clusters where every superuser is also an OS root. Audit pg_extension regularly and remove anything not actively used.
Backup and Replication Security
Logical replication slots and backups can leak data sideways. A replica with weaker access controls becomes a copy of your production data anyone can query. Set up replicas with the same pg_hba.conf rigor as primary, and treat backup files (pg_dump output, base backups) as data classified at the same level as the database itself.
Listen Address
listen_addresses = '*' in postgresql.conf makes Postgres accept TCP connections on every interface. Combined with a permissive pg_hba.conf, this is how databases end up indexed on Shodan. For a database that only serves a single application server on the same VPC, set listen_addresses to the specific private IP or use Unix sockets.
Security Assessment
Strengths
-
- Decades of security hardening and auditing
-
- Native Row Level Security (RLS)
-
- Powerful role-based access control
-
- SSL/TLS encryption support
-
- Strong authentication mechanisms (SCRAM-SHA-256)
-
- HIPAA and SOC 2 compliant deployments possible
-
- Extensible permission model with column and row granularity
-
- Active security team with rapid CVE response
Concerns
-
- SQL injection if queries not parameterized
-
- Misconfiguration can expose data
-
- pg_hba.conf requires careful configuration
-
- Self-hosted requires security maintenance
-
- Superuser bypasses RLS — easy to use by accident
-
- Some extensions execute arbitrary OS-level code
-
- Backups and replicas inherit production data sensitivity
PostgreSQL-as-a-service comparison
Most teams now run Postgres through a managed provider rather than self-hosted. The security trade-offs:
- Neon. Serverless Postgres with branching. Good defaults; risks are connection-string leaks across branches and forgetting RLS. See
/safety/neon/. - Supabase. Postgres with auto-generated REST/GraphQL API. The anon key exposes every table that lacks RLS. See
/safety/supabase/. - PlanetScale. Now offers Postgres alongside Vitess MySQL. Branching model similar to Neon; same credential-hygiene concerns.
- AWS RDS / Aurora. Most control, most ops work. VPC-only by default if you configure the security group correctly.
- Self-hosted. Maximum flexibility, all the security work falls on you.
For new projects with no special infrastructure constraint, the managed providers remove whole categories of mistakes (forgetting TLS, misconfiguring pg_hba.conf, missing patches). The risks shift to application-layer (RLS, parameterization) and credential hygiene, which you would have to handle anyway.
The Verdict
PostgreSQL is one of the most secure database systems available. Its native security features, decades of hardening, and active security team make it excellent for security-critical applications. Your responsibility is to use parameterized queries, configure RLS properly, and follow least-privilege principles for roles.
Four checks before production:
- Every query in the codebase uses parameter placeholders, no string interpolation.
- The application connects as a non-superuser role with only the grants it needs.
pg_hba.confrequires SSL and SCRAM auth from production app subnets only.- RLS is enabled on every multi-tenant table, and the session sets the tenant context per request.
Related Resources
How to Secure PostgreSQL
Step-by-step security guide covering role design, RLS rollout, and pg_hba.conf hardening.
PostgreSQL Security Checklist
Interactive security checklist with the SQL queries to verify each item.
Supabase RLS Checker
The same RLS test pattern works against any Postgres backend, not just Supabase.
Token Leak Checker
Find Postgres connection strings exposed in browser bundles or public repos.
Scan Your Application
Let VibeEval scan your application for database security vulnerabilities, including SQL injection probes and RLS bypass tests against your Postgres-backed routes.
COMMON QUESTIONS
SCAN YOUR APP
14-day trial. No card. Results in under 60 seconds.