SQL Injection and Security — Protecting Your Database from Malicious Input

Prevent SQL Injection

A school search form accepts a city. Directly inserting its text into a query can allow that text to change the intended condition.

SQL injection occurs when untrusted input is interpreted as part of a statement’s structure. Parameter binding keeps ordinary input values separate from SQL syntax.

  1. Explain and apply

    Bind a user-supplied value and explain why it must not become part of the SQL command.

  2. Work it out

    Bind the city value Pune to a fixed statement. Explain what happens if the supplied value contains quote characters.

  3. Avoid mistakes

    Escaping and filtering alone are fragile substitutes for binding.

Prevent SQL Injection: the request and its result

  1. School need

    A school search form accepts a city. Directly inserting its text into a query can allow that text to change the intended condition.

  2. Database operation

    SQL injection occurs when untrusted input is interpreted as part of a statement’s structure. Parameter binding keeps ordinary input values separate from SQL syntax.

  3. Check the outcome

    The parameter is a city value, not SQL grammar. A value containing a quote remains data instead of introducing a new OR condition. The Python and Java examples show their drivers’ actual binding APIs.

A conceptual model. The query examples below show the actual syntax, input data and results.

Understand what the database does

Write a fixed statement with the driver’s parameter placeholders, then supply the values through its binding API. Keep dynamic identifiers on an explicit allowlist. Validate business rules separately and use an account whose permissions match the application’s actual tasks.

Related concepts: Dynamic SQL and SQL with Python and SQL with Java.

What is SQL Injection?

SQL Injection is a code injection technique where malicious SQL is inserted into a query via user input. If not properly handled, it can manipulate or expose your entire database.

Basic Scenario – Unsafe Query

SQL · portable concepts; preserved examples identify their engine

Let’s assume a school has a student login system:

SELECT * FROM students
WHERE roll_no = '3' AND password = 'pass123';

This query is generated using user input, like this:

// Pseudo-code (not actual SQL)
"SELECT * FROM students WHERE roll_no = '" + input_roll + "' AND password = '" + input_pass + "'";

SQL Injection in Action

SQL · portable concepts; preserved examples identify their engine

Now imagine this input:

input_roll: 3  
input_pass: ' OR '1'='1

The final query becomes:

SELECT * FROM students
WHERE roll_no = '3' AND password = '' OR '1'='1';

Result: The condition always returns true, and the attacker logs in without a valid password.

Advanced Injection – DROP TABLE

SQL · portable concepts; preserved examples identify their engine

input_roll: 3  
input_pass: pass123'; DROP TABLE students; --

Final SQL:

SELECT * FROM students WHERE roll_no = '3' AND password = 'pass123';
DROP TABLE students;
--'

This deletes the entire students table. Terrifying, right?

How to Prevent SQL Injection

SQL · portable concepts; preserved examples identify their engine

1. Use Prepared Statements (Parameterized Queries)

This is the most effective solution. It separates data from SQL logic.

// In MySQL or PHP using PDO:
$stmt = $conn->prepare("SELECT * FROM students WHERE roll_no = ? AND password = ?");
$stmt->bind_param("is", $roll_no, $password);
$stmt->execute();

2. Validate and Sanitize Input

  • Use input types like number or email
  • Check length, pattern, and format of input

3. Escape Dangerous Characters

Escape quotes, semicolons, and dashes in input (only when using dynamic SQL — not recommended).

4. Use ORM or Query Builders

Frameworks like Django, Laravel, or Hibernate handle queries securely behind the scenes.

5. Least Privilege Access

The database user used by the app should not have DROP or ALTER rights.

Secure Example with Prepared Statement

SQL · portable concepts; preserved examples identify their engine

// PHP + MySQLi example
$stmt = $conn->prepare("SELECT name FROM students WHERE roll_no = ? AND password = ?");
$stmt->bind_param("is", $roll_no, $password);
$stmt->execute();

Even if someone tries to inject malicious SQL, it will be treated as plain input — not executable code.

Real-World School Use Cases

  • Prevent fake login during parent-teacher meetings
  • Secure marks upload pages used by staff
  • Protect student registration forms from malicious bots

Best Practices Checklist

Query discussed in this section:

// PHP + MySQLi example
$stmt = $conn->prepare("SELECT name FROM students WHERE roll_no = ? AND password = ?");
$stmt->bind_param("is", $roll_no, $password);
$stmt->execute();
  • Always use prepared statements
  • Validate all user inputs
  • Never concatenate user input into raw SQL
  • Limit DB user privileges
  • Use security-aware frameworks

Important differences and mistakes

Escaping and filtering alone are fragile substitutes for binding. A prepared statement does not make every concatenated identifier safe. An ORM can still execute unsafe raw SQL, and least privilege reduces potential damage without fixing an injected query.

Try it with the school data

Bind the city value Pune to a fixed statement. Explain what happens if the supplied value contains quote characters.

This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.

Practice input: practice_students
roll_nonameclassmarkscity
1Aarav10A85Pune
2Diya9B70Chennai
3Sneha10A85Pune

SQL · create only these practice tables in a disposable database:

CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');

Worked answer and reasoning

This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.

Practice input: practice_students
roll_nonameclassmarkscity
1Aarav10A85Pune
2Diya9B70Chennai
3Sneha10A85Pune

SQL · create only these practice tables in a disposable database:

CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');

SQL · this practice variation runs with SQLite 3; the preserved examples retain their stated dialect

Bind the value "Pune" through the driver’s parameter API. The question mark is a parameter marker, not literal SQL text to replace with string concatenation.

SELECT name FROM practice_students WHERE city = ? ORDER BY roll_no;
Expected query result
name
Aarav
Sneha

The parameter is a city value, not SQL grammar. A value containing a quote remains data instead of introducing a new OR condition. The Python and Java examples show their drivers’ actual binding APIs.

Check your understanding

Which approach safely handles a user-supplied city value?
Choose one answer. You can try again.

Answer and explanation

Bind it through the driver’s parameter API in a fixed statement.

The parameter is a city value, not SQL grammar. A value containing a quote remains data instead of introducing a new OR condition. The Python and Java examples show their drivers’ actual binding APIs.

Check your understanding

Which claim about this lesson’s practice result is justified?
Choose one answer. You can try again.

Answer and explanation

The parameter is a city value, not SQL grammar. A value containing a quote remains data instead of introducing a new OR condition. The Python and Java examples show their drivers’ actual binding APIs.

Escaping and filtering alone are fragile substitutes for binding. A prepared statement does not make every concatenated identifier safe. An ORM can still execute unsafe raw SQL, and least privilege reduces potential damage without fixing an injected query.

Use the idea independently

SQL injection occurs when untrusted input is interpreted as part of a statement’s structure. Parameter binding keeps ordinary input values separate from SQL syntax.

The parameter is a city value, not SQL grammar. A value containing a quote remains data instead of introducing a new OR condition. The Python and Java examples show their drivers’ actual binding APIs.

Continue your understanding with Dynamic SQL and SQL with Python and SQL with Java.

Source for the engine-specific behavior: MySQL 8.4 reference documentation.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: SQL User Management and Permissions — Controlling Access to Your Database →