Using mysql_real_escape_string in IN clause - php

mysql_real_escape_string adds slashes to the values in IN clause and hence no values are returned. How can I send array values that are escaped using mysql_real_escape_string() in IN clause?
Here is my code:
$names_array = array('dave','smith');
$names = mysql_real_escape_string("'". implode("', '", $names_array) ."'");
$sql = "SELECT * FROM user WHERE user_name IN ($names)";
$results = mysql_query($sql);
Query after mysql_real_escape_string changes like this:
SELECT * FROM user WHERE user_name IN (\'dave\', \'smith\')
I don't want these slashes here in IN clause. Also I don't want the values directly substituted in IN clause.
Thanks in Advance.

This might do it.
$names = "'". implode("', '", array_map('mysql_real_escape_string', $names_array)). "'";

Don't use mysql_real_escape_string; don't use the mysql_* functions directly at all; use ADODB or somesuch; don't concatenate your queries in this way, use placeholders (?) and prepared statements. Your code should look similar to this:
include('/path/to/adodb.inc.php');
$DB = NewADOConnection('mysql');
$DB->Connect($server, $user, $pwd, $db);
# M'soft style data retrieval with binds
$rs = $DB->Execute("select * from user where user_names in ?",array(array('dave','smith')));
while (!$rs->EOF) {
print_r($rs->fields);
$rs->MoveNext();
}

Related

How to insert a PHP variable into an SQL query

I have an SQL query
qry1 =
"SELECT DISTINCT (forename + ' ' + surname) AS fullname
FROM users
ORDER BY fullname ASC";
This gets forename and surname from a table called users and concatenates them together, putting a space in the middle, and puts in ascending order.
I then put this into an array and loop through it to use in a select drop-down list.
This works, however, what I now want to do is compare the fullname with a column called username in another table called users.
I'm struggling with how to write the query though. So far I have...
$qry2
"SELECT username
FROM users
WHERE (forename + ' ' + surname) AS fullname
=" . $_POST['Visiting'];
Any advice on to what I am doing wrong?
Rather CONCAT the two columns together. Also remember to escape any variables before adding them to your query.
$qry2 =
"SELECT username AS fullname
FROM users
WHERE CONCAT(forename, ' ', surname)
='" . mysqli_real_escape_string($connection, $_POST['Visiting']) . "'";
Where $connection is your current db connection
I'm not sure that the use of the declared word 'AS' after 'WHERE' is correct in principle.
if you use MySQL, query should look like this:
SELECT [columns]
FROM [tables] [AS declareTableName]
WHERE [condition]
GROUP BY [declares|columns]
SORT BY [declares|columns]
But, i think your problem not in the query. Concatenating names in the query is incorrect. You must separate string with names in Back-end and than use it in query:
$names = explode(' ', $_POST['Visiting']);
This might work, assuming you use PDO:
$qry2 = "SELECT username FROM users
WHERE CONCAT(forename, ' ', surname) = '" . $conn->quote($_POST['Visiting']) . "'";
...but you should have a look at the possible vulnerabilities through SQL injections.
Without knowing which library you use for connecting to the MySQL database, it's impossible to give proper advise about which method you should use for escaping the user's input. quote is the PDO method for escaping, real-escape-string is the equivalent for MySQLi
You should really refer to using PDO.
When using PDO you can bind parameters to specified parts of your query. PDO also has built-in SQL-injection prevention, which is a great security measure that you won't have to deal with yourself. I hope this answers your question. See my example below.
Example:
// Create a new PDO object holding the connection details
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// Create a SQL query
$query = "SELECT username FROM users WHERE (forename + ' ' + surname) AS fullname = :visiting;";
// Prepare a PDO Statement with the query
$sth = $pdo->prepare($query);
// Create parameters to pass to the statement
$params = [
':visiting' => $_POST['Visiting']
]
// Execute the statement and pass the parameters
$sth->execute($params);
// Return all results
$results = $sth->fetchAll(PDO::FETCH_ASSOC);
If you have any other questions about PDO, please refer to the following:
Official PDO documentation:
http://php.net/manual/en/book.pdo.php
Documentation on how to bind variables:
http://php.net/manual/en/pdostatement.bindparam.php
You can use this construction (without "AS fullname" and with apostrophes around variable):
$qry2 "SELECT username FROM users WHERE (forename + ' ' + surname) = '" . $_POST['Visiting'] . "'";
But for better security (SQL injection) You should use the escaping of variable. For example this construction, if You use MySQL database:
$qry2 "SELECT username FROM users WHERE (forename + ' ' + surname) = '" . mysql_real_escape_string($_POST['Visiting']) . "'";

PDO bindValue for table and column

Ok so I just found out that I can't use placeholders for table names and columns
$table = 'users';
$stmt = $db->prepare('SELECT * from ?');
$stmt->bindValue(1, $rable, ??);
So what is really an alternative to having dynamic table names?
$stmt = $db->prepare('SELECT * from '.$table);
This would be vulnerable. Is there an escape_string method for PDO? I went through the manual but didn't seem to catch one. All I found was quote but that doesn't work for tables and columns. Is there any way I can securely implement this functionality, or will I have to switch back to using mysqli?
For escape String
From the link:http://php.net/manual/en/pdo.prepare.php
Calling PDO::prepare() and PDOStatement::execute() for statements that will be issued multiple times with different parameter values optimizes the performance of your application by allowing the driver to negotiate client and/or server side caching of the query plan and meta information, and helps to prevent SQL injection attacks by eliminating the need to manually quote the parameters.
Prepare values are only for fields.
Regarding dynamic table names
Append it table name to query as u did in second statement.
Example
$pdo = new PDO('mysql:host=localhost;dbname=site;',USER,PASS);
$query = $pdo->prepare("DESCRIBE :table");
$query->bindValue(':table', $table, PDO::PARAM_STR, strlen($table));
$query->execute();
while($field = $query->fetch(PDO::FETCH_NUM)){
var_dump($field);
//do something
}
unset($pdo);
Bindable Marks (?) or Bindable named Marks (:foo) couldn't appear as table-names or (pseudo-dynamic-) fieldnames. Both are limited to field-values.
You should avoid dynamic tables inside of your application. Just normalize your database to an more agile and intelligent structure.
If you use ticks, then you can simply replace ticks in the user input, and you should be fine:
$column = 'foo';
$table = 'bar';
$query = 'SELECT ' . $column . ' FROM ' . $table; // Insecure!
$query = 'SELECT `' . str_replace('`', '', $column) . '` FROM `' . str_replace('`', '', $table) . '`'; // Not insecure

Possible to have PHP MYSQL query ignore empty variable in WHERE clause?

Not sure how I can do this. Basically I have variables that are populated with a combobox and then passed on to form the filters for a MQSQL query via the where clause. What I need to do is allow the combo box to be left empty by the user and then have that variable ignored in the where clause. Is this possible?
i.e., from this code. Assume that the combobox that populates $value1 is left empty, is there any way to have this ignored and only the 2nd filter applied.
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND location = '$value1' AND english_name = $value2 ";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Thanks for any help.
C
Use
$where = "WHERE user_id = '$username'";
if(!empty($value1)){
$where .= "and location = '$value1'";
}
if(!empty($value2 )){
$where .= "and english_name= '$value2 '";
}
$query = "SELECT * FROM moth_sightings $where";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Several other answers mention the risk of SQL injection, and a couple explicitly mention using prepared statements, but none of them explicitly show how you might do that, which might be a big ask for a beginner.
My current preferred method of solving this problem uses a MySQL "IF" statement to check whether the parameter in question is null/empty/0 (depending on type). If it is empty, then it compares the field value against itself ( WHERE field1=field1 always returns true). If the parameter is not empty/null/zero, the field value is compared against the parameter.
So here's an example using MySQLi prepared statements (assuming $mysqli is an already-instantiated mysqli object):
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = ?
AND location = IF(? = '', location, ?)
AND english_name = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('ssss', $username, $value1, $value1, $value2);
$stmt->execute();
(I'm assuming that $value2 is a string based on the field name, despite the lack of quotes in OP's example SQL.)
There is no way in MySQLi to bind the same parameter to multiple placeholders within the statement, so we have to explicitly bind $value1 twice. The advantage that MySQLi has in this case is the explicit typing of the parameter - if we pass in $value1 as a string, we know that we need to compare it against the empty string ''. If $value1 were an integer value, we could explicitly declare that like so:
$stmt->bind_param('siis', $username, $value1, $value1, $value2);
and compare it against 0 instead.
Here is a PDO example using named parameters, because I think they result in much more readable code with less counting:
$sql = "SELECT *
FROM moth_sightings
WHERE user_id = :user_id
AND location = IF(:location_id = '', location, :location_id)
AND english_name = :name";
$stmt = $pdo->prepare($sql);
$params = [
':user_id' => $username,
':location_id' => $value1,
':name' => $value2
];
$stmt->execute($params);
Note that with PDO named parameters, we can refer to :location_id multiple times in the query while only having to bind it once.
if ( isset($value1) )
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND location = '$value1' AND english_name = $value2 ";
else
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' AND english_name = $value2 ";
But, you can also make a function to return the query based on the inputs you have.
And also don't forget to escape your $values before generating the query.
1.) don't use the simply mysql php extension, use either the advanced mysqli extension or the much safer PDO / MDB2 wrappers.
2.) don't specify the full statement like that (apart from that you dont even encode and escape the values given...). Instead use something like this:
sprintf("SELECT * FROM moth_sightings WHERE 1=1 AND %s", ...);
Then fill that raw query using an array holding all values you actually get from your form:
$clause=array(
'user_id="'.$username.'"',
'location="'.$value1.'"',
'english_name="'.$value2.'"'
);
You can manipulate this array in any way, for example testing for empty values or whatever. Now just implode the array to complete the raw question from above:
sprintf("SELECT * FROM moth_sightings WHERE 1=1 AND %s",
implode(' AND ', $clause) );
Big advantage: even if the clause array is completely empty the query syntax is valid.
First, please read about SQL Injections.
Second, $r = mysql_numrows($result) should be $r = mysql_num_rows($result);
You can use IF in MySQL, something like this:
SELECT * FROM moth_sightings WHERE user_id = '$username' AND IF('$value1'!='',location = '$value1',1) AND IF('$value2'!='',english_name = '$value2',1); -- BUT PLEASE READ ABOUT SQL Injections. Your code is not safe.
Sure,
$sql = "";
if(!empty($value1))
$sql = "AND location = '{$value1}' ";
if(!empty($value2))
$sql .= "AND english_name = '{$value2}'";
$query = "SELECT * FROM moth_sightings WHERE user_id = '$username' {$sql} ";
$result = mysql_query($query) or die(mysql_error());
$r = mysql_numrows($result);
Be aware of sql injection and deprecation of mysql_*, use mysqli or PDO instead
I thought of two other ways to solve this:
SELECT * FROM moth_sightings
WHERE
user_id = '$username'
AND location = '%$value1%'
AND english_name = $value2 ";
This will return results only for this user_id, where the location field contains $value1. If $value1 is empty, this will still return all rows for this user_id, blank or not.
OR
SELECT * FROM moth_sightings
WHERE
user_id = '$username'
AND (location = '$value1' OR location IS NULL OR location = '')
AND english_name = $value2 ";
This will give you all rows for this user_id that have $value1 for location or have blank values.

