Defenses Against SQL Injection – Java Backend Security Guide
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
The primary SQL-injection defense is parameter binding. In JDBC that means PreparedStatement; in JPA it means named or positional parameters. User text remains the value of a placeholder and cannot add an operator or close a quote.
Parameters do not represent SQL structure such as table names, column names, operators, or sort direction. If the product supports one of those choices, I map an incoming enum or key to a small set of trusted fragments. Criteria API, Specifications, or another safe query builder can help with dynamic predicates.
Then I reduce the blast radius with a least-privilege database account, narrow schema access, safe error responses, patched drivers and ORM libraries, monitoring, and tested recovery. Stored procedures help only when they bind values rather than concatenate dynamic SQL. A WAF may catch common probes, but it can be bypassed and does not remove the vulnerable query.
Defenses Against SQL Injection – Java Backend Security Guide
Direct Answer
Use parameter binding for every data value. For query structure that cannot be bound—such as a selected column or sort direction—map the request to a small set of server-defined fragments. Add least-privilege database accounts, safe stored procedures and ORM use, restricted error details, dependency patching, monitoring, and tested backups. A WAF can detect or block some probes but is not a substitute for fixing query construction.
Code Example
// You CANNOT bind a column name with ?. Validate it against an allowlist.
private static final Set<String> SORTABLE =
Set.of("name", "price", "created_at"); // known-good columns only
public List<Product> search(String term, String sortColumn) {
if (!SORTABLE.contains(sortColumn)) {
throw new IllegalArgumentException("Invalid sort column"); // reject
}
// term is bound; sortColumn is from a trusted allowlist, not raw input
String sql = "SELECT * FROM products WHERE name LIKE ? ORDER BY " + sortColumn;
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, "%" + term + "%");
// ... map ResultSet to Product ...
}
return List.of();
}
Values use ?; the one thing that can't be a parameter (the column name) is constrained to a fixed allowlist.
Common Mistakes & Pitfalls
- "Sanitize input" as the whole answer: parameterization is the real fix; validation is a supporting layer.
- Blocklisting characters: stripping quotes or
--is fragile and bypassable via encodings. Prefer allowlists. - Relying on a WAF: useful for detection, but it can be evaded — never your only control.
- Over-privileged DB user: running the app as a DB admin means any injection can drop tables. Use least privilege.
- Stored procedures that concatenate: a proc that builds dynamic SQL from input is just as injectable.
Check your understanding
Answer these without looking back at the article.
- Why is allowlist validation preferred over blocklisting dangerous characters?
- How do stored procedures help, and can they still be vulnerable?
- Should a WAF ever be your only SQL injection defense?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.