How to prepare a statement the right way? - php

I'm currently writing a CMS and I want to prevent SQL injection.
I have the variable $url_variable which is a $_GET. Now I want to prepare the statement and search for the $url_variable in my table.
$stmt = $db_connect->prepare("SELECT * FROM $url_table WHERE url = ?");
$url_variable = $stmt->quote($url_variable);
$stmt->bind_param("s", $url_variable);
Now my questions: Is this code above right? Do I need the quote? And is it safe from any SQL injection?

You should create a whitelist for your $url_table variable and check if the table name is in your whitelist before you use it in your query. For example, you can do so with a simple if check:
if ($url_table == 'allowedTableName' || $url_table == 'anotherAllowedTableName')
{
$stmt = $db_connect->prepare("SELECT * FROM $url_table WHERE url = ?");
$stmt->bind_param("s", $url_variable);
//........
}
else {
die('Illegal table name provided');
}
You shouldn't quote your variable that is bound, you are good to go with the prepared statement!

Using bind_param protects those parameters from SQL injection. You don't need $stmt->quote, and it can actually cause the query to fail. It will add escape characters, and when you use bind_param it will search for these characters literally.

In this code:
if you use Url_Variable by method GET can be added strip_tags or htmlenteties to remove/prevent cross-site-scripting attacks by completely removing any HTML and PHP tags it finds, as in:
$url_variable=$_GET['url_variable'];
$url_variable=strip_tags($url_variable);
This is to avoid mal program/script.
More secure will be handled by prepared statements, as in:
$stmt = $db_connect->prepare("SELECT * FROM $url_table WHERE url = ?");
$stmt->bind_param("s", $url_variable);
Therefore, no need to use Quote in Mysqli since it has been used prepared statements and bind the parameters to ensure that you don't forget to escape a particular string which is led to a potential security problem.

You shouldn't quote bind variables in prepared statements - the statement takes care of that for you:
$stmt = $db_connect->prepare("SELECT * FROM $url_table WHERE url = ?");
$stmt->bind_param("s", $url_variable);

Related

Single Quote Escape in PDO Prepared Statement Parameters

So I understand PDO Prepared Statements should protect from SQL injection and ' escapes. But when I attempted the following...
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["id"]))
{
$id = $_POST["id"];
//$id = "2' AND name='Entry2";
$someinfo = "updated";
...DB Stuff...
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $dbpassword);
$stmt = $conn->prepare("UPDATE testdb SET info=:someinfo WHERE id=:id");
$stmt->bindParam(':id', $id);
$stmt->bindParam(':someinfo', $someinfo);
$stmt->execute();
$conn = null;
exit();
}
Then the row with id=2 and name=entry2 would be updated. Now it doesn't seem like this can be used to escape into other SQL queries, and I assume I can take precautions to ensure this kind of escape can't really do damage. But I wanted to be sure that there wasn't some other way to prevent ' escapes making unexpected changes to SQL query parameters. (Worth noting, I tried something similar in SQLi and got pretty much the same result.)
Is there something I'm missing? Or is this just the way Prepared Statements work.
After looking around some more, this behavior was eloquently explained/solved for me here:
https://phpdelusions.net/pdo#comment-277
It turns out it's not escaping the string, but instead truncating input after the integer which just made it appear to escape the string. I was able to confirm this upon modifying the code.

If I prepare AND bind every query in my application [duplicate]

