Expert · JDBC, Maven, Gradle and JUnit · topic 47 of 60 · 20 min
SQL injection and safe queries
Explanation
How the attack works
// DANGEROUS: building SQL from user input
String email = request.getParameter("email");
String sql = "SELECT * FROM users WHERE email = '" + email + "'";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);If a user types the email ' OR '1'='1, the SQL becomes SELECT * FROM users WHERE email = '' OR '1'='1'. The condition is always true, so the query returns EVERY user. Other inputs can add UNION SELECT to read other tables, or comment out the rest of a query with --. SQL injection has been in the OWASP Top 10 list of web security risks for many years (as part of "Injection").
The fix: PreparedStatement parameters
String sql = "SELECT id, email, role FROM users WHERE email = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, email); // sent separately as DATA, never parsed as SQL
try (ResultSet rs = ps.executeQuery()) { … }
}With a prepared statement, the SQL text is fixed and sent to the database first; the parameter values are sent separately and treated purely as values. Whatever the user types, even quotes and SQL keywords, can only ever be compared as an email string.
What parameters cannot do
Placeholders work for VALUES, not for table names, column names or keywords like ASC/DESC. If users can choose a sort column, map their choice through a fixed allow-list in Java (switch on "price" → "price", anything else → "name"), never paste their text into SQL.
String orderBy = switch (sortParam) {
case "price" -> "price";
case "newest" -> "created_at DESC";
default -> "name";
};
String sql = "SELECT id, name, price FROM products WHERE category = ? ORDER BY " + orderBy; // safe: only allowed valuesDefence in depth
- Least privilege: the application's database user should have only the permissions it needs (no DROP TABLE, no access to unrelated schemas).
- Validate input: an order id should be a positive number before it reaches the database.
- Never log passwords, OTPs, card numbers or full SQL with sensitive values.
- Use ORMs and query builders (JPA, Spring Data, jOOQ) correctly: they parameterise for you, but string-built native queries are still vulnerable.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
SQL injection and safe queries · 1/6
In this lesson you will learn how SQL injection works and how to stop it.
Prefer a video by a teacher? Find free videos on “sql injection explained java preparedstatement prevention” on YouTube ↗
In real life: A login bypass on an old admin panel
A small company's internal admin page built its login query with string concatenation. A security review (done with permission) showed that typing ' OR '1'='1' -- as the username logged in as the first admin. The fix took ten minutes: a PreparedStatement and hashed password comparison (passwords are never compared as plain text in SQL).
String sql = "SELECT id, password_hash FROM admins WHERE username = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, username);
try (ResultSet rs = ps.executeQuery()) {
boolean ok = rs.next() && passwordEncoder.matches(password, rs.getString("password_hash"));
}
}Practice (0/3 done)
An injected login query became: SELECT email FROM users WHERE email = '' OR '1'='1'. Run exactly that (it is your own practice table) to see how many rows it leaks.
Which code is safe from SQL injection?
In your own local practice project, write a small Java method findUserByEmail(Connection c, String email) first with string concatenation, test it with the input ' OR '1'='1 and observe that it returns all rows. Then rewrite it with a PreparedStatement and show that the same input returns nothing.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Maven: building and dependenciesJDBC, Maven, Gradle and JUnit · same module
- Gradle in briefJDBC, Maven, Gradle and JUnit · same module
- SQL injection and how to stop itBackend Development · Backend security
- SQL injection (SQLi)Ethical Hacking and Penetration Testing (legal labs) · Web attacks and their fixes
- Choosing a database, and keeping it safeSQL and Databases · Procedures, triggers, SQL engines and NoSQL
- Secure coding basicsCyber Security: from zero to SOC analyst · Malware and web security
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is the main fix for SQL injection in Java?
Answer: PreparedStatement with parameters
Parameters are the correct default.
Can a ? placeholder be used for a column name in ORDER BY?
Answer: No: use a fixed allow-list in code
Placeholders are for values.
What does "least privilege" mean for the app's database user?
Answer: It has only the permissions the app needs
Limits the damage if something goes wrong.
Which is legal and ethical practice?
Answer: Testing your own app or platforms built for practice
Only test with permission.
What causes SQL injection?Related
Answer: Joining user input directly into SQL text
When user text is glued into the query, it can change the query itself. That is injection.
Parameterised queries protect you because input is…Related
Answer: Treated only as data, never as SQL code
The database treats placeholders' values as pure data.
From SQL injection (SQLi)
What does "least privilege" mean?Related
Answer: Each user or app gets only the rights it needs
Small rights mean small damage when an account is misused or leaked.
Where must input validation always happen?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “JDBC, Maven, Gradle and JUnit” before its exam: Maven: building and dependencies, Gradle in brief, Unit testing with JUnit.
- Module 10 exam: JDBC, Maven, Gradle and JUnit · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Spring Boot basics. 60 topics left in the course before the final exam.