ASVS 5.0 V13.2.2 requires that communication between backend components, including operating system services, APIs, middleware and data layers, uses accounts with the least privileges necessary. It is Level 2.
If a service only reads orders, its account should only read orders.
Why it matters
A vulnerability such as SQL injection is as dangerous as the account behind the query. With a table owner or superuser account, an injection becomes schema destruction. With a narrow account it is a contained incident.
How to implement it
Create a role per service and grant only what the code does.
CREATE ROLE orders_api LOGIN;
GRANT CONNECT ON DATABASE shop TO orders_api;
GRANT USAGE ON SCHEMA orders TO orders_api;
GRANT SELECT, INSERT, UPDATE ON orders.orders TO orders_api;
GRANT SELECT ON orders.products TO orders_api;
-- no DELETE, no DDL, no access to other schemas
Run migrations with a separate account that the running app never uses. For cloud roles, list exact actions and resources instead of wildcards:
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::acme-invoices/prod/*"
}
Run service processes as non root users with no extra OS capabilities.
How to test it
Log in as the service account and try forbidden actions: drop a table, read another schema, write where only read is needed. Each must fail. Review permissions on a schedule and remove anything unused.
Common mistakes
- Using the database owner for convenience.
- Wildcard actions and resources in cloud policies.
- Granting access "temporarily" and never removing it.
Quick check
Should the app run migrations with its runtime account?
No. Use a separate, more privileged account only during deployment.