I have the following code:
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}
Do I need to mysql_real_escape_string in this case or am i okay?
No, you don't have to escape value yourself (i.e. no you don't need to call mysqli_real_escape_string), when you are using prepared statements : the DB engine will do that itself.
(Actually, if you were calling mysql_real_escape_string and using bound parameters, your strings would get escaped twice -- which would not be great : you'd end up with escaping characters everywhere...)
As a sidenote : your values are passed as integers (as indicated by the 'ii'), so you wouldn't have to call mysql_real_escape_string, even if you were not using prepared statements : as its name indicates, this function is used to escape... strings.
For integers, I generally just use intval to make sure the data I inject into my SQL queries really are integers.
(But, as you are using prepared queries, once again, you don't have to do that kind of escaping yourself)
No, you must not. Combining the two would result
in visible escape characters showing up in your data.
function dbPublish($status)
{
global $dbcon, $dbtable;
if(isset($_GET['itemId']))
{
$sqlQuery = 'UPDATE ' . $dbtable . ' SET active = ? WHERE id = ?';
$stmt = $dbcon->prepare($sqlQuery);
$stmt->bind_param('ii', $status, $_GET['itemId']);
$stmt->execute();
$stmt->close();
}
}

Why mysql_real_escape_string() did not prevent hack?

I've a website that hacked today. Server logs returned something like this as hacker's tries:
www.site.com/notifications.php?PID=7&id=999999.9%20union%20all%20select%20%28select%20distinct%20concat%280x7e%2C0x27%2Cunhex%28Hex%28cast%28schema_name%20as%20char%29%29%29%2C0x27%2C0x7e%29%20from%20%60information_schema%60.schemata%20limit%201%2C1%29%2C0x31303235343830303536%2C0x31303235343830303536%2C0x31303235343830303536--
But I've used mysql_real_escape_string() in my code:
if (isset($_GET['id']) && $_GET['id'] != '') {
$id = mysql_real_escape_string($_GET['id']);
} else {
$id = '';
}
if ($id == '') {
$stmt = "SELECT * FROM tbln13 ORDER BY id DESC";
} else {
$stmt = "SELECT * FROM tbln13 WHERE id = $id";
}
$NewsResult = mysql_query($stmt) or die (mysql_error());
Why my website could not prevent this attack?
Because escape_string add slashes and such to quotes. You didn't have any quotes in your query, or the string they submitted.
Your query doesn't have a STRING in it, it appears to expect an int. If you expected an integer, you should have verified it was an int, or forced it to an int, before using it in a query. Escaping a value as a string, then using it as an int, won't work.
Switch to prepared statements in MySQLi or PDO.
The sql injected query looks like this
SELECT * FROM tbln13 WHERE id = 999999.9
union all select
(select distinct concat(0x7e,0x27,unhex(Hex(cast(schema_name as char))),0x27,0x7e)
from `information_schema`.schemata
limit 1,1),
0x31303235343830303536, 0x31303235343830303536, 0x31303235343830303536--
as you see, you were injected because you have just allowed this!
You expected a number but you didn't check for it! So you got the number and something more.
You should have checked the $id variable for what you expected, which is the number. This is what I would use:
if (!preg_match('/^\d+$/', $id))
die("ERROR: invalid id"); // error, don't continue
Use prepared statements, that will, in most cases, prevent SQL injections.
A simple and comprehensible guide to prepared statements can be found in this website:
Bobby Tables
More over you should stop using MYSQL, it's outdated and will be removed in future implementations. Use MySQLi or PDO instead.
Because your escaped variable is not a string therefore it is not inside quotes in your query. If you want a quick fix you can change your query to:
$stmt = "SELECT * FROM tbln13 WHERE id = '$id'";
It is not standard use for numeric comparison but should work.
As mentioned by others, you should ditch deprecated mysql_* functions and instead used prepared statements via mysqli or PDO.
Even then you should also be validating your input, because just using prepared statements will not help you identify whether you have input values that are valid. You would ideally make sure all your input is valid before even attempting a prepared statement. In this case, this validation could be as simple as this:
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if (false === $id) {
// you do not have a valid integer value passed. Do something.
} else {
// continue with your prepared statement
}

Escaping strings

I'm using PDO and was under the impression that prepare escaped apostrophes but I can see that isn't the case. what do I use to escape my strings for apostrophes?
$sql = 'SELECT test FROM test WHERE id = :id';
$sth = $dbh->prepare($sql);
$sth->execute(array(':id' => 1));
$red = $sth->fetchAll();
I suspect that whilst you might be using a prepared statement, you are not binding parameters. For example, instead of
$val = "Some string with an a'postrophe in it";
$stmt = $pdo->prepare("UPDATE table SET col = '$val'");
$stmt->execute();
You should use
$val = "Some string with an a'postrophe in it";
$stmt = $pdo->prepare('UPDATE table SET col = :val');
$stmt->bindParam('val', $val);
$stmt->execute();
or at least
$val = "Some string with an a'postrophe in it";
$stmt = $pdo->prepare('UPDATE table SET col = :val');
$stmt->execute(array('val' => $val));
This is using named parameters but you can also use positional ones using ? as a placeholder
I am not sure I understand your question, but this might help with PDO escaping:
PDO::quote($data)
I Suspect you are not using preparred statements correctly, or there is something wrong with your code.
The docs specifically states:
The parameters to prepared statements
don't need to be quoted; the driver
automatically handles this. If an
application exclusively uses prepared
statements, the developer can be sure
that no SQL injection will occur
(however, if other portions of the
query are being built up with
unescaped input, SQL injection is
still possible).

How do I sanitize input with PDO?

Do I need to use mysql_real_escape_string() on my input (such as $_POST and $_GET) when I use the PDO library?
How do I properly escape user input with PDO?
If you use PDO you can parametize your queries, removing the need to escape any included variables.
See here for a great introductory tutorial for PDO.
Using PDO you can seperate the SQL and passed parameters using prepared statements, this removes the need to escape strings, as because the two are held seperately then combined at execution, the parameters are automatically handled as stings, from the above source:
// where $dbh is your PDO connection
$stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
/*** bind the paramaters ***/
$stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
$stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
/*** execute the prepared statement ***/
$stmt->execute();
Note: sanitization occurs during variable binding ($stmt->bindParam)
Other resources:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
http://www.phpeveryday.com/articles/PDO-Prepared-Statement-P550.html
http://php.net/manual/en/pdo.prepared-statements.php
The important point when using PDO is:
PDO will only sanitize it for SQL, not for your application.
So yes, for writes, such as INSERT or UPDATE, it’s especially critical to still filter your data first and sanitize it for other things (removal of HTML tags, JavaScript, etc).
<?php
$pdo = new PDO(...);
$stmt = $pdo->prepare('UPDATE users SET name = :name WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first
$name = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING); // <-- filter your data first
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO
$stmt->bindParam(':name', $name, PDO::PARAM_STR); // <-- Automatically sanitized for SQL by PDO
$stmt->execute();
Without sanitizing the user input, a hacker could have saved some javascript into your database and then, when output it into your site you would have been exposed to a threat!
http://www.phptherightway.com/#pdo_extension

Categories