Parameterised IN Clause in prepared statement using MySql,PHP and ADODB

I am writing some SQL and using AdoDb to connect to my database and run the queries and so on. I am using parametrized queries and have run into a snag.
Is their a way to pass an array of values to an in_clause in AdoDb/MySql for parametrization.
My problem is that if I pass a prepared string as the parameter i.e. 'test','test2','test3' it does not work as the library or database auto escapes it and adds external quotes at the start and end so all the internal quotes are then auto escaped thus the query returns nothing as it looks for '\'test\',\'test2\',\'test3\'' as opposed to what I fed it.
UPDATED WITH ANOTHER POSSIBLE METHOD TO ACCOMPLISH THIS
<?php
$in_clause = implode(",", $first_names);
$query = "
SELECT
mytable_id_pk
FROM
mytable
WHERE
FIND_IN_SET(mytable_fname," . $DB->Param('first_names') . ")"
$stmt = $DB->Prepare($query);
$result = $DB->Execute($stmt,array($in_clause));
?>
I would do it this way (as I was googling for a while and google came up with nothing useful):
$count = count($first_names);
$in_params = trim(str_repeat('?, ', $count), ', ');
$query = "
SELECT
mytable_id_pk
FROM
mytable
WHERE
mytable_fname IN ({$in_params});";
$stmt = $DB->Prepare($query);
$result = $DB->Execute($stmt, $first_names);
This should do it...
First a few tips:
Please read carefully the AdoDB documentation on prepared statements.
Never include ; in SQL query strings.
You can try something like this:
$question_marks = substr(str_repeat('?,', count($first_names)), 0, -1);
$query = "SELECT mytable_id_pk FROM mytable WHERE mytable_fname IN (" . $question_marks . ")";
$stmt = $DB->Prepare($query);
$result = $DB->Execute($stmt,$first_names);
WARNING: I haven't tested this (not having a mySQL installation here).

