Skip to content

Intermediate · Backend security · topic 37 of 54 · 25 min

SQL injection and how to stop it

Explanation

SQL injection happens when text typed by a user is glued into an SQL query, so the user can change what the query does. One clever input can show every row, delete a table or skip a login. The fix is simple: send values separately from the query with placeholders (parameterised queries), and give the app a database user with only the rights it needs.

How the attack works

An SQL query is a piece of text that the database reads as instructions. If your code builds that text by joining strings with user input, the user is now helping to write your instructions. That is injection: user input that becomes code.

javascript
// DANGER: do not write code like this
const rollNo = req.query.roll; // typed by the user
const sql = "SELECT name, marks FROM results WHERE roll_no = '" + rollNo + "'";
const rows = await db.query(sql);
Building a query by joining strings (runs on a server with Node.js). This is the bug.

A normal student types R101 and gets one row. Now an attacker types this into the box: x' OR '1'='1 . Paste it into the string and look at the query the database receives.

sql
-- The attacker typed:  x' OR '1'='1
SELECT name, marks FROM results WHERE roll_no = 'x' OR '1'='1'
-- '1'='1' is always true, so EVERY row comes back
The quote in the input closed the string early, and the rest became part of the condition.
Think of it like this: Imagine a bank form that says: "Pay ____ rupees to Ravi". You write: "100 rupees to Ravi, and also pay 50,000". If the clerk copies your words into the order without thinking, the extra instruction gets done too. A safe clerk only accepts a number in that box, never new instructions.

The real damage can be large: reading every user's email and password hash, logging in as admin without a password, changing prices, or deleting tables. SQL injection has caused many big data leaks.

The fix: parameterised queries

A parameterised query (also called a prepared statement) sends the SQL text and the values separately. The SQL has placeholders where values go. The database driver sends the values on their own, so they are always treated as data, never as code, whatever quotes they contain.

javascript
// node-postgres (package "pg"): placeholders are $1, $2, ...
const { rows } = await pool.query(
  'SELECT name, marks FROM results WHERE roll_no = $1',
  [req.query.roll]
);
PostgreSQL with node-postgres: the value travels in the array, not in the text (runs on a server with Node.js).
javascript
// mysql2: placeholders are ?
const [rows] = await connection.execute(
  'SELECT name, marks FROM results WHERE roll_no = ? AND year = ?',
  [req.query.roll, 2026]
);
MySQL with mysql2: each ? is filled from the array in order (runs on a server with Node.js).

An ORM (a library that builds SQL for you, like Prisma, Sequelize or Drizzle) uses parameters for normal queries, which helps a lot. But every ORM also has a way to run raw SQL. Raw queries still need placeholders, and string-building there is just as dangerous.

Careful: Placeholders work for values, not for table or column names. If a user picks the sort column (?sort=price), check it against an allow-list such as ["name", "price", "created_at"] and use a default when it is not in the list. Never paste it in directly.
Tip: Least privilege: the database user your app logs in with should have only the rights it needs. A shop app usually needs SELECT, INSERT and UPDATE on a few tables. It should not be allowed to DROP tables or read the admin schema. Then even a successful injection does less damage.
DoDo not
Use $1 or ? placeholders for every valueJoin user text into SQL strings
Use an allow-list for column names and sort orderTrust a column name from the URL
Validate input type and length tooRely only on removing quotes by hand
Give the app user only the rights it needsConnect as the database owner or superuser

The same idea applies beyond SQL. NoSQL databases, shell commands and LDAP queries can be injected too. The rule is always the same: keep code and data apart.

Watch the lesson

SQL injection and how to stop it · 1/7

SQL injection

SQL injection is one of the oldest web attacks, and it still works on careless code.

Prefer a video by a teacher? Find free videos on “sql injection explained parameterized queries” on YouTube ↗

In real life: The login box that let anyone in

A small coaching institute built a login page with this query: SELECT * FROM users WHERE email = '<email>' AND password = '<password>'. Someone typed admin@example.in' -- as the email. In SQL, -- starts a comment, so the password check was ignored and they were logged in as admin. The fix was two changes: a parameterised query with placeholders, and storing only password hashes (checked with bcrypt) instead of plain passwords.

Practice (0/2 done)

Task 1 · Code · SQL

See the danger for yourself. The results site runs: "SELECT name, marks FROM results WHERE roll_no = '" + input + "'". An attacker types: x' OR '1'='1 . Write the exact query the server ends up running (put the input inside the quotes) and run it. You should see EVERY student's marks.

Task 2 · Code · Javascript

Write buildQuery(filters) for a product search. It returns { sql, params } for PostgreSQL. Start from "SELECT id, name, price FROM products". Supported filters, always in this order: city (city = $n), category (category = $n), maxPrice (price <= $n). Join conditions with " AND " after " WHERE ". Ignore any other keys. Never put a filter value into the sql text.

Quick quiz

Question 1

1.What causes SQL injection?

Question 2

2.Why does the input x' OR '1'='1 return every row?

Question 3

3.Which line is safe with node-postgres?

Question 4

4.A user chooses the sort column with ?sort=price. How should you use it?

Question 5

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

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: The OWASP Top 10: the most common web risks