I have a function that's like
function getInfoById($id, $info) {
}
the idea is to have a query be "SELECT $info FROM table WHERE id = $id"
This doesn't work with PDO because you can't escape column names. I also don't really want to use "SELECT *" because doesn't that return a bigger result set and use more memory?
Yes, PDO does not have a builtin function for delimiting identifiers like table names and column names. The PDO::quote() function is only for string literals and date literals.
For what it's worth, when I worked on Zend Framework, I implemented a quoteIdentifier() function.
You're right that SELECT * fetches all columns, likely using more memory and spoiling the benefit of covering indexes.
My recommendation is to create an allowlist column names. That is, make sure $info actually names a column of table. Then you don't need to worry about the column name not existing, or containing a strange character, or anything. You get to control the set of columns that are legitimate to put in the query.
You should also delimit the column name anyway. Delimited identifiers are necessary if the column name contains punctuation, whitespace, international characters, or matches an SQL reserved word. See Do different databases use different name quote?
function getInfoById($id, $info) {
// you can make this a literal list, or query it from DESC or INFORMATION_SCHEMA
$cols = array('col1', 'col2', 'col3');
if (array_search($info, $cols) === false) {
return false;
}
$sql = "SELECT `$info` FROM table WHERE id = :id";
$stmt = $pdo->prepare($sql);
if ($stmt === false) {
return false;
}
. . .
}
I show more examples of allowlisting in my presentation SQL Injection Myths and Fallacies or my book SQL Antipatterns, Volume 1: Avoiding the Pitfalls of Database Programming.
I would just filter it out with some regex. Keep it simple.
Also, you should bind $id and have it be :id
$info = preg_replace('/[^A-Za-z0-9_]+/', '', $info);
$stmt = $pdo->prepare('SELECT $info FROM table WHERE id = :id');
$stmt->bindParam(':id', $id);
$stmt->execute();
What about using quote and substr.
$sql = 'SELECT * FROM table WHERE `' . substr($db->quote($field), 1, -1) . '` = :id';
This will remove the quotes surrounding the escaped field.
Related
I have multiple tables in my database named teacher1, teacher2.... I am trying to access them using a variable $id.
I have written down the following code.
$query = "SELECT * FROM table.$id";
How could i access those different tables using a variable.
I'm not clear from the question text whether your $id variable contains the full table name, or some kind of number.
However, in either case you have to make a slight tweak to your $query variable.
If $id contains the full name of the table (i.e. teacher1):
$query = "SELECT * FROM " . $id;
If $id contains a number used to identify which teacher table it is (i.e. 1, 2, 3, etc):
$query = "SELECT * FROM teacher" . $id;
Learn to use parameters!
$stmt = $conn->prepare($query = "SELECT t.* FROM table t where t.id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
Don't munge query strings with parameter values! This introduces SQL injection risk, can introduce syntax errors that are hard to debug, and can degrade performance.
For example:
$sql = "SELECT * FROM purch_inv WHERE '".$anyrow."'='".$anyrecord."'";
or
$sql = "SELECT * FROM '".$table."' WHERE 'rowabc'='".$anyrecord."'";
I have been trying this but it is not working, any ideas?
In php you could use variables in double quotes (ex.)
$name = 'John';
$var = "Hello $name!";
It shows: Hello John.
With single quotes (ex.)
$name = 'John';
$var = 'Hello $name!';
It shows: Hello $name.
Single-quotes mark a string literal.
But you want identfiers;
SELECT * FROM identifier1 WHERE identifier2='stringliteral'
a table name is an identifier. Identifiers
always can (and sometimes must) be wrapped in backticks.
SELECT x,y,z FROM `foo`
When I said a table name is an identifier, that wasn't entirely correct. In SELECT ... FROM foo foo is a name; it just so happens to be comprised of only one identifier, which is the table name.
There can be compound or multiple-part names, consisting of multiple identifiers connected via a dot between the parts. In that case you can (or must) wrap each single identifier in backticks; not the the whole name
SELECT x,y,z FROM `mydatabase`.`foo`
The fields you select (i.e. x,y and z in this case) are also names; so the same rules apply
SELECT `x`,`y`,`z` FROM `mydatabase`.`foo`
and again, in case you have multipart names you have to wrap each identifier individually in backticks, not the whole name
SELECT
`foo`.`x`,
`bar`.`x`,
`foo`.`y`,
`bar`.`z`
FROM
`foo`
JOIN
`bar`
ON
`foo`.`x`>`bar`.`y`
So, when do you use single quotes?
When you want a literal string in your query, like e.g.
SELECT x,y FROM foo WHERE y='abc'
this tells the MySQL parser that you want to compare the value of the field y to the string (literal) abc while
SELECT x,y FROM foo WHERE y=`abc`
would compare the value of the field y to the value of the field abc (which in my example doesn't exists and would therefore raise an error)
Full circle back to your question
$sql = "SELECT * FROM `$table` WHERE `rowabc`='$anyrecord'";
But please keep a good eye on http://docs.php.net/security.database.sql-injection regarding $anyrecord.
And make sure it is you (not the user) who is in control of $table.
yes you can
but you can't qoute the table name and column name
$sql="SELECT * FROM ".$table." WHERE rowabc='".$anyrecord."'";
the other example should be like this
$sql="SELECT * FROM purch_inv WHERE ".$anyrow."='".$anyrecord."'";
Obviously you need to think about SQL injection with the variables getting passed into your select query! So because table and column names cannot be replaced by parameters in PDO, you could use a function to create a whitelist of table names to pass into your query, then use a function with PDO to execute the statement:
$myWhitelist = array('table1', ...)
$myTable= array_intersect_keys($table1, array_flip($whitelist));
So now $table1 is safe to pass into your select function:
function select($conn, $table1, $someColumn) {
$myvar = $conn->prepare("SELECT FROM ".$table1." WHERE id = :someColumn");
$myvar->bindParam(":someColumn", $someColumn, PDO::PARAM_INT);
$myvar->execute();
if ($myvar->rowCount() > 0) {
return true;
} else {
return false;
}
}
I've two 3 variable and that used in $sql string
$bikeid = xxxxx
$st_char = column name
$st_tab = table name
I've coded out like this
$sql = "select $st_char
from $st_tab
where bike_id like '$bike_id'";
And like this
$sql = "select ".$st_char."
from dbo.".$st_tab."
where bike_id like ".$bike_id;
To select data from my database,the result is the same,they can get data from database
My question is which one is right and which one is wrong
if none wrong which one is better and why ?
Thanks
Both are bad because they are vulnerable to SQL injection.
There are also potential performance gains from using prepared statements. So at the very least, your query should look like:
select $st_char from $st_tab where bike_id like :bike_id
Unfortunately, you can't use parameters in certain situations, like column and table names. In this case you will need to do manual string concatenation, but whitelist allowed input. For example:
$allowed_cols = array('col1', 'col2', 'col3');
$allowed_tables = array('t1', 't2', 't3');
if(in_array($st_char, $allowed_cols, true) && in_array($st_tab, $allowed_tables, true))
{
$query = "select $st_char from $st_tab where bike_id like :bike_id";
// perform execution here
}
else
{
// invalid or malicious input
}
You may also want to wrap the table/column names in square brackets ([]) to avoid conflicts with any reserved keywords:
$query = "select [$st_char] from [dbo].[$st_tab] where bike_id like :bike_id";
I am trying to set up PHP queries for MySQL in a way to prevent SQL injection (standard website).
I had a couple of INSERT queries where changing this worked well but on the following SELECT I keep getting an error since the update and it looks like the while loop doesn't work with the changes I made (it works well without using the statement as in the old code).
Can someone tell me what I am doing wrong here ?
New PHP:
$stmt = $conn->prepare("SELECT ? FROM TranslationsMain WHERE location LIKE '%calendar weekday%' ORDER BY sortOrder, ?");
$stmt->bind_param('s', $selectedLang);
$stmt->execute();
$result = $stmt->get_result();
while($arrCalWeekdays = $result->fetch_assoc()){
$calWeekdays[] = $arrCalWeekdays;
}
$conn->close();
Old PHP (changed part):
$sql = "SELECT " . $selectedLang . " FROM TranslationsMain WHERE location LIKE '%calendar weekday%' ORDER BY sortOrder, " . $selectedLang;
$result = $conn->query($sql);
while($arrCalWeekdays = $result->fetch_assoc()){
$calWeekdays[] = $arrCalWeekdays;
}
$conn->close();
Error message:
Fatal error: Call to a member function fetch_assoc() on a non-object in /homepages/21/d580042014/htdocs/myform.php on line 21
Many thanks in advance.
You cannot bind column and table names, only data. You need to specify the table and then bind for your '%calendar weekday%'.
$stmt = $conn->prepare("SELECT " . $selectLang . " FROM `TranslationsMain` WHERE `location` LIKE ? ORDER BY `sortOrder`, " . $selectedLang);
$stmt->bind_param('s', $calendar_weekday);
If you want to use dynamic table / column names you should perform the minimal white-listing of those items. You can build a dynamic white list by asking the database what columns are valid for a given database table. For example:
SELECT `COLUMN_NAME`
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA` = `database_name`
AND `TABLE_NAME` = `table_name`
You could place all of this information in arrays and then check to make sure the table / column names used in the query are in the arrays. Extra consideration for table and column names should be performed, making sure that no key / reserved words are used for these names.
Finally, use backticks around the validated table / column names when calling the values for the dynamic queries. This will cover any potential changes to the key / reserved words list and provides an additional layer of protection.
My database table has many columns.
I want to do a search based on multiple columns.
Sometimes it may not be the value of some columns.
How do these fields in sql query to be ineffective?
Thank you.
for examle:
$C1=$_POST[c1];
$C2=$_POST[c2];
SELECT * FROM mytable WHERE column1='$c1' AND column2='$c2'
i want if C2 be nulled, disable it from sql query.
One way is:
if(!$_POST[C2]){
SELECT * FROM mytable WHERE column1='$c1'
}
...
I want do it through sql query to do because My table has many columns.
First, you should never write queries with variables inside like that. Learn about PDO / mysqli and prepared statements.
Second, key references for an array should either be a string or integer; the expression $_POST[c1] will most likely cause a notice and implicit conversion to a string. It's better to write $_POST['c1'].
Third, and to answer your question, you can use isset() and strlen() to determine whether a value is "empty", i.e. empty string.
$params = array($_POST['c1']); // you should also check whether $_POST['c1'] is defined too
$sql = 'SELECT * FROM `table_name` WHERE column1 = ?';
if (isset($_POST['c2']) && strlen($_POST['c2'])) {
$sql .= ' AND column2 = ?';
$params[] = $_POST['c2'];
}
$stmt = $db->prepare($sql);
$stmt->execute($params);
Build an array of conditions by iterating through the POST values, adding a condition if the respective POST parameter is not empty:
$conditions = array();
foreach ($_POST as $key => $value) {
if (!empty($value)) {
$conditions[] =
$dbcolumn[$key] . " = '" . mysql_real_escape_string($value) . "'";
}
}
You will need an array $dbcolumn that matches POST variables to the database columns (or you have to provide some other means of translating between the two).
Now create a SQL query for the given conditions:
$query = 'SELECT * FROM mytable';
if (!empty($conditions)) {
$query .= ' WHERE ' . join(' AND ', $conditions);
}
Note that the extension that provides mysql_real_escape_string() is deprectaded. You should probably use some other extension to comunicate with the MySQL server and would than have to use the repsective call of the other extension.
This code not recomended, but if you realy want to do it on MySQL, you can use LIKE syntax like this:
SELECT * FROM mytable WHERE column1="$c1" AND column2="$c2%"
Add % character before or after $c2
Please don't do it!!