APEX Educational Institute

SQL Injection Explained: How It Works and How to Prevent It

Understand how SQL injection attacks work with a simple vulnerable login example, then fix it with parameterised queries in Java, Python and Node.js, plus defence-in-depth measures.

Intermediate | 3 min read | Updated

SQL injection (SQLi) happens when user input is placed directly into a SQL query, so an attacker can change what the query does. It remains one of the most damaging web vulnerabilities: it can expose entire databases, bypass logins or delete data. The good news is that it is completely preventable.

Only test for SQL injection on systems you own or have written permission to test. Unauthorised testing is illegal.

A vulnerable login

java
// DO NOT DO THIS
String sql = "SELECT * FROM users WHERE email = '" + email +
             "' AND password_hash = '" + hash + "'";
ResultSet rs = statement.executeQuery(sql);

If someone types this into the email field:

text
' OR '1'='1' --

the query becomes:

sql
SELECT * FROM users WHERE email = '' OR '1'='1' --' AND password_hash = '...'

'1'='1' is always true and -- comments out the password check, so the attacker can log in as the first user, often an admin.

Why it happens

The database cannot tell the difference between your SQL code and the user's data when both are mixed into one string. The fix is to keep them separate.

The fix: parameterised queries (prepared statements)

The query structure is sent first with placeholders; values are sent separately and are always treated as data, never as code.

Java (JDBC)

java
String sql = "SELECT id, password_hash FROM users WHERE email = ?";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
    ps.setString(1, email);
    try (ResultSet rs = ps.executeQuery()) {
        // verify the password with BCrypt against password_hash
    }
}

Python (psycopg / PostgreSQL)

python
cur.execute("SELECT id, password_hash FROM users WHERE email = %s", (email,))
row = cur.fetchone()

Note the tuple (email,). Never use f-strings or % formatting to build the SQL yourself.

Node.js (pg)

javascript
const { rows } = await pool.query(
  "SELECT id, password_hash FROM users WHERE email = $1",
  [email]
);

ORMs such as Hibernate/JPA, SQLAlchemy, Prisma and Drizzle parameterise automatically when you use their query builders. They become vulnerable again if you concatenate strings into raw SQL methods.

Things parameters cannot protect

Placeholders work for values, not for table names, column names or ORDER BY directions. Use an allow-list:

python
ALLOWED_SORT = {"name", "created_at", "fee"}
sort = request.args.get("sort", "name")
if sort not in ALLOWED_SORT:
    sort = "name"
cur.execute(f"SELECT name, fee FROM courses ORDER BY {sort}")

Defence in depth

Parameterised queries are the primary fix. Add these layers too:

LayerWhat it does
Least-privilege DB userThe app account cannot drop tables or read unrelated schemas
Input validationReject input that does not match the expected format (e.g. email, numeric ID)
Generic error messagesNever show database errors to users; log them on the server
Web Application FirewallBlocks many common attack patterns (a safety net, not a fix)
MonitoringAlert on unusual query errors or spikes
Password hashingEven if data leaks, passwords are hashed with bcrypt or Argon2

Types of SQL injection (for testers)

  • In-band (error-based / UNION-based): results or errors appear directly in the page.
  • Blind (boolean / time-based): no visible output; the attacker infers data from true/false page differences or response delays.
  • Out-of-band: data is sent to an external server (rarer).

Testers check every input (forms, URL parameters, headers, JSON bodies) with special characters such as ' and look for errors or behaviour changes. Authorised tools like OWASP ZAP and sqlmap automate this in permitted test environments.

Interview questions

  • Why do prepared statements prevent SQLi? The SQL structure is compiled before values are bound, so input can never change the query's logic.
  • Is escaping quotes enough? No. Escaping is error-prone and database-specific; parameterisation is the reliable fix.
  • Can stored procedures be vulnerable? Yes, if they build dynamic SQL by concatenating their parameters.

Next steps

Search your own projects for string-built SQL and replace every instance with parameters. Learn the full OWASP Top 10, or practise ethical hacking safely in our Cyber Security + AI course.

Master it hands-on

Cyber Security

Cyber Security + AI

Networking, ethical hacking, web security, SOC and AI security.

16 weeks Beginner to Advanced
Online LiveRecorded Course