Skip to main content

SEC.PRIV.ROLE_CREATEROLE — The account may create roles

  • Category: security
  • Severity: medium
  • Level: 0
  • Confidence: deterministic
  • Downtime class: none — the finding is about an account, not about a statement
  • Stability: stable
  • Suites: audit
  • Applies to: PostgreSQL 18

What changed in PostgreSQL 16, and what did not

Most write-ups describe CREATEROLE as a full escalation: alter any non-superuser role, grant any membership, therefore give yourself anything. That was true, and it is not what happens now.

Measured on PostgreSQL 18, as a role holding CREATEROLE and nothing else:

AttemptResult
create a new roleallowed
alter a role it did not createrefused — only roles with the CREATEROLE attribute and the ADMIN option on role "…" may alter this role
grant itself pg_read_all_datarefused — same shape
alter a role it did createallowed (it holds ADMIN on what it creates)

So the account cannot take privileges it does not already hold. What it can do is create login accounts with passwords of its choosing and administer them — access that lives outside your deployment process and survives a password rotation on the account everybody knows about.

Bad

ALTER ROLE app CREATEROLE;

Good

ALTER ROLE app NOCREATEROLE;

If something in your deployment genuinely creates roles — a tenant-per-role design, a provisioning job — give that job its own role and leave the application's alone.

If the account is also a superuser, SQLens reports SEC.PRIV.ROLE_SUPERUSER and stays quiet here: a superuser holds this attribute by definition, and two findings about the same account would bury the one that matters.