SQL Injection Prevention in PHP
In this lesson: Stop SQL injection in PHP using PDO prepared statements — attack example, safe code and checklist for login forms. Secure now.
What is SQL injection
SQL injection is an attack where a user sneaks SQL code into an input — like a login box — and, if you build your query by pasting that input in, the database runs the attacker's code. It can read private data, change records, or even delete whole tables. It is one of the most serious web security risks.
How the attack works
Imagine a login that pastes the email straight into SQL. A normal user enters their email; an attacker enters:
' OR '1'='1
The query becomes ... WHERE email = '' OR '1'='1', which is always true — so the attacker logs in without a password. The danger is treating input as part of the SQL.
The dangerous way
<?php
// NEVER do this:
$email = $_POST["email"];
$sql = "SELECT * FROM users WHERE email = '$email'";
$result = $pdo->query($sql);
?>
Here the user's input becomes part of the SQL text. Any SQL they type runs. This single pattern is the cause of almost every injection attack.
The safe way: prepared statements
<?php
$email = $_POST["email"];
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
?>
The ? is a placeholder. The real value goes in execute(), and the database treats it strictly as data — never as SQL. Even ' OR '1'='1 is just harmless text now.
Named placeholders
PDO also supports clearer named placeholders for longer queries.
<?php
$stmt = $pdo->prepare(
"SELECT * FROM users WHERE email = :email AND status = :status"
);
$stmt->execute([":email" => $email, ":status" => "active"]);
?>
Named placeholders like :email make it obvious which value goes where — helpful when a query has several inputs.
Extra good habits
- Least privilege: give the database user only the permissions it needs.
- Validate input: still check that data is the right shape (a valid email, a number, etc.).
- Hide errors in production: do not show raw SQL errors to users; log them instead.
- Escape output: when displaying data, use
htmlspecialcharsto also block XSS.
Common mistakes
- Pasting input into SQL. The root cause — always use placeholders.
- Skipping prepared statements for SELECT. Search boxes are a classic injection point too.
- Relying only on escaping. It is easy to forget; prepared statements are safer.
- Showing raw SQL errors. They reveal your structure to attackers.
Summary
- SQL injection runs an attacker's SQL through unsafe input.
- The cause is pasting user input directly into a query.
- Prepared statements send SQL and values separately — the reliable fix.
- Use placeholders (
?or:name) for every user value, including SELECT. - Add validation, least privilege and hidden errors as extra layers.
Next, apply this in PHP MySQL connection, or block XSS with XSS prevention.
