Sql Injection with Mutual Tls
How Sql Injection Manifests in Mutual Tls
SQL Injection vulnerabilities in Mutual TLS (mTLS) environments exploit the trust established between client and server certificates. While mTLS provides strong authentication, it doesn't prevent SQL injection in the application layer where certificate validation occurs.
Consider a typical mTLS setup where client certificates are validated before processing requests. An attacker with a compromised or fraudulently obtained certificate can still exploit SQL injection in the application logic:
func handleRequest(w http.ResponseWriter, r *http.Request) {
// mTLS ensures client certificate is valid
clientCert := r.TLS.PeerCertificates[0]
// Vulnerable: certificate validation doesn't prevent SQL injection
userID := r.FormValue("user_id")
query := fmt.Sprintf("SELECT * FROM users WHERE id = %s AND cert_serial = '%s'",
userID, clientCert.SerialNumber)
rows, err := db.Query(query)
// ...
}The mTLS layer only verifies the certificate chain and revocation status. It doesn't sanitize SQL inputs or prevent malicious query construction. An attacker with a valid certificate can still inject:
user_id=1 OR 1=1 --This bypasses authorization checks even when mTLS authentication succeeds. The certificate validates, but the SQL query returns all user records.
Another mTLS-specific scenario involves certificate attribute injection. Some applications use certificate fields (like Common Name or Organizational Unit) in database queries:
String cn = cert.getSubjectX500Principal().getName();
String query = "SELECT * FROM users WHERE cert_cn = '" + cn + "'";
If the certificate's Common Name contains special characters or SQL syntax, injection becomes possible. An attacker can craft certificates with malicious values that survive mTLS validation but break SQL queries.
Mutual Tls-Specific Detection
Detecting SQL injection in mTLS environments requires testing both the certificate validation and application layers. middleBrick's black-box scanning approach is particularly effective here because it tests the actual attack surface without requiring source code access.
When scanning mTLS endpoints, middleBrick first establishes a valid TLS connection with client certificate authentication. The scanner then tests SQL injection patterns across all input parameters, including:
- Query parameters that might be used in database lookups
- Certificate-derived values used in application logic
- Headers that pass through mTLS middleware
- Request bodies that interact with database operations
middleBrick's SQL injection detection includes parameterized testing with common payloads:
user_id=1' OR '1'='1
user_id=1' UNION SELECT password FROM users --
user_id=1'; DROP TABLE users; --The scanner monitors for SQL error responses, unexpected data exposure, or changes in response structure that indicate successful injection. For mTLS specifically, middleBrick tests whether certificate attributes are being incorporated into SQL queries and whether they're properly sanitized.
middleBrick also checks for timing-based SQL injection vulnerabilities, which are particularly relevant in mTLS environments where requests might be slower due to certificate validation overhead. The scanner uses techniques like:
user_id=1 AND SLEEP(5)
user_id=1' WAITFOR DELAY '00:00:05' --These tests help identify blind SQL injection where error messages are suppressed but timing differences reveal vulnerability.
Mutual Tls-Specific Remediation
Remediating SQL injection in mTLS environments requires addressing both the database layer and how certificate data is handled. The foundation is always prepared statements with parameterized queries:
# Vulnerable - certificate data concatenated into SQL
cert_sn = r.TLS.PeerCertificates[0].SerialNumber
query = f"SELECT * FROM users WHERE cert_serial = '{cert_sn}' AND id = {user_id}"
# Secure - parameterized queries
query = "SELECT * FROM users WHERE cert_serial = ? AND id = ?"
cursor.execute(query, (cert_sn, user_id))
Even when using mTLS for authentication, never trust certificate fields as safe inputs. Always validate and sanitize certificate-derived data before using it in SQL queries:
const cert = req.socket.getPeerCertificate();
const certSn = cert.serialNumber;
// Validate certificate serial number format
if (!/^[0-9a-fA-F]+$/.test(certSn)) {
throw new Error('Invalid certificate serial number');
}
// Use parameterized query
const query = 'SELECT * FROM users WHERE cert_serial = ? AND user_id = ?';
const params = [certSn, req.params.userId];
For mTLS-specific certificate handling, implement strict validation of certificate attributes before database use:
var cert = context.Connection.ClientCertificate;
var cn = cert.Subject?.Split(',').FirstOrDefault(s => s.Trim().StartsWith("CN="));
if (cn == null || !cn.StartsWith("CN=")) {
throw new UnauthorizedAccessException();
}
var commonName = cn.Substring(3).Trim();
// Parameterized query with certificate data
using (var cmd = new SqlCommand("SELECT * FROM Users WHERE CertCN = @cn AND Id = @id", conn)) {
cmd.Parameters.AddWithValue("@cn", commonName);
cmd.Parameters.AddWithValue("@id", userId);
// ...
}
Additionally, implement certificate field whitelisting to prevent injection through certificate attributes:
func validateCertField(field string) bool {
// Allow only alphanumeric and basic punctuation
matched, _ := regexp.MatchString(`^[a-zA-Z0-9 .-]+$`, field)
return matched
}
certCN := clientCert.Subject.CommonName
if !validateCertField(certCN) {
http.Error(w, "Invalid certificate", http.StatusBadRequest)
return
}
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |