CME-920

Database Least-Privilege Access Control

Description

Enforce database-level access control by assigning each application service a dedicated database account with GRANT-limited permissions. App accounts receive only SELECT, INSERT, UPDATE, DELETE on specific tables — never CREATE, DROP, ALTER, GRANT, or DBA/superuser roles. Prevents SQL injection from escalating to schema modification, cross-database access, or OS command execution via xp_cmdshell/COPY/LOAD_FILE. Each microservice or application component should use a separate credential with the minimum privilege set required for its operations.

CVSS Vector Impacts

Metric Transition Rationale
Confidentiality (C) H → L SQL injection cannot exfiltrate data from other databases or tables outside the app account's GRANT scope; UNION-based extraction is limited to the account's SELECT privileges
Integrity (I) H → L Cannot modify schema, drop tables, or alter other services' data; DML limited to explicitly GRANTed tables

CWE Relationships

Verification

Verify application database accounts have no superuser, DDL, or GRANT privileges

$ psql -h localhost -U <app_user> -c "SELECT rolsuper, rolcreatedb, rolcreaterole FROM pg_roles WHERE rolname='<app_user>'"
# Expected: f|f|f
Platform: linux
$ psql -h localhost -U <app_user> -c "SELECT grantor, privilege_type FROM information_schema.role_table_grants WHERE grantee='<app_user>'" | grep -iE 'create|drop|alter|grant'
Platform: linux
$ Invoke-Sqlcmd -Query "SELECT IS_SRVROLEMEMBER('sysadmin', '<app_user>')"
# Expected: 0
Platform: windows
$ Invoke-Sqlcmd -Query "SELECT dp.permission_name FROM sys.database_permissions dp JOIN sys.database_principals pr ON dp.grantee_principal_id = pr.principal_id WHERE pr.name = '<app_user>' AND dp.permission_name IN ('ALTER','CREATE','CONTROL')"
# Expected: (0 rows affected)
Platform: windows
$ mysql -h localhost -u <app_user> -e "SHOW GRANTS FOR CURRENT_USER()" | grep -iE 'ALL PRIVILEGES|SUPER|CREATE|DROP|ALTER|GRANT'
Platform: linux

Remediation

linux PostgreSQL dedicated application user with DML-only grants

psql -h localhost -U postgres -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '<app_user>') THEN CREATE USER <app_user> WITH PASSWORD '<password>' NOSUPERUSER NOCREATEDB NOCREATEROLE; END IF; END \$\$;"
psql -h localhost -U postgres -d <database> -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA <schema> TO <app_user>; ALTER DEFAULT PRIVILEGES IN SCHEMA <schema> GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO <app_user>;"
psql -h localhost -U postgres -d <database> -c "REVOKE CREATE ON SCHEMA public FROM PUBLIC;"
linux MySQL dedicated application user with DML-only grants

mysql -u root -e "CREATE USER IF NOT EXISTS '<app_user>'@'localhost' IDENTIFIED BY '<password>';"
mysql -u root -e "GRANT SELECT, INSERT, UPDATE, DELETE ON <database>.* TO '<app_user>'@'localhost';"
mysql -u root -e "FLUSH PRIVILEGES;"
← CME-919: JIT Compiler Hardening (JIT Restriction / V8 Sandbox) CME-921: URL Request Path Canonicalization Before Authorization →