Skip to content

Expert · JDBC, Maven, Gradle and JUnit · topic 47 of 60 · 20 min

SQL injection and safe queries

Explanation

SQL injection happens when user input is pasted into SQL text, letting an attacker change the query itself, for example logging in without a password or reading other customers' data. The fix is to always use PreparedStatement with ? parameters (or an ORM that does), never string concatenation. Add defence in depth: least-privilege database users, input validation and no secrets in logs.

How the attack works

java
// 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

java
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.

java
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 values

Defence 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.
Careful: Escaping quotes by hand (replace("'", "''")) is not a reliable fix; encodings and database-specific tricks have broken such filters many times. Parameters are the only correct default. Test your own application only; trying injection on systems you do not own or have written permission to test is illegal in India under the IT Act, 2000 (for example sections 43 and 66) and in most countries.
Think of it like this: Building SQL with + is like a bank clerk who reads aloud whatever is written on a cheque, including "…and also pay the bearer everything in the vault". A prepared statement is a printed cheque form: the customer can only fill in the blank boxes, and the clerk treats whatever is in the amount box as an amount, never as an instruction.
Tip: Search your codebase for createStatement( and for SQL strings containing + with variables. Each one is worth a review.

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

SQL injection

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).

java
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)

Task 1 · Code · SQL

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.

Task 2 · Question

Which code is safe from SQL injection?

Task 3 · Practice

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

Question 1

1.What is the main fix for SQL injection in Java?

Question 2

2.Can a ? placeholder be used for a column name in ORDER BY?

Question 3

3.What does "least privilege" mean for the app's database user?

Question 4

4.Which is legal and ethical practice?

Read more (free)

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.

From SQL injection and how to stop it

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.

From Choosing a database, and keeping it safe

Where must input validation always happen?Related

Answer: On the server

Attackers bypass the browser; the server must check.

From Secure coding basics

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

  1. 3 more topics in “JDBC, Maven, Gradle and JUnit” before its exam: Maven: building and dependencies, Gradle in brief, Unit testing with JUnit.
  2. Module 10 exam: JDBC, Maven, Gradle and JUnit · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Spring Boot basics. 60 topics left in the course before the final exam.

← Previous: JDBC: talking to a database