VNX-JAVA-028 – SQL injection via string concatenation in Java
Overview
This rule detects calls to JDBC methods .execute(), .executeQuery(), and .executeUpdate() where the SQL string argument is constructed via string concatenation (+), .concat(), or String.format(). When user-supplied data is embedded directly into a SQL query string without parameterization, an attacker can manipulate the query’s structure — altering its logic, extracting unauthorized data, modifying records, or executing administrative commands depending on the database user’s privileges. This is classified as CWE-89 (Improper Neutralization of Special Elements used in an SQL Command).
SQL injection remains one of the most prevalent and impactful vulnerabilities in web applications. Despite decades of awareness, string-concatenated queries continue to appear in Java codebases due to developer convenience, copied legacy patterns, and dynamic query generation for reporting or search features. Even a single injectable parameter in an otherwise safe codebase can expose the entire database.
Severity: High | CWE: CWE-89 – Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’) | OWASP: A03:2021 – Injection | CAPEC: CAPEC-66 – SQL Injection | ATT&CK: T1059.003
Why This Matters
A successful SQL injection attack can result in complete compromise of the database: full data exfiltration, unauthorized modification or deletion of records, and in some configurations, remote code execution via database-side features such as xp_cmdshell (SQL Server) or LOAD_FILE/INTO OUTFILE (MySQL). Authentication bypass via injection into login queries (' OR '1'='1) is a well-documented attack pattern that requires no database knowledge from the attacker.
In regulated environments (PCI-DSS, HIPAA, GDPR), SQL injection leading to data exposure carries mandatory breach notification requirements and significant fines. Attackers actively scan for SQL injection using automated tools; vulnerable endpoints are typically discovered and exploited within hours of exposure.
What Gets Flagged
// FLAGGED: string concatenation in executeQuery
String query = "SELECT * FROM users WHERE username = '" + username + "'";
ResultSet rs = stmt.executeQuery(query);
// FLAGGED: String.format used to construct SQL
String sql = String.format("DELETE FROM sessions WHERE user_id = %s", userId);
stmt.executeUpdate(sql);
// FLAGGED: .concat() in execute call
stmt.execute("UPDATE accounts SET balance = " + amount + " WHERE id = " + id);
Remediation
- Replace all string-concatenated SQL with
PreparedStatementand positional parameters (?). The JDBC driver handles quoting and escaping, preventing injection regardless of input content. - For dynamic table or column names (which cannot be parameterized), use a strict allowlist of permitted identifiers and validate against it before interpolation.
- Apply the principle of least privilege to database accounts used by the application — a read-only account cannot be abused to
DROPtables even if injection is possible. - Use an ORM (Hibernate, jOOQ) with parameterized query APIs to eliminate manual SQL construction.
// SAFE: PreparedStatement with parameterized query
String sql = "SELECT * FROM users WHERE username = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
// process results
}
}
// SAFE: multiple parameters
String updateSql = "UPDATE accounts SET balance = ? WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(updateSql)) {
pstmt.setBigDecimal(1, newBalance);
pstmt.setLong(2, accountId);
pstmt.executeUpdate();
}