SQL Injections in PHP: How to Protect Yourself in 2025

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why are SQL injections still relevant?

\n\n

Imagine: your website runs like clockwork, users register, leave comments, place orders. And suddenly — the database ends up in the hands of an attacker. Not because hackers are geniuses, but because someone forgot to put a question mark in an SQL query.

\n\n

SQL injections are not an archaic threat from the 2000s. In 2024, OWASP still ranks them in the top 3 most dangerous web application vulnerabilities. And the question "How can I prevent SQL injection in PHP?" on Stack Overflow has over 1.5 million views — and continues to grow. So, the problem is alive.

\n\n

In this article, we will figure out how to firmly close the door to SQL injections in PHP in 2025. No magic, no complex frameworks, just clean code and best practices.

\n\n

What is an SQL injection in simple terms?

\n\n

An SQL injection is when user input becomes part of an SQL command, not just data. A real-life example:

\n\n
<?php\n// BAD CODE — never do this!\n$username = $_POST['username'];\n$query = "SELECT * FROM users WHERE username = '$username'";\n$result = mysqli_query($conn, $query);\n?>
\n\n

If an attacker enters the string ' OR '1'='1 into the username field, the query becomes:

\n\n
SELECT * FROM users WHERE username = '' OR '1'='1'
\n\n

This returns all users. And if they enter '; DROP TABLE users; -- — you could lose the entire table. Scary? You bet.

\n\n

Best practices for protecting against SQL injections in PHP 2025

\n\n

1. Use PDO with prepared statements (this is the standard)

\n\n

PDO (PHP Data Objects) is a universal interface for working with databases. Its main advantage: it separates SQL logic and data. You write a query with placeholders, and PDO takes care of safe escaping itself.

\n\n
<?php\n// GOOD CODE — use PDO\n$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8mb4';\n$pdo = new PDO($dsn, $user, $pass, [\n    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n    PDO::ATTR_EMULATE_PREPARES => false, // disable emulation\n]);\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');\n$stmt->execute(['email' => $_POST['email']]);\n$user = $stmt->fetch();\n?>
\n\n

Why does this work? The database receives the query structure separately from the data. Even if the user enters malicious SQL, it will be interpreted as a regular string, not as part of the command.

\n\n

2. Mysqli with prepared statements (alternative)

\n\n

If you only work with MySQL, you can use mysqli — but only with prepared statements, not with escaping via mysqli_real_escape_string().

\n\n
<?php\n$mysqli = new mysqli('localhost', 'user', 'pass', 'testdb');\n$stmt = $mysqli->prepare('INSERT INTO comments (user_id, text) VALUES (?, ?)');\n$stmt->bind_param('is', $userId, $commentText);\n$stmt->execute();\n?>
\n\n

Important: in 2025, mysqli_real_escape_string() still exists, but relying on it for protection is a bad idea. It only escapes strings, but does not protect against injections in numeric fields or in LIKE operators.

\n\n

3. Filtering and validation — the second line of defense

\n\n

Prepared statements are the foundation. But add data type filtering on top of them. If a number is expected — check that it is a number. If an email — check the format.

\n\n
<?php\n$id = filter_input(INPUT_POST, 'user_id', FILTER_VALIDATE_INT);\nif ($id === false) {\n    throw new InvalidArgumentException('ID must be an integer');\n}\n\n$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');\n$stmt->execute(['id' => $id]);\n?>
\n\n

Combine

Blogs

Book Recommendations