security_control_failure, high severity, certain confidence.
src/sql_jsc/mysql/MySQLConnection.rs:662
SSLMode::Require failed open when the MySQL server handshake did not advertise CLIENT_SSL. A malicious MySQL server or network MITM could omit CLIENT_SSL, causing the client to skip TLS setup and continue authentication over the existing plaintext socket despite ssl_mode=require.
Verified by Swival.dev Security Scanner: https://swival.dev
- The client connects with
ssl_modeset toRequire. - The server handshake is controlled by a malicious MySQL server or network MITM.
- The hostile handshake omits
CLIENT_SSL.
handle_handshakeintersects desired client capabilities with server-advertised capabilities.- If the hostile handshake omits
CLIENT_SSL, the negotiated capabilities also omitCLIENT_SSL. - The TLS request and socket upgrade path is skipped because
self.capabilities.CLIENT_SSLis false. tls_statusis then set toTLSStatus::SslNotAvailable.- The fallback branch only rejected
SSLMode::VerifyCaandSSLMode::VerifyFull. SSLMode::Requirewas explicitly allowed to continue withPreferandDisable.- Execution then reached
send_handshake_response(), sending authentication on the non-TLS socket. - For
caching_sha2_password, full-auth continuation was worse because the later check usedssl_mode == Disablerather than actual TLS state, allowingRequireto send the password as if TLS were active.
SSLMode::Require means the connection must not proceed without TLS. The server handshake is attacker-controlled input in the threat model, and omitting CLIENT_SSL deterministically forced a plaintext fallback. This directly violates the security control and can expose authentication material over plaintext.
Reject non-CLIENT_SSL handshakes when ssl_mode is Require or stronger.
The patch moves SSLMode::Require into the same rejection path as SSLMode::VerifyCa and SSLMode::VerifyFull when TLS is unavailable. SSLMode::Prefer and SSLMode::Disable remain allowed to continue without TLS, preserving the intended fallback behavior only for modes that permit plaintext.
None
diff --git a/src/sql_jsc/mysql/MySQLConnection.rs b/src/sql_jsc/mysql/MySQLConnection.rs
index 77382ca441..afbe03af50 100644
--- a/src/sql_jsc/mysql/MySQLConnection.rs
+++ b/src/sql_jsc/mysql/MySQLConnection.rs
@@ -701,13 +701,10 @@ impl MySQLConnection {
self.tls_status = TLSStatus::SslNotAvailable;
match self.ssl_mode {
- SSLMode::VerifyCa | SSLMode::VerifyFull => {
+ SSLMode::Require | SSLMode::VerifyCa | SSLMode::VerifyFull => {
return Err(AnyMySQLError::AuthenticationFailed);
}
- // require behaves like prefer for postgres.js compatibility,
- // allowing graceful fallback to non-SSL when the server
- // doesn't support it.
- SSLMode::Require | SSLMode::Prefer | SSLMode::Disable => {}
+ SSLMode::Prefer | SSLMode::Disable => {}
}
}
// Send auth response