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:

  1. Every query in the codebase uses parameter placeholders, no string interpolation.
  2. The application connects as a non-superuser role with only the grants it needs.
  3. pg_hba.conf requires SSL and SCRAM auth from production app subnets only.
  4. RLS is enabled on every multi-tenant table, and the session sets the tenant context per request.

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

01
Is PostgreSQL safe to use in production?
Yes. PostgreSQL has been in active development since 1996 with a security team that responds quickly to vulnerabilities. The engine is one of the most secure databases available. The risks live entirely in how your application talks to it: SQL injection, leaked connection strings, and over-privileged roles.
Q&A
02
Does PostgreSQL prevent SQL injection automatically?
No. PostgreSQL accepts whatever SQL you send. The protection comes from parameterized queries — sending the SQL template separately from the values so the database never interprets user input as code. ORMs like Drizzle, Prisma, SQLAlchemy, and the raw `pg`/`psycopg` drivers all support this; the danger is when developers fall back to string concatenation for 'just one quick query'.
Q&A
03
What is Row Level Security and when should I enable it?
RLS is a Postgres feature that filters rows based on a per-session context — typically the authenticated user's ID. Enable it for any multi-tenant table where the application would otherwise need to remember to add `WHERE tenant_id = ?` to every query. With RLS, that filter is enforced by the database, even if the application forgets.
Q&A
04
How should pg_hba.conf be configured for production?
Restrict by host and require strong auth methods. `host all all 0.0.0.0/0 md5` is the worst-case default — it lets any IP connect with a password. For production, list specific subnets, require `scram-sha-256` not `md5`, and require SSL via `hostssl`. Reject `trust` for anything that isn't a local socket on a single-tenant machine.
Q&A
05
Is the PostgreSQL superuser role safe to use for application connections?
No. The superuser bypasses all permission checks, including RLS. An application connecting as superuser turns every SQL injection from a row leak into a complete database takeover. Create a dedicated application role with `CONNECT` and `SELECT/INSERT/UPDATE/DELETE` only on the tables it needs.
Q&A
06
Does PostgreSQL encrypt data at rest?
Not by itself. Disk-level encryption is provided by the OS (LUKS, BitLocker) or the cloud provider (RDS, Aurora, Neon, Supabase all encrypt at rest by default). Postgres also supports `pgcrypto` for column-level encryption — useful for fields like SSNs that should be encrypted with a key the database doesn't have.
Q&A
07
What are the most dangerous PostgreSQL extensions from a security view?
`plpython3u`, `plperlu`, and `plsh` allow execution of arbitrary code on the database server in the OS user that runs Postgres. `pg_read_server_files` and `pg_read_binary_file` can read filesystem contents. These should not be installed on a production cluster unless absolutely required, and only the superuser should be able to install or call them.
Q&A
08
How does PostgreSQL compare to MySQL for security?
Both are mature and well-audited. PostgreSQL has stronger native multi-tenancy primitives (RLS, schemas), more granular role inheritance, and stricter type enforcement. MySQL has historically had more lenient default behaviors (silent type coercion, default character sets that varied across versions). For new applications with tenant isolation needs, Postgres tends to be the safer default.
Q&A

SCAN YOUR APP

14-day trial. No card. Results in under 60 seconds.

START FREE SCAN