I have heard that sprintf() protects against SQL injection. Is it true? If so, how?
Why people are recommending to write query like this:
$sql = sprintf('SELECT * FROM TABLE WHERE COL1 = %s AND COL2 = %s',$col1,$col2);
sprintf won't protect you! It only replaces the %s
you must mysql_real_escape_string so:
$sql = sprintf('SELECT * FROM TABLE WHERE COL1 = "%s" AND COL2 = "%s"',
mysql_real_escape_string($col1),
mysql_real_escape_string($col2));
is safer injection
note: I suggest you take a look at PDO, it is what I like to use for DBconections and queries
That doesn't do any protection. Using sprintf makes for more readable code then dropping in and out of a string to run mysql_real_escape_string over each of the variables … but that example doesn't escape the variables at the end so that advantage is lost.
If you want decent protection, use something that provides bound parameters.
Using sprintf might protect against SQL injection for numeric fields:
$sql = sprintf("SELECT * FROM table WHERE col1 = %i", $col1);
By using sprintf in this way, you can be sure that $col1 will be converted to an integer--although it might generate an error or warning, if it's not truly an integer.
The proper way to protect against SQL injection is to check all of your input values, and do escaping. But that's much more thoroughly covered in other questions, so I'm not going to go into detail here.
It obviously doesn't and if you've actually read that in a book or tutorial you should automatically discard it for future reference.
However, it can be a practical way to generate output that needs further processing. Please compare:
echo '<p>Hello, <strong></strong>' . htmlspecialchars($name) . ', welcome to ' . htmlspecialchars($place). '</p>';
echo sprintf('<p>Hello, <strong>%s</strong>, welcome to %s</p>',
htmlspecialchars($name),
htmlspecialchars($place)
);
Same applies to other kind of output, such as SQL code, but of course you still need to do something to input in order to make it safe: sprintf() is just a regular string function that's unaware of SQL and databases.
Please note that bind parameters use a similar syntax:
// Fictional DB abstraction layer
$sql = 'SELECT foo_id
FROM foo
WHERE name=:name AND status=:status';
$params = array(
'name' => $name,
'status' => $status,
);
$result = $db->run($sql, $params);
That's why I particularly find easier to use those DB libraries that provide this syntax, such as PDO.
Related
I need some opinions about my php coding. I'm specially curious if this is safe against sql injections. Apparently it seems to be, but I might be wrong.
And what do you think of this "style" of coding, as in, is it acceptable or really bad practice ?
$validinputs = array(1,9,21,'a','b');
if(in_array($_GET['search'], $validinputs))
{
$queryfilter = " = " . $_GET['search'];
}
else
{
$queryfilter = "IS NOT NULL";
}
(...)
$query = "SELECT * FROM `table` WHERE `field` {$queryfilter}";
Thanks!
EDIT: In this case i compare with $validinputs because these are the only valid search terms for that field, any other search term would return nothing.
In my opinion, this is acceptable but not good practice at all. Why don't you use the standard SQL escape functions? These are really powerful and maintainable when wrapped in a class. I don't think that someone wants to maintain your application / script when you need to escape your strings this way that often. Probably causing a huge mess.
Ideally, you'd use stored procedures and your query would look like so...
$query = "call find_in_table('" . $_GET['search'] . "')";
... but since you have a list of acceptable inputs and are very strictly filtering them before passing them into a query string, I would say you're quite safe from SQL injection and using a stored procedure would be a performance enhancement for you more so than a security feature.
Any way to prevent malicious sql statements without using prepared statements and parameterized queries?
Example after simplify:
<?php
$con = mysqli_connect($_POST['db_server'], $_POST['db_user'],
$_POST['db_password'], $_POST['db_database']) or die(mysql_error());
$result = mysqli_query($con, $_POST['query_message']);
?>
Is it possible to check out the parameter $_POST['query_message'] is safe or not?
You should always build your queries within your code and then sanitise any variables you're going to use within them. NEVER pass the query or the database connection variables in via $_POST unless your user is querying the database via that form, in which case I'd recommend you just install phpMyAdmin.
As for sanitising your variables, if you really don't want to use PDO's prepared statements, you can sanitise incoming integers as follows:
$id = (isset($_POST['id']) ? (int)$_POST['id'] : null);
if ($id) {
$sql = "SELECT *
FROM `table`
WHERE `id` = {$id}";
}
And for strings use this:
$username = (isset($_POST['username']) ? mysqli_real_escape_string($con, $_POST['username']) : null);
if ($username) {
$sql = "SELECT *
FROM `table`
WHERE `username` = {$username}";
}
You can also call real_escape_string() directly on your $con object as follows:
$username = (isset($_POST['username']) ? $con->real_escape_string($con, $_POST['username']) : null);
However, as with #Shankar-Damodaran above, I highly suggest you do use PDO prepared statements to query your database.
Why you don't wanna use Prepared Statements ? That is really weird. I strongly suggest you should go for it.
You could make use of mysqli::real_escape_string for escaping quotes that is commonly used for SQL Injection Attacks.
Something like...
OOP Style
$message = $mysqli->real_escape_string($_POST['query_message']);
Procedural Style
$message = mysqli_real_escape_string($link,$_POST['query_message']);
other way is using:
htmlentities($query);
as an extra you could use preg_match() regular expressions to avoid
the inclusion of certain words (SELECT, DROP, UNION .......)
Example:
try{
$query = sprintf("SELECT * FROM users WHERE id=%d", mysqli_real_escape_string($id));
$query = htmlentities($query);
mysqli_query($query);
}catch(Exception $e){
echo('Sorry, this is an exceptional case');
}
There are real world cases where prepared statements are not an option.
For a simple example, a web page page where you can do a search on any number of any columns in the database table. SAy that table has 20 searchable columns. you would need a huge case statement that has all 20 single column queries, all 19+18+17+16+15+14+13+... 2 column queries, all possible 3 column queries... that's a LOT of code. much less to dynamically construct the where clause. That's what the OP means by prepared statements being less flexible.
Simply put, there is no generic case. If there was, php would have it already.
real_escape_string can be beaten. a common trick is to % code the character you are trying to escape so real_escape_string doesn't see it. then it gets passed to mysql, and decoded there. So additional sanitizing is still required. and when all characters used in injection are valid data, it's a PITA, because you can't trust real_escape_string to do it.
If you are expecting an integer, it's super easy.
$sanitized=(int)$unsanitized;
done.
If you are expecting a small text string, simply truncating the string will do the trick. does't matter that it's not sanitized if there's not enough room to hold your exploit
But there is no one size fits all generic function that can sanitize arbitrary data against sql injection yet. If you write one, expect it to get put into php. :)
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.
Currently getting more and more into MySQL. It's something i haven't been too fussed about but i want to write some scripts with it now.
My question is simple, im making a search script and just want to know if my php code can prevent some SQL injections.. the code:
$orig = $_POST['term'];
$term = mysql_real_escape_string($orig);
$sql = mysql_query("select * from db1 where content like '%$term%' ");
Is this ok? Alternatively if anyone has an easier/better/safer way of doing this plese feel inclined to let me know.
To avoid warnings in case $_POST['term'] isn't set:
if (isset($_POST['term'])) {
$term = mysql_real_escape_string($_POST['term']);
$sql = mysql_query("select * from db1 where content like '%$term%' ");
// rest of sql query
}
Yes, it is safe from SQL injection. If you want to use a more systematic method of avoiding SQL injection issues I would recommend learning to use PDO and parameterised queries.
yes it should be fine with mysql_real_escape_string
The standard escaping is often insufficient for values used in the LIKE clause. Unless you want the user to specify % placeholders of his own, you should add:
$term = mysql_real_escape_string($_POST['term']);
$term = addcslashes($term, "%_");
To be precise, this only an issue for very large tables, where excessive %%%% placeholder injection in LIKE queries could decelerate the database server.
In your case mysql_real_escape_string will prevent SQL injection because it escapse single quotes and your string is set between single quotes. So in any case $term will always be just a simple string for SQL.
If you have something like
select * from A where id = $number
then no escaping would prevent an injection like:
0; drop A;
To prevent this scenario you would go well with prepared statements (PDO) or type-checking.
$column = $_GET['id'];
$result = mysql_query("SELECT $column FROM table");
echo $result;
I'm building a website with mysql and am thus trying to learn about sql injections. I assume that this code is vulnerable, but i cant seem to make a working exploit. How would i pull column 'here' from table 'example2'?
Thanks
Imagine $_GET['id'] was equal to something like this
* FROM anytable_i_want; --
the double hypen means the rest of your string is a comment ... so now the sql you're executing is:
SELECT * FROM anytable_i_want;
The single best way to protect from this kind of nonsense is the prepared statement. If you use, say the PDO interface, you do something like this:
$HANDLE = $PDO->prepare('SELECT ? FROM mytable');
$HANDLE->execute(array($_GET['id']));
now no matter what was submitted as $_GET['id'] it woudlnt have any odd effects.
mysql_real_escape_string will cover you if using my mysql_ family of functions, although there is an exploit in the wild that you may be subject to if you change the charset at runtime.
Take a look at PDO and the use of prepared statements to help with preventing SQL injections:
http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
make $column something like :
" here FROM example2 -- "
if the following text was passed as $_GET['id'], you would have an exploit:
$_GET['id'] = '[other sql commands here]';
use either mysql_real_escape_string() or mysqli_real_escape_string() (if you are using the improved interface)