MySQL privilege change auditing: GRANT, REVOKE, and unexpected SUPER

Unexpected privilege changes are high-impact, low-frequency events. A spike in Com_grant, an unknown account with SUPER, or a GRANT ALL outside a change window can signal privilege escalation, misconfigured automation, or migration debt. This guide covers how to detect, diagnose, and remove unauthorized grants and deprecated SUPER privileges in production.

What this means

MySQL increments status counters for GRANT, REVOKE, CREATE USER, ALTER USER, and DROP USER. The counters are cumulative; compute deltas over fixed intervals instead of reading absolute values.

Current global privileges live in mysql.user. Database-, table-, and column-level grants live in mysql.db, mysql.tables_priv, and mysql.columns_priv. In MySQL 8.0+, role relationships are in mysql.role_edges and mysql.default_roles.

mysql.user.password_last_changed is non-NULL for accounts using built-in authentication plugins. It updates on CREATE USER, ALTER USER, and SET PASSWORD, giving a coarse recency filter even when logs are disabled.

SUPER is deprecated since MySQL 8.0 and will be removed. It bundles administrative capabilities now exposed as granular dynamic privileges. Any privilege change outside a documented change window is at minimum an operational ticket. An unexpected GRANT ALL or SUPER grant from an unknown actor is a security incident.

Common causes

CauseWhat it looks likeFirst thing to check
Unauthorized privilege escalationUnknown account with SUPER or ALL PRIVILEGES; sudden Com_grant spikemysql.user sorted by password_last_changed DESC
Misconfigured automationIdentical grants rolled out across many hosts in a short windowAudit or general log for the service account
Deprecated SUPER migration debtCompliance scanner flags SUPER on non-admin usersmysql.user where Super_priv = 'Y'
Implicit user creation via GRANTLegacy scripts creating accounts without CREATE USERLogs for user creation events and matching CREATE USER entries

Quick checks

-- Cumulative privilege-modification counters
SHOW GLOBAL STATUS
WHERE Variable_name IN ('Com_grant','Com_revoke','Com_create_user','Com_alter_user','Com_drop_user');
-- Accounts with recent password or privilege changes
SELECT user, host, password_last_changed
FROM mysql.user
WHERE password_last_changed IS NOT NULL
ORDER BY password_last_changed DESC;
-- Current SUPER holders against your baseline
SELECT user, host
FROM mysql.user
WHERE Super_priv = 'Y';
-- Is general logging available?
SHOW GLOBAL VARIABLES LIKE 'general_log';
SHOW GLOBAL VARIABLES LIKE 'log_output';
-- Recent grant activity from the general log
-- Warning: this query scans a high-volume table and can impact performance.
SELECT event_time, user_host, argument
FROM mysql.general_log
WHERE argument LIKE 'GRANT%'
   OR argument LIKE 'REVOKE%'
   OR argument LIKE 'CREATE USER%'
   OR argument LIKE 'ALTER USER%'
   OR argument LIKE 'DROP USER%'
ORDER BY event_time DESC
LIMIT 50;

How to diagnose it

flowchart TD
  A[Com_grant spike] --> B{Audit log available?}
  B -->|Yes| C[Query for GRANT/REVOKE]
  B -->|No| D[Check password_last_changed]
  C --> E[Identify actor]
  D --> E
  E --> F{Authorized?}
  F -->|No| G[Revoke and investigate]
  F -->|Yes| H[Update baseline]
  1. Establish a privilege baseline. Dump mysql.user, mysql.db, mysql.tables_priv, mysql.columns_priv, and, in MySQL 8.0+, mysql.role_edges and mysql.default_roles to a versioned snapshot. Without a baseline you cannot distinguish a new grant from historical configuration.

  2. Correlate counter deltas. Sample Com_grant and Com_revoke before and after the suspected window. A sustained increase indicates active changes.

  3. Inspect password_last_changed. Query mysql.user for accounts with a recent timestamp. This column is non-NULL only for built-in authentication plugins, so it is a coarse filter, not proof of a grant.

  4. Query logs. If general_log is enabled, filter it for GRANT, REVOKE, CREATE USER, ALTER USER, and DROP USER. Keep the window short; the general log grows fast and adds overhead. For a real audit trail, use MySQL Enterprise Audit or the MariaDB Audit Plugin.

  5. Identify the actor. Audit plugins and the general log record the MySQL database user, not the OS or end user. If a shared app or automation account executed the change, correlate the timestamp with CI/CD pipelines, deployment logs, or bastion session records. If the session is still open, check PROCESSLIST or performance_schema.threads.

  6. Assess SUPER and ALL PRIVILEGES scope. Count accounts with Super_priv = 'Y' and any global *.* grants outside your expected admin set. Map existing SUPER grants to the minimum required dynamic privileges and revoke the broad grant.

