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).
Related
I have a HTML form, from which a PHP script extracts values, as shown below:
$dbc = mysqli_connect("all required info here...") or die("Error occurred");
$sent = "Any sentence here...which may contain apostrophe or double quotes or both";
$query = "SELECT * FROM myrecord WHERE sentence = '$sent'";
$result = mysqli_query($dbc, $query);
$data = mysqli_fetch_array($result);
mysqli_close($dbc);
The problem is, that the variable $sent can contain any string with a combination of either apostrophe or double quotes or both. This gives an error when going for execution of mysqli_query().
So even if I escape double quotes in initialization of $sent it will still create problem for execution of mysqli_query(). And if I escape both for ' and " then value of $sent does not remains what it actually needs to be (although I am not sure about whether escaping both ' and " will work or not).
Is there any built in function that automatically escapes all special characters of a string? Or any workaround that solves this problem?
[P.S. I have already searched some previous questions on stackoverflow and haven't been able to find a solution.]
What you want, and what you should do is used prepared statements (parameterized queries). With PDO, that would look something like this:
$stmt = $pdo->prepare('SELECT * FROM myrecord WHERE sentence = :sentence');
$stmt->execute([':sentence' => $sentence]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
//do stuff
}
mysqli supports them, too, but the API is slightly more cumbersome (IMO) - see mysqli_prepare docs for details:
$stmt = $mysqli->prepare('SELECT * FROM myrecord WHERE sentence = ?');
//or $stmt = mysqli_prepare($connection, $query);
Then, you bind the parameter (the value to be used in the WHERE clause) using bind_param:
$stmt->bind_param('s', $sentence);
//or mysqli_stmt_bind_param($stmt, 's', $sentence);
Then call execute - or mysqli_stmt_execute, and fetch the results using fetch - or mysqli_stmt_fetch...
As mentioned in the comments: the parameters and query string needn't be quoted in any way, because they're treated as separate entities. The result being that you can re-use the same prepared statement with different paramters:
$stmt = $pdo->prepare('SELECT * FROM table WHERE field = :field');
$fieldVals = [123, 46, 32]; // three values:
$results = array_fill_keys($fieldVals, null);
foreach ($fieldVals as $val) {
$stmt->execute([':field' => $val]);//execute with each value in $fieldVals array
$results[$val] = $stmt->fetchAll(PDO::FETCH_ASSOC); // fetch results for this field value
//optional, but good form:
$stmt->closeCursor();
}
you've now used the same statement 3 times, but only had to send the query string once. The query had to be parsed and processed once, and after that, you merely sent the paramters to the DB. This approach is generally faster, safer (prepared statements protect agains most injection attacks), and just all round better.
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();
}
}
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);
Sorry if this seems a really stupid question, but I'm struggling to get to grips with changing from Mysql to Mysqli and prepared statements.
So in mysql, I would have done this:
$q=('SELECT * FROM table WHERE field="'.$variable.'"');
$result = mysql_query($q);
I now know this is not good. So I now have the below:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
$stmt->execute();
Problem is that the query doesn't work. Say the ? is actually "tree". So the query becomes:
'SELECT * FROM table WHERE field=tree LIMIT 1'
If I tried to run that query in say phpmyadmin I get "Unknown column tree in where clause". Obviously if I put quotes around it then it works, hence the original query. So how can I get this to work if I can't use quotes, since then you are looking for the literal question mark?
For reference I am then using this code:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
As I can't use get_result() which is very annoying. I have PHP version 5.4, and even the mysqlnd driver, but can't enable it as I'm on a VPS and my host says it might affect other sites on that server. Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Any help would be appreciated!
This:
$stmt = $mysqli->prepare('SELECT * FROM table WHERE field=? LIMIT 1');
$stmt->bind_param('s', $variable);
is not equivalent to this:
SELECT * FROM table WHERE field=tree LIMIT 1
Prepared statement placeholders are not the same as copy and pasting in values. You are binding the value "tree" as a string here, the database will actually understand this. The ? is not simply being replaced by the bound value, the database understands the difference between your query structure with its placeholders and the values you're binding into them. Binding the parameter this way is equivalent to running:
SELECT * FROM table WHERE field='tree' LIMIT 1
Consequently what is actually just two lines in MySql is actually now something like 15 lines in the 'improved' mysqli. Great.
Mysqli is not intended to be used as is. It is but a building material for the higher level library. When used wisely, it can give you data in one line:
$data = $db->getAll('SELECT * FROM table WHERE field=?s', $variable);
(BTW, the same goes for the old mysql ext as well)
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();
}
}