SQL injection is ranked A03 in the OWASP Top 10 and has caused the biggest data breaches in history. Learn all the vulnerable patterns in Spring Boot — JDBC, JPA, native queries — and how to fix them permanently.
JOptimize Team
SQL injection happens when user input is concatenated directly into a SQL query, allowing attackers to manipulate the query logic. It's ranked A03 in the OWASP Top 10 and has been responsible for some of the biggest data breaches in history.
// ❌ Vulnerable — NEVER do this @GetMapping("/users") public List<User> searchUsers(@RequestParam String name) { String query = "SELECT * FROM users WHERE name = '" + name + "'"; return jdbcTemplate.query(query, userRowMapper); }
If an attacker passes ' OR '1'='1 as the name parameter, the query becomes:
SELECT * FROM users WHERE name = '' OR '1'='1'
This returns every user in the database. With more sophisticated payloads, attackers can delete tables, extract passwords, or execute OS commands.
// ❌ Vulnerable String sql = "SELECT * FROM users WHERE email = '" + email + "'"; jdbcTemplate.query(sql, rowMapper); // ✅ Safe — use named parameters String sql = "SELECT * FROM users WHERE email = :email"; namedParameterJdbcTemplate.query(sql, Map.of("email", email), rowMapper); // ✅ Also safe — use ? placeholders String sql = "SELECT * FROM users WHERE email = ?"; jdbcTemplate.query(sql, rowMapper, email);
// ❌ Vulnerable @Query(value = "SELECT * FROM users WHERE name = '" + "' + name + '" + "'", nativeQuery = true) List<User> findByName(String name); // ❌ Also vulnerable — string concatenation in @Query @Repository public interface UserRepository extends JpaRepository<User, Long> { default List<User> search(String term) { String sql = "SELECT * FROM users WHERE name LIKE '%" + term + "%'"; // ... } } // ✅ Safe — use named parameters in @Query @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) List<User> findByName(@Param("name") String name); // ✅ Even better — use JPQL (not nativeQuery) @Query("SELECT u FROM User u WHERE u.name = :name") List<User> findByName(@Param("name") String name);
// ❌ Vulnerable public List<User> search(String term) { String sql = "SELECT * FROM users WHERE name LIKE '%" + term + "%'"; return em.createNativeQuery(sql, User.class).getResultList(); } // ✅ Safe public List<User> search(String term) { String sql = "SELECT * FROM users WHERE name LIKE :term"; return em.createNativeQuery(sql, User.class) .setParameter("term", "%" + term + "%") .getResultList(); }
Spring Data derived query methods are safe by default — they always use parameterized queries:
// ✅ Always safe — Spring generates parameterized SQL List<User> findByEmail(String email); List<User> findByNameContaining(String name); Optional<User> findByEmailAndStatus(String email, String status);
Use derived queries whenever possible — they're the safest and most readable option.
JOptimize specifically detects string concatenation in SQL queries:
joptimize analyze . # → [CRITICAL] SQL Injection risk: query built with string concatenation. # User input can manipulate the query structure. # UserRepository.java:34 # # → [CRITICAL] SQL Injection risk: String.format() used to build SQL query. # ReportService.java:67
Both + concatenation and String.format() in SQL queries are flagged as CRITICAL.
Parameterized queries prevent SQL injection, but input validation adds an extra layer:
@RestController public class UserController { @GetMapping("/users/search") public List<User> search(@RequestParam @Size(max = 100) @Pattern(regexp = "[a-zA-Z0-9 ]+") String name) { return userService.search(name); } }
Enable Bean Validation in your Spring Boot app:
@Configuration public class ValidationConfig { @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); } }
Dynamic ORDER BY clauses can't use parameterized queries for column names:
// ❌ Vulnerable — column name can't be parameterized String sql = "SELECT * FROM users ORDER BY " + sortColumn + " " + sortDirection; // ✅ Safe — whitelist allowed columns private static final Set<String> ALLOWED_COLUMNS = Set.of("name", "email", "createdAt"); public List<User> findAll(String sortColumn, String sortDirection) { if (!ALLOWED_COLUMNS.contains(sortColumn)) { throw new IllegalArgumentException("Invalid sort column"); } String direction = "DESC".equalsIgnoreCase(sortDirection) ? "DESC" : "ASC"; String sql = "SELECT * FROM users ORDER BY " + sortColumn + " " + direction; return jdbcTemplate.query(sql, rowMapper); }
? placeholders or :namedParamWant to go deeper on Java security and OWASP vulnerabilities?
Java Security — OWASP Top 10 for Java Developers — hands-on course covering SQL injection, XSS, CSRF, authentication failures and more with Spring Boot examples.
JOptimize scans your entire codebase for SQL injection patterns — JDBC, JPA, native queries, String.format() — and flags them as CRITICAL with the exact file and line:
npm install -g @joptimize/cli joptimize auth jp_live_your_key joptimize analyze .
Or upload your ZIP at joptimize.io.
Master Spring Boot, security, and Java performance with hands-on courses.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.