Applications running with database superuser or admin credentials mean any SQL injection, code execution, or insider query can drop tables, read all data, or modify any row without restriction. CWE-272 (Least Privilege Violation) and OWASP A01 (Broken Access Control) cover this. NIST 800-53 AC-6 mandates least-privilege for all system accounts. A postgres root connection string in DATABASE_URL means a single RCE or injection turns into a full database wipe. API keys scoped to all operations mean one leaked key — from logs, from a debug endpoint, from a shoulder-surf — gives an attacker full service access.
Low because least-privilege violations don't introduce a vulnerability themselves, but they dramatically amplify the blast radius when any other control fails.
Create a dedicated database user with only the permissions the application actually needs:
CREATE USER app_user WITH PASSWORD 'strong_password';
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;
-- Do NOT grant: CREATE, DROP, ALTER, TRUNCATE, or pg_superuser
For Stripe, use the Stripe Dashboard to create restricted API keys scoped only to the operations your server performs (e.g., charges:write only, not customers:read_write). Store the restricted key in STRIPE_SECRET_KEY, not the full secret key.
ID: security-hardening.dependency-security.least-privilege
Severity: low
What to look for: Enumerate every database connection and service account credential. For each, review database connection configuration and external service integrations. Check whether the database user has only the permissions it needs (SELECT, INSERT, UPDATE, DELETE on specific tables — not DROP, CREATE, ALTER, or GRANT). Check API keys for external services — are they scoped to the minimum necessary operations?
Pass criteria: Database connection uses a dedicated user with table-level permissions only. No root or admin credentials in the application connection. External service API keys are scoped to the minimum required operations — 100% of accounts must have no more than the minimum required permissions. Report: "X service accounts found, all Y use minimum required permissions."
Fail criteria: Application uses the database root user or a user with superuser/admin privileges. A single all-access API key used for all operations.
Skip (N/A) when: The application uses a serverless database where the SDK manages all access control (e.g., Supabase Row Level Security with anon key), or the project is in early development with a local database only.
Detail on fail: "DATABASE_URL uses postgres root user — application has full database admin access including DROP TABLE" or "Single Stripe secret key used for both reading webhooks and creating charges — no scope separation"
Remediation: Create a dedicated database user:
-- PostgreSQL: create a least-privilege application user
CREATE USER app_user WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;
-- Do NOT grant: CREATE, DROP, ALTER, TRUNCATE, or superuser
For Stripe, use restricted keys in the Stripe dashboard scoped only to the operations your server performs.