Passing values to MySQL IN operation in PDO prepared statement?

I have a form field that is returning a comma-delimited string that I want to pass in to a PHP PDO MySQL query IN operation, but the IN operation requires that the values be comma-delimited (as opposed to my string of delimited values).
How do I do this?
$values = $_POST['values']; # '10,5,4,3' (string)
$query = "SELECT * FROM table WHERE id IN (:values)";
$data = array( ':values' => $values );
You can't pass in multiple values in a single placeholder. You will have to enter a different placeholder for each value to be passed into IN (). Since you don't know how many there will be, use ? instead of named parameters.
$values = explode(',', $values) ;
$placeholders = rtrim(str_repeat('?, ', count($values)), ', ') ;
$query = "SELECT * FROM table WHERE id IN ($placeholders)";
$stm = $db->prepare($query) ;
$stm->execute($values) ;
PDO alone cannot bind arrays to a :parameter. You need a helper function for that.
Also in your example, the literal string '10,5,4,3' would be received as one value. Parameter binding will turn it into .. id IN ('10,5,4,3') and not into a list comparison.
The workaround in your case would be to fall back on using dynamic SQL and escaping.
$values = preg_replace('/[^\d,]/', "", $_POST['values']);
$query = "SELECT * FROM table WHERE id IN ($values)";
I'm personally using a wrapper/helper function which has a special syntax for arrays (but you don't actually have one to begin with, so it would be a double workaround):
db("SELECT * FROM table WHERE id IN (??)", explode(",",$values));
The trick is to recognize that $values is a bunch of individual values, and set up your query with this in mind. This is easier to do if you use ? placeholders instead of named placeholders. For example, you could do something like the following:
$values = explode(',', $_POST['values']); //array(10,5,4,3)
$placeholder_string = implode(',', array_fill(0, count($values), '?')); // string '?,?,?,?'
$query = "SELECT * FROM table WHERE id IN ($placeholder_string)";
$statement = $db->prepare($query);
$statement->execute($values);

Categories