Is the code below better at preventing a SQL injection on a MySQL database than mysqli_real_escape_string would be?
$str = "SELECT * FROM customers WHERE username = '; DELETE FROM customers WHERE 1 or username = '";
$str2 = "";
for($i = 0; $i < strlen($str); $i++)
{
if (strpos ("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ,.?!0123456789", $str[$i], 0) !== FALSE)
{
$str2 = $str2 . $str[$i];
}
}
echo "$str2";
There is absolutely no reason to use a whitelist/blacklist when trying to avoid SQL injection. All you need to do when using the mysqli_ functions without prepared statements is to process data with mysqli_real_escape_string().
However, you'd be better off learning about prepared statements instead. They are cleaner and safer than escaping.
The code to execute your example query with prepared statements would look like this:
$stmt = $conn->prepare('SELECT * FROM customers WHERE username = ?');
$stmt->bind_param('s', $username);
$result = $stmt->execute();
Since you don't understand the example, here's a detailed explanation:
Line 1 prepares the statement. You use ? whenever you want to use external data.
Line 2 binds a string (s) parameter for the first placeholder. So $username should contain the (untrusted) value
Line 3 executes the statement with the previously bound parameter. SQL and data is transferred separately so there is no SQL injection risk.
Is the code below better at preventing a mysql injection than mysqli_real_escape_string or does it not matter?
Neither. It is worse and you should not do it! Instead of escaping(!) data(!!) you are breaking your whole query.
In short, it will
make valid queries not work
not prevent malicious queries
Even if you apply your method just on the data, not on the whole query, it will
mess with your data
not prevent all kinds of SQL injection
Read this article. On the bottom, where users comments are, you will find a lot of useful SQL injection prevention tricks and hints.
The first step, however, is to validate and sanitize yout GET/POST data before inserting it into a query.
Related
class _display
{
private function threads($id){
$this->dbh->prepare("select threads where id = :id");
$this->dbh->execute(array(':id' => $id));
$row = $this->dbh->fetch();
}
}
$id = $_GET['id'];
Do I need to do anything to $id?
TL;DR: No, parameters in prepared statements do not need to be escaped.
The whole issue of escaping SQL queries came about because the ancient mysql_* library was only passing in the whole query as a string, without a way of specifying "this is syntax" and "this is data" - that was implicit from the syntax, and it was the responsibility of the caller to pass in a valid statement; that also allowed for malformed/malicious data to be treated as syntax, resulting in SQL injections etc.
Prepared statements are taking a different approach: you are sending the query with placeholders, and you pass in the data separately. Because of this, it is not needed to escape the data, as it's already separated from the syntax. (Of course, prepared statements are not a silver bullet, but using them effectively closes one major class of vulnerabilities)
You can bind the value of $id
$get=$this->dbh->prepare("select threads where id = ?");
$get->bindValue(1,$id,PDO::PARAM_INT);
$data = $get->execute();
$data=$get->fetch(PDO::FETCH_ASSOC);
This will reduce SQL injection chance as we bind id by integer and this is best practice .
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. :)
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)
<?php
$id = intval($_GET['id']);
$sql = mysql_query("SELECT username FROM users WHERE id = $id");
$row = mysql_fetch_assoc($sql);
$user = htmlspecialchars($row['username']);
?>
<h1>User:<?php echo $user ?></h1>
Can you see any threats in the above code? Do I have to use htmlspecialchars on everything I output? And should i use is_numeric or intval to check so that the get is numeric?
I'm just building a minimal site. I'm just wondering if the above code is vulnerable to sql injection, xss?
Generally speaking mysql_real_escape_string() is preferred but since it's a number, intval() is OK. So yes, it looks OK from a security perspective.
One thing though, on many platforms, ints are limited to 32 bits so if you want to deal in numbers larger than ~2.1 billion then it won't work. Well, it won't work how you expect anyway.
These sorts of security precautions apply to any form of user input including cookies (something many people forget).
I would strongly recommend using PDO and prepared statements. While your statement above looks safe, you're going to have problems as soon as you do more complex queries.
Instead of puzzling over whether a particular query is safe, learn about prepared statements and you won't have to worry. Here is your example, re-written with PDO:
# Make a database connection
$db = new PDO('mysql:dbname=your_db;host=your_db_server', 'username',
'password');
# The placeholder (:id) will be replaced with the actual value
$sql = 'SELECT username FROM users WHERE id=:id';
# Prepare the statement
$stmt = $db->prepare($sql);
# Now replace the placeholder (:id) with the actual value. This
# is called "binding" the value. Note that you don't have to
# convert it or escape it when you do it this way.
$stmt->bindValue(':id', $id);
# Run the query
$stmt->execute();
# Get the results
$row = $stmt->fetch();
# Clean up
$stmt->closeCursor();
# Do your stuff
$user = htmlspecialchars($row['username']);
I've added a lot of comments; it's not as much code as it looks like. When you use bindValue, you never have to worry about SQL injection.
Well,
You are casting the received id to an int ; so no possible SQL injection here.
And the rest of the DB query is "hard-coded", so no problem there either.
If id was a string in DB, you'd have to use mysql_real_escape_string, but for an integer, intval is the right tool :-)
About the output, you are escaping data too (and, as you are outputting HTML, htmlspecialchars is OK) ; so no HTML/JS injection.
So, this short portion of code looks OK to me :-)
As a sidenote, if you are starting developping a new website, it is the moment or never to take a look at either mysqli (instead of mysql), and/or PDO ;-)
It would allow you to use functionnalities provided by recent versions of MySQL, like prepared statements, for instance -- which are a good way to protect yourself from SQL injection !