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.
JOptimize detects this automatically
Scan your Java project in 30 seconds — free, no credit card.
// ❌ 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.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.