I am having this piece of code:
$result = mysqli_query($con , 'SELECT * FROM messages WHERE group = "'.$_POST['group'].'" ORDER BY date '.$_POST['order'].'');
I don't understand why it is always returning me false. The variables $_POST['group'] and $_POST['order'] aren't empty .
$_POST['group']='PHP'
$_POST['order']='DESC'
The conecction to the database is corect too.
GROUP is a mysql reserved word and needs to be quoted using backticks;
SELECT * FROM messages WHERE `group` = ...
You're also wide open to SQL injection, you should never add un-validated/un-escaped POST data in string format to your SQL. The safest way to do queries with user data is using prepared statements, or - as a less secure alternative - escape the data using mysqli_real_escape_string.
$result = mysqli_query($con , "SELECT * FROM messages WHERE group = '".mysqli_real_escape_string($_POST['group'])."' ORDER BY date '".mysqli_real_escape_string($_POST['order'])."'";
Try formatting the query like this and see if it helps your result. I also added mysqli_real_escape_string() to escape your input, as your query was wide open to SQL injection.
http://php.net/manual/en/mysqli.real-escape-string.php
Related
new to php and am enrolled on a course, so can ask tutor tomorrow if this is more complicated than i think it might be!
I have an sql query, and it works fine. But I am trying to add and 'and' in the select statement.
This is what I have at the minute
$query = "SELECT * from table1 where table1.age <= " . $_POST['min_age'] ;
I have a 'region' input on my linked html page and want results to be returned only if the min_age and region values match those inputted by the user.
I have tried adding an 'and where' but it doesn't work and I am not sure if it is because of the multiple "'s or if what I am trying to do needs a different method?
Thanks
If you need multiple conditions, just separate them with AND:
... WHERE table1.age <= ? AND table1.region = ?
No need to use WHERE again. Just like you wouldn't need to use if() more than once if you were writing a complex condition in PHP.
PS: This isn't directly related to your question, but you should get into the habit of not putting $_POST or $_GET variables directly into your SQL queries. It's a good way to get hacked! Ask your tutor about "SQL injection," or read my presentation SQL Injection Myths and Fallacies.
I know you're just starting out, but if you were training to be an electrician, you would place a high priority on learning how to avoid being electrocuted or how to avoid causing a fire.
Here's how I would write your query using mysqli. One advantage of using query parameters is you never need to worry about where you start and end your quotes.
$query = "SELECT * from table1 where table1.age <= ? AND table1.region = ?";
$stmt = $mysqli->prepare($query) or trigger_error($mysqli->error, E_USER_ERROR);
$stmt->bind_param("is", $_POST["min_age"], $_POST["region"]);
$stmt->execute() or trigger_error($stmt->error, E_USER_ERROR);
The other good habit I'm showing here is to always report if prepare() or execute() return an error.
If you must interpolate variables into your SQL, first make sure you protect the variables either by coercing the value to an integer, or else by using a proper escaping function like mysqli_real_escape_string(). Don't put $_POST variables directly into the string. Also you don't have to stop and restart the quotes if you use PHP's syntax for embedding variables directly in double-quoted strings:
$age = (int) $_POST["min_age"];
$region = $mysqli->real_escape_string($_POST["region"]);
$query = "SELECT * from table1 where table1.age <= {$age}
AND table1.region = '{$region}'";
I do understand that the prepared statements is the ultimate way to seek protection against the SQL injection. However, they provide coverage in a limited fashion; for example, in cases where I let the user to decide how the order by operation to be ( i.e, is it ASC or DESC? etc ), I get no coverage there with the prepared statements.
I understand that I can map the user input to a pre-defined white list for that. But, this is only possible when a whitelist can be created or guessed thoroughly beforehand.
For example, in the cases I mention above ( the ASC, or DESC ), this can easily be mapped and verified against a list of accepted values. But isn't there a situation where the portion of the SQL statement cannot be verified against a white list?
If such a situation exists, then what's the recommended approach?
If I were to escape the user_input using the underlying database's built-in escape utility (such as mysqL_real_escape_string for mysql) across the board, where would I fail?
I'm asking this question with the assumption that I always construct my sql statements with quoted values - even for integers...
Let's take a look at the following example and reflect upon it..
select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}
Assume all vars are user supplied.
If I were to mysql_real_escape_string all the variables in the above SQL ( as opposed to using prepared statements which covers me only half-way forcing me to come up whitelists for the other half that it cannot help), wouldn't it be equally safe (and easier to code)? If not, in which input scenario escape utility would fail?
$fields = mysql_escape($fields);
$table = mysql_escape($table);
$age = mysql_escape($age);
$orderby_pref = mysql_escape($orderby_pref);
select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}
You always need to use white-lists for stuff like table- or column names, whether you use prepared statements or the mysql escape functions.
The problem is that table names and column names are not quoted in single or double quotes, so if you use a function that specifically quotes these characters (and some more of course...), it will do nothing for your table name.
Consider the table name my_table; DELETE * FROM mysql; SELECT * FROM my_table. Nothing in this string will get escaped by mysql's escape functions but it is definitely a string you would want to check against a white-list.
Apart from that the mysql escape functions have a problem with character sets that can render them useless, so you are always better off with prepared statements.
You could use PDO and your life will get easier ... :
# Order
switch(strtoupper($Order)){
default:
case 'ASC':
$Order = 'ASC';
break;
case 'DESC':
$Order = 'DESC';
break;
}
# ID
$ID = 39;
$Username = 'David';
# Query
$Query = $this->DB->Main->prepare('SELECT * FROM Table WHERE ID = :ID AND Username = :Username ORDER BY HellBob '.$Order);
$Query->bindValue(':ID', $ID, PDO::PARAM_INT);
$Query->bindValue(':Username', $Username, PDO::PARAM_STR);
# All good ?
if(!$Query->execute()){
exit('Error');
}
// Results
$Row = $Query->fetch(PDO::FETCH_ASSOC);
You don't have to worry about quotes or SQL injections. You can use simple "white list" as you mention to get variable into your query.
http://php.net/manual/en/pdo.prepared-statements.php
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).
What are the possible scenarios where some of the input is unescaped? Is that even possible if all the other input goes into the database using PDO?
I'm thinking of the scenario where other input is processed with mysql_* functions and not escaped with mysql_real_escape_string. Is there anything else that could be a threat?
Thanks a lot. Regards
It means you cannot use untrusted values directly e.g. as a column or table name - or as a LIMIT parameter.
For example, this is safe:
$query = "SELECT * FROM tbl WHERE col = ?";
while these aren't:
$query = 'SELECT * FROM tbl WHERE col = ? LIMIT ' . $_GET['limit'];
$query = 'SELECT * FROM tbl WHERE ' . $_GET['field'] . ' = ?';
$query = "SELECT * FROM tbl WHERE col = ? AND othercol = '" . $_GET['other'] . "'";
$query = 'SELECT * FROM ' . $_GET['table'] . ' WHERE col = ?';
Basically, prepared statements' placeholders are meant to be used in places where you would have used an escaped value within single quotes in a classical query.
In case you wonder why databases usually do not support placeholders for things like table names: Besides the fact that dynamic table/column names are not that common, the database engine usually optimizes a prepared statement when it's prepared. This however cannot be done properly without knowing exactly which tables/columns are accessed.
Consider this:
$sql = "SELECT * FROM ".$_GET['tablename']." WHERE somecol = ?";
Because I populated the table name with un-escaped user input, it would be possible to pass in for example public_table p LEFT JOIN hidden_table h ON h.id = p.id and get results you didn't want me to, even though you have escaped the value passed to the somecol comparison.
The point is that while prepared statements safely escape any user input you pass to a ? in the query, they can't escape data that already existed in the string before you passed it to prepare().
It means don't be lured into thinking PDO is magic pill...if you don't use prepared statements, you will still be vulnerable.
I have posted about this before but never in this regard so please take a look:
I was told one way to do sql injections was to use 1=1 where someone can see all entries that don't belong to them.
But lets say i structure my query so that it also selects the user_id of the current user, would that work:
$userid = Current users stored id in database;
$postid = mysql_real_escape_string($_GET['id']);
And now lets assume that i enter: domain.com/page.php?id='' OR '1'='1'
Select article_name from table where user_id=$userid and post_id=$postid
Will the query still return everything or will it not since i have added the User_id barrier?
If you use PDO you don't have to worry about escaping data (in this situation):
$stmt = $pdo->prepare('SELECT article_name FROM table WHERE user_id = :userid AND post_id = :postid');
$stmt->execute(array(
':userid' => $userid,
':postid' => intval($_GET['id']) //Just to be safe
));
// You could also do this instead (thanks #Digital Precision)
//$stmt->bindValue(':postid', $_GET['id'], PDO::PARAM_INT);
//$stmt->execute(array(':userid' => $userid));
while($row = $stmt->fetch()) {
//Work with data
}
For more on PDO see the PHP docs.
The problem with using mysql_real_escape_string() is that as its name suggests it only escapes strings. It escapes the characters that can be used to terminate a string so that an attacker can't close a string and enter malicious SQL.
If you are stubborn and refuse to use PDO, you could use a function like intval() on any unsanitized integers to ensure they contain only numbers.
$post_id = intval($_GET['id']); //Now $post_id can only be a number
mysql_real_escape_string() is for sanitizing strings only. It does NOT protect from SQL injection in integers that are not wrapped in quotes, so your observation is correct: What is shown above is indeed not safe despite mysql_real_escape_string().
You need to either wrap your values in quotes:
Select article_name from table where user_id='$userid' and post_id='$postid'
or make sure that $userid and $postid are integers before running the query.
Not sure what you mean by "I was told one way to do sql injections was to use 1=1 where someone can see all entries that don't belong to them".
1=1 always evaluates to true. I've only ever seen this done when the query being generated by the application has only conditional where clauses with no root where clause. Not sure what it has to do with protecting you from sql injections.
Your query would look like:
Select article_name from table where user_id=$userid and post_id=\'\' OR \'1\'=\'1\'
As other mention while i typing this, it is better to quote your values. So you will have:
Select article_name from table where user_id=$userid and post_id='\'\' OR \'1\'=\'1\''
This returns nothing, if there is not a post with such id.
So your query will not return every post from the current user. But keep in mind to quote your values.
Simple question: how can I protect so that when the user inputs "union select" in a field and then sends it as a parameter to a query mysql won't give an error.
Should I simply check if "union select" is inputed or there are also other keywords that could result in a mysql error?
Here's the SQL protection function I use now:
function sql_protect(&$n){
$n=stripslashes($n);
$n=mysql_real_escape_string($n);
$dntwant = array("\'","\\", "/","\"",")","(","<",">","\\r\\n",";","*","%");
$n = str_replace($dntwant,"", $n);
}
The query has something similar in it:
where column1 like '%$user_input%'
You need two things
Either use PDO, no checking needed PDO does everything.
Or use :
$var = mysql_real_escape_string($_POST['var']);
$query = "SELECT * FROM test WHERE var = '$var' ";
// ^ ^ These single quotes are vital!!
This is all you need to do, forget about stripslashes, and str_replace.
If you want to use dynamic database/table/column names, or you want to inject numbers for the limit clause, be warned mysql_real_escape_string will not work. Neither will PDO.
Nothing will work except for checking the input you are about to inject against a pre-approved whitelist.
See this question on how to do that: How to prevent SQL injection with dynamic tablenames?
Try to prepare and execute the query:
$stmt = $db->prepare("SELECT * FROM test WHERE column1 LIKE ?");
$stmt->execute(array("%{$user_input}%"));