I have an application that takes data via a POST request. I am using this data to insert a new row into the database. I know that using mysql_real_escape_string() (plus removing % and _) is the way to go for strings, but what about integer values? Right now, I am using the PHP function intval() on them.
However, I wanted to make sure that intval() is perfectly safe. I can't see a way of an attacker preforming a SQL injection attack when the variables are run through intval() first (since it always returns an integer), but I wanted to make sure this is the case from people that have more experience than I.
Thanks.
Yes, intval() is safe. There is absolutely no way to perform an SQL injection when the parameter is converted to integer, because (obviously) the format of an integer does not allow putting SQL keywords (or quotes, or whatever) in it.
The easiest way to prevent SQL injection is to always use prepared statments. Use the mysqli libraries or better yet an ORM such as doctrine etc.
Your queries then become something like:
$stmt = $db->prep_stmt("select * from .... where userid = ? and username = ?");
/* Binding 2 parameters. */
$stmt->bind_param("is", $userid, $username);
$userid = 15;
$username = "don";
/* Executing the statement */
$stmt->execute( ) or die ("Could not execute statement");
I always do
$var = (int)$_POST['var'];
to make sure $var is handled as an integer in all circumstances, and never look at $_POST['var'] again. Looking at the manual, intval() does exactly the same.
Whichever way you go, in subsequence $var will be an actual integer, which is quite safe to handle.
Prepared statment is best way to deal sql injection.
or use PDO
otherwise, intval is better than is_numeric
Related
Currently I'm not using prepare or bind statement in my php code but I'm taking only int data for my search query so I just want to know where SQL injection is even possible with this query?
$enroll = mysqli_real_escape_string($conn, intval($_POST['enroll']));
$result = mysqli_query($conn, "SELECT * FROM Student_Data WHERE enroll=$enroll LIMIT 1");
I use intval() to take Int only input.
Thank you for the information.
In general, No It is not possible. But if your intension is to protect from SQL injection, why you should not use the better way which is proven? Overall, nothing can be guaranteed tomorrow. So better choice is to use the best practice what is available today.
Firstly, escaping is always context-dependent. The context mysqli_real_escape_string is designed for is inside a single-quoted string in an SQL query. If you are using the string in any other context, do not use that function. For instance, this is not secure:
$input = "a + ' + b";
$sql = "Select * From whatever Where something=" . mysqli_real_escape_string($conn, $input);
The result is this:
Select * From whatever Where something=a + '' + b
So you've gone from a syntax error to a valid, and user-controlled, query.
For that reason, applying both intval and mysqli_real_escape_string never makes sense: if you're using the value in an integer context, it's simply the wrong function, and a symptom of the dangerous mentality that "more escaping is better".
Secondly, understand what the functions you're using actually do: intval converts any PHP value, if it can, to an integer; then the . operator will always convert its operands to strings. So $anything . intval($input) is always converting $input first to an integer, and then to a string.
So, certainly we can predict exactly what characters are possible to end up in the query - digits 0 to 9, and the - sign. It's just about possible that some obscure bug in the database or driver could misinterpret those in such a way as to have unintended consequences, but it's pretty unlikely.
Thirdly, if you have the choice, just use parameters and the whole thing becomes irrelevant.
Remember that, since you no longer have the context it works with, you should not also use mysqli_real_escape_string.
I've been filtering every variables before using PDO. At that time, I usually escape strings and check its length. If the value is integer or any other numeric value, I try to figuring out the value is really desired type and value.
But after using PDO, only thing I do for security is, set the PDO::PARAM_* as binding option like following..
$stmt = $this->db->prepare("select * from $dbSessionTableName where acntid = ? and end > now()");
$stmt->bindValue(1, $_SESSION['account_id'], PDO::PARAM_INT);
$stmt->execute();
Is this really secure?
There are two answers to your question.
Speaking of binding parameters, yes, it is fully secure against SQL injection and do not require any other validation. And even setting the PDO::PARAM_* as a binding option is not necessary. You can simply make your code as follows and it will be secure as well:
$stmt = $this->db->prepare("select * from table where acntid = ? and end > now()");
$stmt->execute([$_SESSION['account_id']]);
note that all old-style "sanitization" practices related to sql injection are not only not necessary but rather harmful and should be avoided.
However, speaking of the $dbSessionTableName variable, it is still insecure as it could be with any other approach. And has to be validated.
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 have a quick question about mysql_real_escape_string. Where should I use it?
I have a *.php file with form that is redirecting it to itself, but that file is using another file that has class in it with function Add(params);
So should I escape strings when they are submitted?
$catName = mysql_real_escape_string($_POST['edtCatAddName']);
Or should I escape strings in my class?
$catName = mysql_real_escape_string($catName);
Or perhaps both these situations are wrong and I need to do something else? I've tried to escape just my query like this
$query = mysql_real_escape_string("INSERT INTO cat (catName, catDescr, catImg, catSubLevel, catSubID) VALUES ('$catName', '$catDescr', '$catImgURL', $catSubLevel, $catSubID)");
But it's not too good because this way my query won't go since catName and some other variables are string type and I need to add ' before and after them and these chars are escaped.
Any advice? I'm very new to this...
So if I use PDO then all I have to do is
$STH = $DBH->prepare("my raw, not escaped query");
$STH->execute();
and I can feel secure?
nowhere, you should use PDO prepared statements instead to protect you against SQL-injections.
When to use mysql_real_escape_string()
Actually mysql_real_escape_string() is used while sanitize a input from a user. So you should (at least) use it everywhere a user can input anything that goes into a query. It is also very suggested to use Prepared Statements.
What Prepared Statements are
Basically they are sql queries that are very safe.
Let's make an example.
SELECT UserName FROM user WHERE UserUID = X
Is a simple query. Let's say that the X is a variable that come from a $_GET input. Some users could add to X everything. Even a 1; and then start a new query. This technique is called SQL Injection.
Now with mysql_real_escape_string() you solve part of this problem, and it's quite safe. But Prepared statements tell the server that
SELECT UserName FROM user WHERE UserUID =
Is something like a static part, and then that X is a variable. In this way the server is kinda prepared to execute such a query, and nothing else, considering any input in X like an input. In this way you have not to worry about user inputs at all.
you can do:
$catName = mysql_real_escape_string($_POST['catName']);
or use mysql_real_escape_string() directly in your query.
For values which are expected to be a number (integer, float) - you can either use intval($var) for integers or floatval($var) for floats.
BUT:
never use mysql_real_escape_string() for the entire query - that's simply wrong ;-)
EDIT:
I forgot to mention: the best is to use PDO(PHP Data Objects) -> http://de.php.net/PDO
Don't. Use parameters in queries using mysqli or PDO.
<?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 !