In my MySQL table, I have a column of TEXT type. On my HTML Form, user pastes text into it that might contain "" , ' ( ) and so on. I want to know how to safely execute Insert Query if these characters exist in the text and might crash the query execution.
How to handle them properly in PHP?
If you are not using prepared statements (either with PDO or MySqli) you should pass user's input trough MySql_Real_Escape_String() function. Or MySqli_Real_Escape_String() if you are using MySqli (but not prepared statements).
I would, however, advise you to use prepared statements as your life will be much easier and you get SQL-Injection protection for free.
Use a prepared statement.
Prepared statements can help increase
security by separating SQL logic from
the data being supplied. This
separation of logic and data can help
prevent a very common type of
vulnerability called an SQL injection
attack. Normally when you are dealing
with an ad hoc query, you need to be
very careful when handling the data
that you received from the user. This
entails using functions that escape
all of the necessary trouble
characters, such as the single quote,
double quote, and backslash
characters. This is unnecessary when
dealing with prepared statements. The
separation of the data allows MySQL to
automatically take into account these
characters and they do not need to be
escaped using any special function.
A quick example,
$db = new mysqli('localhost', 'username', 'password', 'db');
$stmt = $db->prepare("INSERT INTO mytable (text_column) VALUES (?)");
$stmt->bind_param("s", $mytext); // s = string, b = boolean, i = int, etc
$stmt->execute();
...
Related
I am trying to understand when I should use prepared statements in php/mysqli. Should every php/mysqli query use prepared statements or just queries and instances where user input is involved ... such as an html form that asks a user to enter data to search within a database?
I am migrating my old php5/mysql code to php7/mysqli. I have many php files that query a mysql db. I would like clarification if I need to use prepared statements for every php file that connects to a mysql db ... for example php files that are referenced via "php require" and include simple sql select statements to render images and links to a html page?
<?php
//establish connection
$con = new mysqli('localhost','uid','pw','db');
//check connection
if ($con->connect_error) {
die("Connection failed: " . $con->connect_error);
}
//search variable that stores user input
$search = "%{$_POST['search']}%";
//prepare, bind and fetch
$stmt = $con->prepare("SELECT image, caption FROM `tblimages`
WHERE catid = 3 AND caption LIKE ? order by caption ASC");
$stmt->bind_param("s", $search);
$stmt->execute();
$stmt->bind_result($image,$caption);
while ($stmt->fetch()) {
echo "{$image} <br> {$caption} <br>";
}
$stmt->close();
//close database connection
mysqli_close($con);
?>
The code above works and is the first I've ever used prepared statements. It takes user input from a form (blank box to enter a search term - POST) and searches a db ... then renders results to an html page. This seems like a logical use of prepared statements. However ... I have other php files where users select data from a drop down box in a form to render a result (the user does not enter data into a search box like above). Do I use prepared statements for that instance as well? Plus do I use prepared statements for php files that are referenced via "php require" and include simple sql select statements to render images and links to a html page? I've yet to find clarification of the specific instances to use prepared statements to prevent sql injections. Any clarification or references welcome.
Short answer: Always use prepared statements.
Long answer:
Prepared statements separate your data from SQL commands. They are provided by PDO or by MySQLi. Their biggest advantage is that it is impossible to have SQL injection if your data is treated as data. Another advantage is that you can execute the same query over and over again with different set of data, which might be better for your performance and often keeps your code cleaner.
However, there are times when you would like to have some kind of dynamic query based on user's selection or actions. As you probably know table and column names are not data, but part of SQL query, therefore you can't keep them separated. The alternative to prepared statements then is to have a white list of possible values and only allow user input validated against the white list.
You might ask what are query, real_query, multi_query and PDO::exec good for?
As the PHP Manual shows they are good at times when you only need to execute constant query without any variables or when you have a query which can't be prepared. e.g.
$mysqli->query('SELECT Name FROM City LIMIT 10');
$pdo->exec('DELETE FROM fruit');
$mysqli->multi_query('DELETE FROM fruit; DELETE FROM pets;');
What if you know the type and values of your data? Should you also prepare/bind?
Yes! Get into a habit of binding all data going with SQL query. There is no reason to make exceptions. It is much more difficult to trace those exceptions in your code and always be sure you do not overwrite the "safe" value with some unknown input.
If you are still not sure how to use prepared statements or you think that they are too complicated (they are not) you can take a look at an amazing PHP tutorial at https://phpdelusions.net
This is how MySQLi prepared statements work in PHP:
Prepare an SQL query with empty values as placeholders (with a question mark for each value).
Bind variables to the placeholders by stating each variable, along with its type.
Execute query.
The four variable types allowed:
i - Integer
d - Double
s - String
b - Blob
A prepared statement, as its name implies, is a way of preparing the MySQL call, without storing the variables. You tell it that variables will go there eventually — just not yet. The best way to demonstrate it is by example.
$stmt = $mysqli->prepare("SELECT * FROM myTable WHERE name = ? AND age = ?");
$stmt->bind_param("si", $_POST['name'], $_POST['age']);
$stmt->execute();
//fetching result would go here, but will be covered later
$stmt->close();
If you've never seen prepared statements before, this may look a little weird.
Basically what's happening is that you are creating a template for what the SQL statement will be.
In this case, we are selecting everything from myTable, where name and age equal ?. The question mark is just a placeholder for where the values will go.
The bind_param() method is where you attach variables to the dummy values in the prepared template.
Notice how there are two letters in quotes before the variables.
This tells the database the variable types.
The s specifies that name will be a string value, while the i forces age to be an integer.
This is precisely why I didn't add quotation marks around the question mark for name, like I normally would for a string in an SQL call.
You probably thought I just forgot to, but the reality is that there is simply no need to (In fact, it actually won't work if you do put quotes around the ?, since it will be treated as a string literal, rather than a dummy placeholder.).
You are already telling it that it will be a string literal when you call bind_param(), so even if a malicious user tries to insert SQL into your user inputs, it will still be treated as a string.
$stmt->execute() then actually runs the code; the last line simply closes the prepared statement. We will cover fetching results in the Select section.
I'm learning about avoiding SQL injections and I'm a bit confused.
When using bind_param, I don't understand the purpose. On the manual page, I found this example:
$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, 'sssd', $code, $language, $official, $percent);
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
Now, assuming those 4 variables were user-inputted, I don't understand how this prevents SQL injections. By my understanding, they can still input whatever they want in there.
I also can't find an explanation for the 'sssd' in there. What does it do? Is that what makes it secure-er?
Final question: I read on another question that mysqli_real_escape_string is deprecated, but it doesn't say that in the manual. How is it deprecated? Can it not escape special characters anymore for some reason?
Note: This question explained what bind_param does, but I still don't understand why it is any safer or more protected.
Bind_param explanation
Now, assuming those 4 variables were user-inputted, I don't understand
how this prevents SQL injections. By my understanding, they can still
input whatever they want in there.
The main principle there is using prepared statement which is designed for sending safe query to db server, this can be done by escaping user input which is not part of the real query, and also checking the query without any (where clause) to check the validity of the query before using any parameters.
From this question: PDO sends raw query to MySQL while Mysqli sends prepared query, both produce the same result
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username =?")) {
$stmt->bind_param("i", $user);
$user = "''1''";
server logs:
130802 23:39:39 175 Connect ****#localhost on testdb
175 Prepare SELECT * FROM users WHERE username =?
175 Execute SELECT * FROM users WHERE username =0
175 Quit
By Using prepared statement, db server will check the query without any parameter, at this stage, errors can be detected before binding any parameter, then, if the query was valid, parameters also will be send to the server for finalizing the query.
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.
..
I also can't find an explanation for the 'sssd' in there. What does it
do? Is that what makes it secure-er?
The answer is here: http://php.net/manual/en/mysqli-stmt.bind-param.php
i
corresponding variable has type integer
d
corresponding variable has type double
s
corresponding variable has type string
b
corresponding variable is a blob and will be sent in packets
Final question: I read on another question that
mysqli_real_escape_string is deprecated, but it doesn't say that in
the manual. How is it deprecated? Can it not escape special characters
anymore for some reason?
Can you give a reference? I think you misunderstood with (mysql_real_escape_string())
By using prepared statements you are separating SQL queries from user entered data. Instead of input data, you put placeholders ('?' char) in your SQL query. Then you send the query to the DBMS server (e.g.: MySQL) by means of the "mysqli::prepare" method. So the server checks that everything is ok and, if so, it waits for input data. By now it already knows your query. Just it has to wait for input data to bind to the query.
At this point, "bind_param" comes into action, binding placeholders to user entered data.
Notice that bind_param only binds data to placeholders leaving unchanged the query.
So there is not way to change the original SQL query, because it has already sent to the server by means of the prepare method and because you are sending SQL queries and input data separately so user entered data can't interfere with queries.
ANYWAY...
The actual purpose to use a prepared statement in SQL is to cut the cost of processing queries, NOT to separate data from query. That's how it's being used now, not how it was designed to be used in the first place.
'sssd' stands for "string", "string", "string" and "double".
In fact: $code is a string, $language is a string, $official is a string and $percent is a double type.
mysqli_real_escape_string is not deprecated but mysql_real_escape_string is deprecated
(the first one is mysqlI, where I stands for "improved").
I'm familiar with prepared statements and I know that they are best practice when it comes to protecting against MySQL injection. But I'm wondering how this PHP/MySQL statement could be at risk of an injection attack:
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".$_POST['name']."';");
It seems to me like the input from the user would be contained inside the single quotes. Can you execute more than one query in one mysqli_query statement?
Also, is making the above safe just as easy as this...
$result = mysqli_query($db,"SELECT name FROM users WHERE id = '".mysqli_real_escape_string($_POST['name'])."';");
It seems to me like the input from the user would be contained inside the single quotes
It would unless you include single quotes in the posted name, which would allow you to break out of the quotes. Example, post the name as:
' or 1 or '
The WHERE clause becomes:
WHERE id = '' or 1 or '';
This would match and retrieve all rows in the table because of the or 1 part. As you can see, it breaks out of the quotes to inject some SQL, then it goes back into the quotes to make the query valid.
Can you execute more than one query in one mysqli_query statement?
No, but if it was executed with mysqli_multi_query then yes you could add multiple queries on to the end.
is making the above safe just as easy as mysqli_real_escape_string?
Generally yes but a Prepared Statement would be better. Using escaping, the WHERE clause would become (using my example above):
WHERE id = '\' or 1 or \'';
This is no longer vulnerable because the quotes can't be broken out of, and would only match rows if the name literally matches ' or 1 or ' which is obviously unlikely.
It seems to me like the input from the user would be contained inside the single quotes
All the attacker has to do is put a single quote inside the name POST data, and it won't be any more.
name=' OR 1=1
Also, is making the above safe just as easy as this
That looks OK … but it hurts my eyes. Use prepared statements. They are much easier to read then SQL built by concatenating strings together.
Basic explaination:
If you simply insert $_POST['name'] into the query as per your first example, the resulting SQL string will be invalid if the name variable contains a single quote character.
This will immediately annoy anyone named O'Brien, or similar.
But this can then be exploited by a hacker, who could modify his "name" to include valid SQL code after the single quote. This could be any valid SQL, allowing the hacker to do anything to your DB or query anything from it. Exactly what he can do would depend on other factors in your code, but suffice to say that even in the best case scenario, he could do some pretty devastating things.
To answer your second question: Yes. Escaping using mysqli_real_escape_string() will mitigate this problem.
However, to take things one step further, you might also want to investigate using Prepared Queries, which is a feature of the mysqli extension. This can make your code a lot neater as it avoids having to use that nasty long mysqli_real_escape_string() function name all over the place. It also has other benefits such as improved query caching.
Hope that helps answer the question.
What if I passed the following value for $_POST['name']?
'; DELETE FROM users WHERE name <> '
I would be closing the first single quote, then introducing the damaging query which just has a single open quote at the end, which would be closed by the single quote in your original query.
You second query is fine. Though you really ought to consider use of prepared statements (which are supported by mysqli)
If you're using mysqli you should always be using the SQL placeholder method for doing this. The escaping functions are the hard way.
$stmt = $db->prepare("SELECT name FROM users WHERE id = ?");
$stmt->bind_param('i', $_POST['name']);
$stmt->execute();
If you don't understand the risk here, you really need to read up on SQL injection attacks in general, and read what automated hacking tools can do to those that aren't cautious enough.
Historically, I've always used
mysql_real_escape_string()
for all input derived from users that ends up touching the database.
Now that I've completely converted over to MySQLi and I'm using prepared queries with bound parameters, have I effectively eliminated the possibility of SQL injection attacks?
Am I correct in saying I no longer need
mysql_real_escape_string()?
This is my understanding and the basis of a project of mine:
http://sourceforge.net/projects/mysqldoneright/files/Base/MysqlDoneRight-0.23.tar.gz/download
This is not something I want to get wrong though as now that I've released it, it could affect others as well.
All user provided input will now end up in bind_parms.
The queries provided in the prepare phase are static.
Yes. Using the prepared query will escape parameters.
It's not so simple. You can use bound parameters instead of interpolating application variables into SQL expressions in place of literal values only:
$sql = "SELECT * FROM MyTable WHERE id = ".$_GET["id"]; // not safe
$sql = "SELECT * FROM MyTable WHERE id = ?"; // safe
But what if you need to make part of the query dynamic besides a literal value?
$sql = "SELECT * FROM MyTable ORDER BY ".$_GET["sortcolumn"]; // not safe
$sql = "SELECT * FROM MyTable ORDER BY ?"; // doesn't work!
The parameter will always be interpreted as a value, not a column identifier. You can run a query with ORDER BY 'score', which is different from ORDER BY score, and using a parameter will be interpreted as the former -- a constant string 'score', not the value in the column named score.
So there are lots of cases where you have to use dynamic SQL and interpolate application variables into the query to get the results you want. In those cases, query parameters can't help you. You still have to be vigilant and code defensively to prevent SQL injection flaws.
No framework or data-access library can do this work for you. You can always construct a SQL query string that contains a SQL injection flaw, and you do this before the data-access library sees the SQL query. So how is it supposed to know what's intentional and what's a flaw?
Here are the methods to achieve secure SQL queries:
Filter input. Trace any variable data that gets inserted into your SQL queries. Use input filters to strip out illegal characters. For instance, if you expect an integer, make sure the input is constrained to be an integer.
Escape output. Output in this context can be the SQL query which you send to the database server. You know you can use SQL query parameters for values, but what about a column name? You need an escaping/quoting function for identifiers, just like the old mysql_real_escape_string() is for string values.
Code reviews. Get someone to be a second pair of eyes and go over your SQL code, to help you spot places where you neglected to use the above two techniques.
When you bind parameters to a prepared statement, it escapes the data automatically, so you shouldn't escape it before you send it through. Double escaping is usually a bad thing. At the very least, it produces ugly results with extra escaped characters later on.
Is the any difference between writing
{$_GET['id']}
and
'".$_GET['id']."'
in a sql statement? both works the same
Its always a bad idea to put data from a get request directly into SQL, and there are many easy ways to prevent SQL injection.
For the simple case where you know you want a numeric ID, then you can simply force the value to be numeric and then there is no chance of SQL injection. One way might be okoman's usage of sprintf(), maybe something like this:
$sql = "INSERT INTO table VALUES(".sprintf("%d",$_GET['id']) .")";
though I think its ugly and bad form. Something like this is nicer:
$sql = "INSERT INTO table VALUES(".(int)$_GET['id']) .")";
Which simply casts the value to an int - if its not a number that can be cast to int - you'll get a "0" which may or may not be what you want.
Other alternatives (and for when your data is not supposed to be a number) include using escaping functions such as add_slashes() or the above mentioned mysql_real_escape_string().
IMHO, the best way to use database access from within PHP and very easily protect from SQL injection is to use the new PHP PDO library ( http://php.net/PDO ). This allows you to write SQL strings that contain no data whatsoever, and have the data added later inside the database server itself. In this form there is absolutely no way to do SQL injection.
Here is some example code:
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
$stmt = $dbh->prepare("INSERT INTO table VALUES (?)");
$stmt->bindParam(1, $_GET['id']);
$stml->execute();
As you can see, the data is not added to the SQL query itself and so no need for escaping.
Using either of these directly in a SQL statement is a VERY BAD IDEA. It allows for SQL injections. Be sure to sanitize your inputs using something like mysql_real_escape_string.
The main difference between the two is that the top can only be used inside a string that uses double quotes. The bottom, however, can be used with either double or single quotes.
As far as I know there's no difference, but should you be doing that? You're allowing unsanitised input into your sql query which means if your website is internet facing you're leaving it wide open to sql injection attacks.
Here's a Q&A you should read before going any further:
How can I prevent SQL injection in PHP?
If you use a variable - especially an associative array - in a string, you can be quite sure that it will lead to errors. It's just bad style.
I - personally - don't like the second alternative either.
sprintf( '... %d ...', $_GET[ 'id' ] );
That's my favorite way of putting a variable into a string.