# Safe read-only baseline dump. On MySQL 5.7, omit role_edges and default_roles.
mysqldump mysql user db tables_priv columns_priv role_edges default_roles \
  > "mysql_privs_$(date +%F).sql"

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Com_grant rateDetects new grantsSustained spike outside maintenance
Com_revoke rateDetects privilege removal, possibly to hide activitySpike correlating with an incident
SUPER holder countTracks deprecated and overbroad grantsCount above the admin baseline
password_last_changed recencyIdentifies recently touched accountsEntries newer than the last approved change
Audit log eventsForensic statement-level evidenceGRANT/REVOKE/CREATE USER/ALTER USER from non-allowlisted hosts

Fixes

Revoke unauthorized grants

If an account received privileges outside a change window, revoke the specific privilege and scope rather than using REVOKE ALL unless the account should have no access.

-- Revoke a specific deprecated privilege
REVOKE SUPER ON *.* FROM 'unexpected_user'@'%';

Verify with SHOW GRANTS FOR 'unexpected_user'@'%';.

If the account is entirely unauthorized, drop it:

DROP USER 'unexpected_user'@'%';

Warning: DROP USER is immediate. If the account is used by a running application, connections will fail on their next authentication. Prefer REVOKE until you confirm the account is unused.

Replace SUPER with dynamic privileges

SUPER bundles capabilities now available as dynamic privileges. Replace it with the smallest set the account actually needs. For example:

REVOKE SUPER ON *.* FROM 'backup_svc'@'%';
GRANT BACKUP_ADMIN, SESSION_VARIABLES_ADMIN ON *.* TO 'backup_svc'@'%';

An account that only initiates replication should receive REPLICATION_SLAVE_ADMIN instead of SUPER.

Tradeoff: migration requires knowing exactly which SUPER operations the account uses. A too-narrow replacement breaks automation. Test in a non-production environment first.

Address role-based privilege inflation

In MySQL 8.0+, roles carry privileges that may be activated automatically or at login. An account can show few direct grants while inheriting broad access.

SHOW GRANTS FOR 'app_user'@'%';
SHOW GRANTS FOR 'app_user'@'%' USING 'admin_role';
SHOW GRANTS FOR 'admin_role';

Audit mysql.role_edges and mysql.default_roles regularly, and expand role grants during reviews so the effective privilege set is visible.

Prevention

  • Snapshot privileges weekly. Dump mysql.user, mysql.db, mysql.tables_priv, mysql.columns_priv, and, in 8.0+, mysql.role_edges and mysql.default_roles into version control and diff them.
  • Restrict GRANT execution. Do not use shared application credentials for schema or user changes. Use dedicated, named admin accounts.
  • Require explicit CREATE USER. Disable implicit user creation via GRANT by policy and automation linting.
  • Enable continuous auditing. Use MySQL Enterprise Audit or the MariaDB Audit Plugin. The general log is too expensive and unstructured for continuous security monitoring.
  • Alert on counter spikes. Page or ticket for any non-zero Com_grant or Com_revoke rate outside maintenance windows.
  • Audit SUPER quarterly. Migrate remaining SUPER grants to dynamic privileges before the privilege is removed.

How Netdata helps

  • Chart Com_grant and Com_revoke rates continuously to detect privilege changes as they happen.
  • Correlate privilege counter spikes with connection anomalies, such as Aborted_connects increases, that can indicate probing before escalation.
  • Feed custom SUPER-holder queries into Netdata via a script or custom collector to alert on baseline drift.
  • Cross-reference privilege change timestamps with slow query or replication lag charts to separate administrative activity from workload shifts.