How to Prevent SQL Injection in PHP: Best Practices for Secure Web Applications

Learn how to prevent SQL injection in PHP with secure coding practices. Discover the best methods like prepared statements, parameterized queries, and PDO/MySQLi configurations to protect your web applications from malicious attacks.
How to Prevent SQL Injection in PHP: Best Practices for Secure Web Applications Image

To safeguard your PHP application, follow these proven techniques to prevent SQL injection effectively.

  • Step 1: Use Prepared Statements and Parameterized Queries (Best Practice)

  • Step 2: Use an ORM (Object-Relational Mapping)

  • Step 3: Escape User Inputs (If You Must Use Legacy Code)

  • Step 4: Correctly Setting Up the Database Connection

  • Step 5: Enable Error Reporting and Set the Correct Charset

  • Step 6: Validate User Inputs

  • Step 7: Limit Database Privileges

  • Step 8: Keep Your PHP and Database Systems Updated

The Best Way to Prevent SQL Injection

The most effective way to prevent SQL injection attacks is to separate user data from SQL commands. This ensures the data is treated strictly as values, not executable code. The safest method is to use prepared statements and parameterized queries, which securely pass SQL statements to the database server separate from user inputs. This eliminates the risk of malicious SQL injection, even if the data contains harmful content. These techniques ensure secure SQL queries in PHP.

1. Use Prepared Statements and Parameterized Queries (Best Practice)

Prepared statements separate data from SQL commands, ensuring that data is always treated as data — never as executable code. This is the most secure method to prevent SQL injection in PHP.

Example using PDO:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
$sql = "SELECT * FROM users WHERE email = :email";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':email', $_POST['email'], PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll();
?>

Example using MySQLi (PHP 8.2+ with execute_query()):

Since PHP 8.2+ we can make use of execute_query() which prepares, binds parameters, and executes SQL statement in one method:

<?php
$result = $db->execute_query('SELECT * FROM users WHERE name = ?', [$_POST['name']]);
while ($row = $result->fetch_assoc()) {
    // Process data
}
?>

For older PHP versions (up to 8.1):

<?php
$stmt = $db->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $_POST['name']);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // Process data
}
?>

If you're connecting to a database other than MySQL, there is a driver-specific second option that you can refer to (for example, pg_prepare() and pg_execute() for PostgreSQL). PDO is the universal option.

2. Use an ORM (Object-Relational Mapping)

PHP frameworks like Laravel, Symfony, and CodeIgniter provide ORM tools that automatically handle prepared statements for you.

Example in Laravel Eloquent:

<?php
$users = User::where('email', request('email'))->get();
?>

3. Escape User Inputs (If You Must Use Legacy Code)

For older codebases that cannot be refactored easily, escaping user inputs is a temporary workaround. However, this method is less secure than prepared statements.

Example using mysqli_real_escape_string():

<?php
$conn = new mysqli("localhost", "username", "password", "testdb");
$email = mysqli_real_escape_string($conn, $_POST['email']);
$sql = "SELECT * FROM users WHERE email = '$email'";
$result = $conn->query($sql);
?>

4. Correctly Setting Up the Database Connection

PDO Configuration

When using PDO with MySQL, real prepared statements are not enabled by default. You must disable emulated prepared statements to ensure data and queries are handled separately.

Example:

<?php
$dsn = 'mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4';
$dbConnection = new PDO($dsn, 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>
  • PDO::ATTR_EMULATE_PREPARES ensures real prepared statements are used.
  • PDO::ATTR_ERRMODE enables error reporting for better debugging.

Note: Older PHP versions (before 5.3.6) silently ignored the charset parameter in the DSN, so ensure your PHP version is up to date.

MySQLi Configuration

For MySQLi, follow this routine:

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // Enable error reporting
$dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');
$dbConnection->set_charset('utf8mb4'); // Set charset for security
?>

5. Enable Error Reporting and Set the Correct Charset

Proper error reporting and character encoding are crucial to ensuring your database is protected.

PDO Configuration:

<?php
$dsn = 'mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4';
$dbConnection = new PDO($dsn, 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>

MySQLi Configuration:

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');
$dbConnection->set_charset('utf8mb4');
?>

6. Validate User Inputs

Validating user input ensures only properly formatted data reaches your database, reducing the risk of SQL injection.

Example of Input Validation:

<?php
if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
    die("Invalid email format");
}
?>

7. Limit Database Privileges

Restrict database permissions to only what's necessary. Avoid using root-level accounts for application queries.

8. Keep Your PHP and Database Systems Updated

Regularly update PHP, MySQL, and libraries to patch security vulnerabilities and stay protected from evolving threats.

Conclusion

By implementing prepared statements, using ORM tools, and validating user input, you can effectively prevent SQL injection in PHP. These practices not only enhance security but also improve the stability and performance of your web applications. For Laravel developers, Eloquent ORM offers built-in protection against SQL injection, making it a preferred choice for modern web development.

 

Do you Like?