Auth Bypass in Mysql
How Auth Bypass Manifests in Mysql
Auth bypass in MySQL contexts typically occurs when authentication mechanisms are improperly implemented or when SQL injection vulnerabilities allow attackers to manipulate authentication queries. The most common pattern involves exploiting boolean logic in WHERE clauses that determine user validation.
Consider this vulnerable authentication function:
const mysql = require('mysql2/promise');
async function authenticate(username, password) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'app_user',
password: 'app_pass',
database: 'auth_db'
});
const query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;
const [rows] = await connection.execute(query);
return rows.length > 0;
}This code is vulnerable because it directly interpolates user input into the SQL query. An attacker can bypass authentication by submitting a username like admin' -- which comments out the password check, or use more sophisticated payloads:
admin' OR 1=1 --
admin' OR '1'='1
admin' UNION SELECT 1, 'admin', 'password', 1 FROM users WHERE 'a'='aAnother MySQL-specific auth bypass pattern involves exploiting the mysql_native_password authentication plugin. If the MySQL server is configured with weak password hashing or allows empty passwords, attackers can connect directly to the database using tools like mysql CLI without proper credentials.
Stored procedure vulnerabilities also enable auth bypass. Consider this stored procedure that authenticates users:
DELIMITER //
CREATE PROCEDURE Login(IN p_username VARCHAR(50), IN p_password VARCHAR(50))
BEGIN
DECLARE v_user_id INT;
SET v_user_id = (SELECT id FROM users WHERE username = p_username AND password = p_password);
IF v_user_id IS NOT NULL THEN
SELECT 'Authenticated' AS status, v_user_id;
ELSE
SELECT 'Failed' AS status;
END IF;
END //
DELIMITER ;This procedure is vulnerable to timing attacks and can be bypassed if the application doesn't properly handle the procedure's output. Additionally, MySQL's LOAD DATA INFILE and SELECT INTO OUTFILE commands, if accessible to unauthenticated users, can lead to data exfiltration and privilege escalation.
MySQL-Specific Detection
Detecting auth bypass vulnerabilities in MySQL requires both static analysis of application code and dynamic testing of authentication endpoints. Using middleBrick's API security scanner, you can identify these vulnerabilities through automated black-box testing.
middleBrick scans authentication endpoints by attempting various bypass techniques:
const middlebrick = require('middlebrick');
// Scan your authentication API endpoint
middlebrick.scan({
url: 'https://api.example.com/login',
method: 'POST',
payload: {
username: 'admin\' OR 1=1 --',
password: 'anything'
}
}).then(report => {
console.log('Security Score:', report.score);
console.log('Auth Bypass Findings:', report.findings.filter(f => f.category === 'BOLA/IDOR'));
});The scanner tests for common MySQL injection patterns including boolean-based blind injection, union-based extraction, and error-based techniques. It specifically looks for responses that indicate successful authentication bypass, such as HTTP 200 status codes with authenticated user data when invalid credentials were submitted.
For code-level detection, use MySQL's built-in audit logging to monitor authentication attempts:
SET GLOBAL audit_log_policy = 'ALL';
SET GLOBAL audit_log_format = 'JSON';
-- Check audit log for suspicious authentication patterns
SELECT * FROM mysql.audit_log
WHERE event_type = 'Connect'
AND STATUS = 'FAILED'
AND USER_HOST LIKE '%test%'
ORDER BY EVENT_TIME DESC;
Database-level detection should also include monitoring for unusual query patterns. Enable the general query log temporarily to capture authentication queries:
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';
-- Analyze authentication query patterns
SELECT argument FROM mysql.general_log
WHERE argument LIKE '%SELECT%FROM%users%'
AND event_time > NOW() - INTERVAL 1 HOUR;
middleBrick's LLM security features are particularly relevant for AI-powered authentication systems that might use MySQL backends. The scanner checks for system prompt leakage and prompt injection vulnerabilities that could compromise authentication logic in AI-driven applications.
MySQL-Specific Remediation
Remediating MySQL auth bypass vulnerabilities requires implementing proper input validation, using prepared statements, and following security best practices for database authentication.
The most critical fix is using parameterized queries instead of string interpolation:
const mysql = require('mysql2/promise');
async function authenticate(username, password) {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'app_user',
password: 'app_pass',
database: 'auth_db'
});
const [rows] = await connection.execute(
'SELECT * FROM users WHERE username = ? AND password = ?',
[username, password]
);
return rows.length > 0;
}
// Even better: use password hashing
async function authenticate(username, password) {
const connection = await mysql.createConnection(config);
const [rows] = await connection.execute(
'SELECT id, username, password_hash FROM users WHERE username = ?',
[username]
);
if (rows.length === 0) return false;
const user = rows[0];
const isValid = await bcrypt.compare(password, user.password_hash);
return isValid;
}For MySQL authentication configuration, ensure strong password policies and disable anonymous access:
-- Set strong password validation policy
SET GLOBAL validate_password_policy = 'STRONG';
SET GLOBAL validate_password_length = 14;
SET GLOBAL validate_password_mixed_case_count = 1;
SET GLOBAL validate_password_number_count = 1;
SET GLOBAL validate_password_special_char_count = 1;
-- Remove anonymous accounts
DELETE FROM mysql.user WHERE User = '';
FLUSH PRIVILEGES;
-- Limit authentication attempts
CREATE TABLE auth_attempts (
ip_address VARCHAR(45),
attempt_time TIMESTAMP,
attempts INT DEFAULT 1,
PRIMARY KEY (ip_address)
);
-- Stored procedure for rate-limited authentication
DELIMITER //
CREATE PROCEDURE SecureLogin(IN p_username VARCHAR(50), IN p_password VARCHAR(50), IN p_ip VARCHAR(45))
BEGIN
DECLARE v_attempts INT;
DECLARE v_last_attempt TIMESTAMP;
-- Check rate limiting
SELECT attempts, attempt_time INTO v_attempts, v_last_attempt
FROM auth_attempts WHERE ip_address = p_ip;
IF v_attempts >= 5 AND TIMESTAMPDIFF(MINUTE, v_last_attempt, NOW()) < 15 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Too many authentication attempts';
END IF;
-- Authenticate user
IF EXISTS (SELECT 1 FROM users WHERE username = p_username AND password = p_password) THEN
-- Successful login
DELETE FROM auth_attempts WHERE ip_address = p_ip;
SELECT 'Authenticated';
ELSE
-- Failed login, update attempts
IF v_attempts IS NULL THEN
INSERT INTO auth_attempts (ip_address, attempt_time) VALUES (p_ip, NOW());
ELSE
UPDATE auth_attempts SET attempts = attempts + 1, attempt_time = NOW() WHERE ip_address = p_ip;
END IF;
SELECT 'Failed';
END IF;
END //
DELIMITER ;Implement database-level access controls to limit what unauthenticated users can do:
-- Create limited-privilege authentication user
CREATE USER 'auth_app'@'localhost' IDENTIFIED BY 'StrongPass!23';
-- Grant only necessary permissions
GRANT SELECT ON auth_db.users TO 'auth_app'@'localhost';
REVOKE ALL PRIVILEGES ON *.* FROM 'auth_app'@'localhost';
FLUSH PRIVILEGES;
-- Enable strict SQL mode to prevent certain bypass techniques
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION';
For applications using MySQL with ORM libraries, ensure the ORM is configured to use parameterized queries by default:
const Sequelize = require('sequelize');
const sequelize = new Sequelize({
dialect: 'mysql',
host: 'localhost',
username: 'auth_app',
password: 'StrongPass!23',
database: 'auth_db',
logging: false,
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
}
});
// Always use model methods or parameterized queries
const User = sequelize.define('user', {
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password_hash: {
type: Sequelize.STRING,
allowNull: false
}
});
async function authenticate(username, password) {
const user = await User.findOne({
where: { username: username }
});
if (!user) return false;
const isValid = await bcrypt.compare(password, user.password_hash);
return isValid;
}Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |