Regarding to many useful answers, I hope to add some values to this thread.
SQL injection is type of attack that can be done through user inputs (Inputs that filled by user and then used inside queries), The SQL injection patterns are correct query syntax while we can call it: bad queries for bad reasons, we assume that there might be bad person that try to get secret information (by passing access control) that affect the three principles of security (Confidentiality, Integrity, Availability).
Now, our point is to prevent security threats such as SQL injection attacks, the question asking (How to prevent SQL injection attack using PHP), be more realistic, data filtering or clearing input data is the case when using user-input data inside such query, using PHP or any other programming language is not the case, or as recommended by more people to use modern technology such as prepared statement or any other tools that currently supporting SQL injection prevention, consider that these tools not available anymore? how you secure your application?
My approach against SQL injection is: clearing user-input data before sending it to database (before using it inside any query).
Data filtering for (Converting unsafe data to safe data)
Consider that PDO and mysqli not available, how can you secure your application? do you force me to use them? what about other languages other than PHP? I prefer to provide general ideas as it can be used for wider border not just for specific language.
- SQL user (limiting user privilege) : most common SQL operations are (SELECT, UPDATE, INSERT), then, why giving UPDATE privilege to a user that not require it? for example: login, and search pages are only using SELECT, then, why using db users in these pages with high privileges?
RULE: do not create one db user for all privileges, for all SQL operations, you can create your scheme like (deluser, selectuser, updateuser) as usernames for easy usage.
see Principle of least privilege
Data filtering: before building any query user input should be validated and filtered, for programmers, its important to define some properties for each user-input variables:
data type, data pattern, and data length. a field that is a number between (x and y) must be exactly validated using exact rule, for a field that is a string (text): pattern is the case, for example: username must contain only some characters lets say [a-zA-Z0-9_-.] the length vary between (x and n) where x and n (integers, x <=n ).
Rule: creating exact filters and validation rules are best practice for me.
Use other tools: Here, I will also agree with you that prepared statement (parametrized query) and Stored procedures, the disadvantages here is these ways requires advanced skills which are not exist in most users, the basic idea here is to distinguish between SQL query and the data that being used inside, both approach can be used even with unsafe data, because the user-input data here not add anything to the original query such as (any or x=x).
for more information please read OWASP SQL Injection Prevention Cheat Sheet.
Now, if you are an advanced user, start using these defense as you like, but, for beginners, if they can't quickly implement stored procedure and prepared statement, its better to filter input data as much they can.
Finally, lets consider that user sends this text below instead of entering his username:
[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'
This input can be checked early without any prepared statement and stored procedures, but to be on safe side, using them starts after user-data filtering and validation.
Last point is detecting unexpected behavior which requires more effort and complexity, its not recommended for normal web applications.
Unexpected behavior in above user input is: SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root once these words detected, you can avoid the input.
UPDATE1:
User commented that this post is useless, ok! here is what OWASP.ORG provided:
Primary Defenses:
Option #1: Use of Prepared Statements (Parameterized Queries)
Option #2: Use of Stored Procedures
Option #3: Escaping all User Supplied Input
Additional Defenses:
Also Enforce: Least Privilege
Also Perform: White List Input Validation
As you may knew, claiming on any article should be supported by valid argument, at least one reference! otherwise its considered as attack and bad claim!
Update2:
From PHP Manual http://php.net/manual/en/mysqli.quickstart.prepared-statements.php:
Escaping and SQL injection
Bound variables will be escaped automatically by the server. The
server inserts their escaped values at the appropriate places into the
statement template before execution. A hint must be provided to the
server for the type of bound variable, to create an appropriate
conversion. See the mysqli_stmt_bind_param() function for more
information.
The automatic escaping of values within the server is sometimes
considered a security feature to prevent SQL injection. The same
degree of security can be achieved with non-prepared statements, if
input values are escaped correctly.
Update3:
I created test cases for knowing how PDO and MySQLi sends the query to MySQL server when using prepared statement:
PDO:
$user = "''1''"; //malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));
Query Log:
189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
189 Quit
MySQLi:
$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();
Query Log:
188 Prepare SELECT * FROM awa_user WHERE username =?
188 Execute SELECT * FROM awa_user WHERE username ='\'\'1\'\''
188 Quit
Its clear that prepared statement also escaping the data, nothing else.
As also mentioned in above statement The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly
, therefore, this proves that data validation such as intval()
is a good idea for integer values before sending any query, in addition, preventing malicious user data before sending the query is correct and valid approach.
Please see this question for more detail: PDO sends raw query to MySQL while Mysqli sends prepared query, both produce the same result
References:
- SQL Injection Cheat Sheet
- SQL Injection
- Information security
- Security Principles
- Data validation
mysql_*
functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial. – Neal Dec 20 '12 at 17:47mysql_
scripts tomysqli_
, and get started using prepared statements: stackoverflow.com/a/15055993/1270996 – Nicholas Pickering Feb 24 at 22:09