Skip to main content

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 — GRANTPROXY has its own syntax, where the object is an account rather than a privilege level