Preventing SQL Injection in PHP with Prepared Statements

SQL injection has been on the OWASP Top 10 for over two decades, and it’s still one of the most common vulnerabilities found in PHP applications — almost always because someone concatenated user input directly into a query string.

// VULNERABLE - never do this
$id = $_GET['id'];
$result = $pdo->query("SELECT * FROM users WHERE id = $id");

// SAFE - prepared statement with a bound parameter
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
$user = $stmt->fetch();

Prepared statements work because the query structure is sent to the database separately from the data — the database never interprets the bound values as SQL syntax, no matter what characters they contain. This isn’t a best practice with exceptions; it should be the only way you ever build a query with user input.