Skip to content
Fix Code Error

How can I prevent SQL injection in PHP?

March 13, 2021 by Code Error
Posted By: Andrew G. Johnson

If user input is inserted without modification into an SQL query, then the application becomes vulnerable to SQL injection, like in the following example:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

That’s because the user can input something like value'); DROP TABLE table;--, and the query becomes:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

What can be done to prevent this from happening?

Solution

Use prepared statements and parameterized queries. These are SQL statements that are sent to and parsed by the database server separately from any parameters. This way it is impossible for an attacker to inject malicious SQL.

You basically have two options to achieve this:

  1. Using PDO (for any supported database driver):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute([ 'name' => $name ]);
    
    foreach ($stmt as $row) {
        // Do something with $row
    }
    
  2. Using MySQLi (for MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // Do something with $row
    }
    

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.


Correctly setting up the connection

Note that when using PDO to access a MySQL database real prepared statements are not used by default. To fix this you have to disable the emulation of prepared statements. An example of creating a connection using PDO is:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

In the above example the error mode isn’t strictly necessary, but it is advised to add it. This way the script will not stop with a Fatal Error when something goes wrong. And it gives the developer the chance to catch any error(s) which are thrown as PDOExceptions.

What is mandatory, however, is the first setAttribute() line, which tells PDO to disable emulated prepared statements and use real prepared statements. This makes sure the statement and the values aren’t parsed by PHP before sending it to the MySQL server (giving a possible attacker no chance to inject malicious SQL).

Although you can set the charset in the options of the constructor, it’s important to note that ‘older’ versions of PHP (before 5.3.6) silently ignored the charset parameter in the DSN.


Explanation

The SQL statement you pass to prepare is parsed and compiled by the database server. By specifying parameters (either a ? or a named parameter like :name in the example above) you tell the database engine where you want to filter on. Then when you call execute, the prepared statement is combined with the parameter values you specify.

The important thing here is that the parameter values are combined with the compiled statement, not an SQL string. SQL injection works by tricking the script into including malicious strings when it creates SQL to send to the database. So by sending the actual SQL separately from the parameters, you limit the risk of ending up with something you didn’t intend.

Any parameters you send when using a prepared statement will just be treated as strings (although the database engine may do some optimization so parameters may end up as numbers too, of course). In the example above, if the $name variable contains 'Sarah'; DELETE FROM employees the result would simply be a search for the string "'Sarah'; DELETE FROM employees", and you will not end up with an empty table.

Another benefit of using prepared statements is that if you execute the same statement many times in the same session it will only be parsed and compiled once, giving you some speed gains.

