SEC.PRIV.PROXY_TO_ADMIN_IN_MIGRATION — A migration lets an account connect as the server's own
- Category: security
- Severity: high
- Level: 0 — reachable at any strictness; the security suite is gated by
security.min_severity - Confidence: deterministic
- Stability: stable
- Suites:
lint,security - Applies to: MySQL
What it reports
A MySQL GRANT PROXY whose proxied account — the one on the ON side — is an account the
server created for itself (the mysql. prefix) or the conventional superuser root.
Why it is not a privilege finding
PROXY is not a privilege over data, and not one over the server either. It is permission to
connect as somebody else: the proxying account authenticates with its own credential and then
holds the proxied account's privileges instead of its own.
So a proxy onto root is a root session — and the statement that creates it names no administrative
privilege at all. A review that reads privilege lists sees nothing.
Why it has its own rule
PROXY reads as administrative, and 8.4 reports it under Context = 'Server Admin'. The obvious
move is to add it to the administrative-privilege list the sibling rule reads. That list describes
names appearing in a GRANT … ON *.* shape, and PROXY has no such shape:
GRANT PROXY ON 'proxied'@'host' TO 'proxy'@'host' -- accepted
GRANT PROXY ON *.* TO 'proxy'@'host' -- ERROR 1064, a syntax error
Its object is an account. An entry in that list could never have matched anything, so it sits in that artifact's excluded set and points here instead.
Flagged
DB::statement("GRANT PROXY ON 'root'@'localhost' TO 'app_runtime'@'%'");
Preferred
DB::statement("GRANT PROXY ON 'app_directory'@'localhost' TO 'app_runtime'@'%'");
The mechanism stays. Proxying is a legitimate authentication arrangement — it is how an external directory maps onto a local account — and the safe form is the same statement onto an account that is not the server's. Impersonating it then grants exactly what that account holds and no more.
It judges the target, not the shape
A rule that reported every GRANT PROXY would be switched off in a week. Measured on 8.4.10, a
stock server ships one proxy of its own accord:
mysql.proxies_priv: root@localhost -> ''@'' with_grant = 1
So the shape is not the problem; who is being impersonated is.
What it cannot see
An arbitrary privileged account — admin@%, deployer@10.% — is invisible here. Nothing in the
statement says what an account holds, and answering that from a name would be a guess. The question
belongs to a catalog reading of mysql.proxies_priv against the privileges each target actually
carries.
root is a convention, not a reserved name. An installation that renamed it is not reported, and
one whose root was replaced by an unprivileged placeholder is reported anyway — the name is what a
statement offers.
The severity does not vary with which administrative target it found. mysql.infoschema holds no
SUPER and mysql.session does; both report at High, because a project account should be
impersonating neither.
Silent on PostgreSQL
PostgreSQL has no GRANT PROXY. Its nearest equivalent is role membership, an ordinary
GRANT <role> TO <role> that the role rules already read, so no PostgreSQL statement can make this
rule speak.
Sources
- MySQL 8.4 — Proxy Users — a proxy user assumes the privileges of the proxied user: it authenticates with its own credential and then holds the proxied account's privileges rather than its own
- MySQL 8.4 —
GRANT—PROXYhas its own syntax, where the object is an account rather than a privilege level