How to Prevent SQL Injection in PHP


What is SQL Injection?

SQL Injection is a type of cyberattack where a malicious user can inject harmful SQL statements into a query by manipulating user inputs.

Example:

$username = $_GET['user'];
$password = $_GET['pass'];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";

If a hacker inputs:

username: admin' -- 
password: anything

The query becomes:

SELECT * FROM users WHERE username='admin' -- ' AND password='anything'

The -- makes everything after it a comment, allowing the attacker to bypass authentication.

How SQL Injection Works (Real Examples)

Classic Injection

$sql = "SELECT * FROM users WHERE id = '$id'";

If the attacker inputs 1 OR 1=1, the query becomes:

SELECT * FROM users WHERE id = '1 OR 1=1'

This returns all users, breaking data confidentiality.

Dangers of SQL Injection Attacks

  • Unauthorized data access
  • User impersonation
  • Database deletion
  • Server compromise
  • Financial loss

SQL injection remains in the OWASP Top 10 threats.

Why SQL Injection Happens in PHP

  • Directly inserting user input into SQL queries
  • Not using prepared statements
  • Failing to validate and sanitize inputs
  • Poor coding practices and lack of awareness

Top 10 Ways to Prevent SQL Injection in PHP

  • Use Prepared Statements (MySQLi or PDO)
  • Sanitize user input with filter_var()
  • Validate data types (integers, emails, etc.)
  • Use parameterized queries only
  • Hide SQL errors (disable display_errors in production)
  • Use stored procedures when needed
  • Use strong DB privileges (LIMIT user access)
  • Perform regular code audits
  • Escape output (not just input)
  • Never trust user input — always verify

Prevent SQL Injection in PHP Using MySQLi

Vulnerable Code (DON'T do this)

$id = $_GET['id'];
$result = $conn->query("SELECT * FROM users WHERE id = $id");

Secure Code Using MySQLi (Object-Oriented)

$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id); // "i" means integer
$stmt->execute();
$result = $stmt->get_result();

Prevent SQL Injection in PHP Using PDO

Vulnerable Code (DON'T do this)

$sql = "SELECT * FROM users WHERE name = '$name'";

Secure Code Using PDO

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ?");
$stmt->execute([$name]);

Or with named parameters:

$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->bindParam(':name', $name, PDO::PARAM_STR);
$stmt->execute();

MySQLi vs PDO - SQL Injection Safe Examples

Operation MySQLi Secure Code PDO Secure Code
Insert $stmt = $conn->prepare("INSERT INTO ...") $stmt = $pdo->prepare("INSERT INTO ...")
Select $stmt->bind_param("i", $id); $stmt->bindParam(':id', $id, PDO::PARAM_INT);
Update $stmt = $conn->prepare("UPDATE ...") $stmt->execute([$name, $id]);
Delete $stmt = $conn->prepare("DELETE FROM ...") $stmt = $pdo->prepare("DELETE FROM ...")

Input Validation and Sanitization in PHP

Use filter_input() or filter_var() to sanitize inputs:

$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$age = filter_var($_POST['age'], FILTER_VALIDATE_INT);

Never rely on front-end validation alone — always validate again on the server.

Use Stored Procedures for Extra Protection

Stored procedures are SQL functions stored in the database. They're harder to inject into.

CREATE PROCEDURE GetUserById(IN uid INT)
BEGIN
  SELECT * FROM users WHERE id = uid;
END;

Call from PHP:

$stmt = $pdo->prepare("CALL GetUserById(?)");
$stmt->execute([$id]);

SQL Injection Testing Tools

  • SQLMap (penetration testing tool)
  • Burp Suite (intercept input/output)
  • Hackbar (browser extension)
  • Wapiti / OWASP ZAP

Test your apps regularly to detect vulnerabilities.

Preventing SQL Injection in PHP Login Systems

Login systems are primary targets for SQLi attacks.

Unsafe Login Code

$query = "SELECT * FROM users WHERE username = '$user' AND password = '$pass'";

Secure Login Code with PDO

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$user, $pass]);

Better yet — hash passwords:

$password = password_hash($pass, PASSWORD_DEFAULT);
// And use password_verify() during login

Common Mistakes That Lead to SQL Injection

  • Using string concatenation in queries
  • Skipping prepared statements for simple queries
  • Trusting $_GET or $_POST blindly
  • Echoing raw SQL errors
  • Relying only on client-side validation

FAQs About Preventing SQL Injection in PHP

Q1. Is prepared statement 100% safe from SQL injection?

Yes, if used correctly. It separates query logic from user data completely.

Q2. What is the best way to prevent SQL injection in PHP?

Use prepared statements with PDO or MySQLi, along with input validation.

Q3. Can SQL injection happen with PDO?

Only if you build dynamic queries using string concatenation. Otherwise, PDO with placeholders is safe.

Q4. Should I use mysqli_real_escape_string()?

It's better than nothing, but it's not foolproof. Prepared statements are superior.

Q5. Can stored procedures be injected?

Yes, if you're passing unvalidated inputs. But they're harder to exploit.

Final Thoughts & Best Practices

SQL injection can destroy your entire application. By following these best practices:

  • Always use prepared statements
  • Validate and sanitize every input
  • Avoid displaying raw database errors
  • Never trust user input
  • Test regularly for vulnerabilities