Oh, and since you asked about how to do it for an insert, here’s an example (using PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute([ 'column' => $unsafeValue ]);

Can prepared statements be used for dynamic queries?

While you can still use prepared statements for the query parameters, the structure of the dynamic query itself cannot be parametrized and certain query features cannot be parametrized.

For these specific scenarios, the best thing to do is use a whitelist filter that restricts the possible values.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
Answered By: Theo

Related Articles

  • How do I include certain conditions in SQL Count
  • Reference — What does this symbol mean in PHP?
  • Is CSS Turing complete?
  • Are PDO prepared statements sufficient to prevent…
  • Error: container has not been made global - how to solve?
  • MySQL database is not receiving any data in PHP
  • Database development mistakes made by application developers
  • Why is my program not running? I am in eclipse ide…
  • get the selected index value of tag in php
  • Why Inserting a value in a nullable field from a…
  • PHP EMAIL ARRAY ORGANIZATION
  • PHP: Inserting Values from the Form into MySQL
  • jQuery ajax success error
  • Send email with PHP from html form on submit with…
  • How to display errors for my MySQLi query?
  • Testing if a site is vulnerable to Sql Injection
  • Testing aurelia customElement with bindable and dependencies
  • How to use the encrypt password for login php
  • SQL query return data from multiple tables
  • Ukkonen's suffix tree algorithm in plain English
  • Add same item to shopping cart with different options
  • Posting array from form
  • How do I count unique visitors to my site?
  • Program to add the squares of numbers in list which…
  • "Notice: Undefined variable", "Notice: Undefined…
  • Calling a specific function
  • Php mailer working with utf-8 locally but not on host
  • PHP mysql insert date format
  • Using if(isset($_POST['submit'])) to not display…
  • PHP parse/syntax errors; and how to solve them
  • You have an error in your SQL syntax; check the…
  • Reference - What does this regex mean?
  • Best practice multi language website
  • Improve INSERT-per-second performance of SQLite
  • How can prepared statements protect from SQL…
  • How does PHP 'foreach' actually work?
  • How to sort encrypted column data?
  • Why is "except: pass" a bad programming practice?
  • Azure CLI - az deployment group create -…
  • Call to undefined function mysql_query() with Login
  • How to Update Database from Assets Folder in App
  • getting error while updating Composer
  • PHP/JavaScript Redirect Issue
  • Why is my PHP string variable breaking where there…
  • ember: understand errors
  • header location not working in my php code
  • The definitive guide to form-based website authentication
  • PHP + MySQL transactions examples
  • Insert data through ajax into mysql database
  • I'm working on pagination for my project. How do I…
  • Logging best practices
  • Insert Backbone.js model into MySQL database
  • How do you parse and process HTML/XML in PHP?
  • mysql_fetch_array()/mysql_fetch_assoc()/mysql_fetch_…
  • Undefined index error PHP
  • Execute a PHP script from another PHP script
  • Clear the form field after successful submission of php form
  • Can a bisection search guess that the user guessed…
  • java.sql.SQLException: - ORA-01000: maximum open…
  • Hidden form value not retrieved or stored
  • Having trouble with my nav bar/header, It used to…
  • How do I obtain a Query Execution Plan in SQL Server?
  • Azure Availability Zone ARM Config
  • Preventing SQL injection in Node.js
  • PDO support for multiple queries (PDO_MYSQL, PDO_MYSQLND)
  • Convert a PHP script into a stand-alone windows executable
  • Showing all errors and warnings
  • How do SO_REUSEADDR and SO_REUSEPORT differ?
  • PHP Form Loop Passing Variables to $_POST When Input…
  • PreparedStatement IN clause alternatives?
  • SQL Server : trigger how to read value for Insert,…
  • Get all variables sent with POST?
  • When to use single quotes, double quotes, and…
  • The mysql extension is deprecated and will be…
  • python 3.2 UnicodeEncodeError: 'charmap' codec can't…
  • Creating a search form in PHP to search a database?
  • Avoid creating new session on each axios request laravel
  • SQL Insert Query Using C#
  • How do I pass JavaScript variables to PHP?
  • retrieve data from db and display it in table in php…
  • Update query PHP MySQL
  • How to create a temporary table in SSIS control flow…
  • SQL syntax error when executing MySQL script using…
  • PHP - Failed to open stream : No such file or directory
  • Why is it common to put CSRF prevention tokens in cookies?
  • Simple C example of doing an HTTP POST and consuming…
  • WordPress/WooCommerce: Save custom payment post meta
  • Why shouldn't I use mysql_* functions in PHP?
  • SyntaxError: JSON.parse: unexpected character at…
  • How can I call PHP functions by JavaScript?
  • How to create websockets server in PHP
  • Alternative to header("Content-type: text/xml");
  • What are the undocumented features and limitations…
  • apache server reached MaxClients setting, consider…
  • How do I insert multiple checkbox values into a table?
  • Extending Laravel/Lumen Query Builder to…
  • PDO's query vs execute
  • Using prepared statements with JDBCTemplate
  • Data not catching in multi step contact form
  • Warning: Cannot modify header information - headers…

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

Hide scroll bar, but while still being able to scroll

Next Post:

Understanding Python super() with __init__() methods

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

.net ajax android angular arrays aurelia backbone.js bash c++ css dataframe ember-data ember.js excel git html ios java javascript jquery json laravel linux list mysql next.js node.js pandas php polymer polymer-1.0 python python-3.x r reactjs regex sql sql-server string svelte typescript vue-component vue.js vuejs2 vuetify.js

  • you shouldn’t need to use z-index
  • No column in target database, but getting “The schema update is terminating because data loss might occur”
  • Angular – expected call-signature: ‘changePassword’ to have a typedeftslint(typedef)
  • trying to implement NativeAdFactory imports deprecated method by default in flutter java project
  • What should I use to get an attribute out of my foreign table in Laravel?
© 2022 Fix Code Error