I've been coding my website in PHP lately and I was pretty proud of myself for my good practices of sanitizing my input before I used it in a query. It was all going great until my friend said I need to sanitize my input. When I tried to explain to him that it was sanitized, he showed me that he had found everything in 'users' table in my database. I didn't know how, so I thought I would post what I was doing wrong that made my sanitizing not work. Here is the PHP code he was exploiting:
start_mysql(); // Starts the databases stuff, etc.
$id = mysql_real_escape_string($_GET['id']);
$game = mysql_query("SELECT * FROM `games` WHERE `id` = $id LIMIT 0, 1");
All he was doing was changing the id parameter, making him able to use SQL injection on my database. I thought mysql_real_escape_string escaped all characters like that, but apparently I was wrong. I did some tests with a normal string to see what would happen, and this is what it said
URL: /game.php?id=' OR '' = '
echo($_GET['id']); // This echo'd: \' OR \'\' = \'
echo(mysql_real_escape_string($_GET['id'])); // This echo'd: \\\' OR \\\'\\\' = \\\'
So, my simple question is, what am I doing wrong?
You need to put the escaped string in single quotes:
WHERE `id` = '$id'
Since id was an integer parameter and you did not surround it in single-quotes in your SQL, the value of $id is sent directly into your query. If you were expecting an integer id, then you should verify that the value of $_GET['id'] is a valid integer.
$id = intval($_GET['id']);
Matt,
mysql_real_escape_string() will only filter for certain characters, if you truly want to prevent injection attacks check out this other Stack Overflow article that suggests you use Prepared statements:
Prepared Statements
PHP Manual entry on Prepared statements
Edit: Also check out Slaks and Michael's postings about wrapping your variable in single quotes.
Good luck!
H
Cast ID. If it is a string it will cast as 0.
$id = (int)$_GET['id'];
Also, MySQL support quotes around both string and numbers in the query.
$game = mysql_query("SELECT * FROM `games` WHERE `id` = '$id' LIMIT 0, 1");
You need to use the parameter binding api. The problem is in this piece of code:
WHERE `id` = $id
You are directly interpolating user input into your SQL statement. That's the open barn door for SQL injection attacks.
You're not using parameterized queries.
MDB2 allows this, though that library may be falling out of favor.
It's very likely that your configuration has magic_quote_gpc, an ancien attempt in PHP to make scripts secure magically. It proved to have multiple flaws and was since deprecated and was scheduled to be completely removed in 5.4 the last time I heard of it.
If you have access to your php.ini configuration, you should disable it. Otherwise, you can modify your script to take it into account and sanitize your input.
All of this is documented here: http://www.php.net/manual/en/security.magicquotes.disabling.php
Otherwise, there is nothing wrong with mysqli_real_escape_string().
You can't prevent SQL injections using mysql_real_escape_string(). It is used for escaping special characters like single quotes ('), double quotes ("), etc.
To prevent SQL injections you have to use PDO statements and filter functions in PHP for sanitizing the user data.
Related
I know most people say to just use prepared statements, but I have a site with many existent queries and I need to sanitize the variables by the mysqli_real_escape_string() function method.
Also the php manual of mysqli_query() says mysqli_real_escape_string() is an acceptable alternative, so here I am ...
I want to do this type of queries:
$query = sprintf("SELECT * FROM users WHERE user_name = %s",
query_var($user_name, "text"));
$Records = mysqli_query($db, $query) or die(mysqli_error($db));
I want to know below function would work, I am unsure if:
I should still do the stripslashes() at the start ? An old function I used from Adobe Dreamweaver did this.
Is it OK to add the quotes like $the_value = "'".$the_value."'"; after the mysqli_real_escape_string() ?
Does it have any obvious / big flaws ?
I noticed the stripslashes() removes multiple \\\\\\ and replaces it with one, so that migt not work well for general use, e.g when a user submits a text comment or an item description that might contain \\\\, is it generally OK not to use stripslashes() here ?
I am mostly worried about SQL injections, it is OK if submitted data included html tags and so, I deal with that when outputing / printing data.
if(!function_exists('query_var')){
function query_var($the_value, $the_type="text"){
global $db;
// do I still need this ?
// $the_value = stripslashes($the_value);
$the_value = mysqli_real_escape_string($db, $the_value);
// do not allow dummy type of variables
if(!in_array($the_type, array('text', 'int', 'float', 'double'))){
$the_type='text';
}
if($the_type=='text'){
$the_value = "'".$the_value."'";
}
if($the_type=='int'){
$the_value = intval($the_value);
}
if($the_type == 'float' or $the_type=='double'){
$the_value = floatval($the_value);
}
return $the_value;
}
}
A text string constant in MySQL / MariaDB starts and ends with a single quote ' character. If the text itself contains a quote character, we escape it by doubling it. So the name "O'Leary" looks like this in a SQL statement's text.
SET surname = 'O''Leary'
That's the only rule. If your users feed you data with backslashes or other escaping schemes, you can feed it verbatim to MySql with the kind of text string representation mentioned here.
Don't overthink this. But use carefully debugged escaping functions. Avoid writing your own, because any tiny bug will allow SQL injection.
Looking at the PHP functions documentation, I found some references that made me decide the stripslashes() is not needed in that function.
https://www.php.net/manual/en/security.database.sql-injection.php
Generic functions like addslashes() are useful only in a very specific
environment (e.g. MySQL in a single-byte character set with disabled
NO_BACKSLASH_ESCAPES) so it is better to avoid them.
https://www.php.net/manual/en/function.addslashes.php
The addslashes() is sometimes incorrectly used to try to prevent SQL
Injection. Instead, database-specific escaping functions and/or
prepared statements should be used.
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 read this:
will help you NOT against injection.
Beause escaping is just a string formatting facility, not injection preventer by any means.
Go figure.
However, escaping have something in common with prepared statements:
Them both doesn't guarantee you from injection if
you are using it only against notorious "user input", not as a strict rule for the building ANY query, despite of data source.
in case you need to insert not data but identifier or a keyword.
On the following Post: Are dynamic mysql queries with sql escaping just as secure as prepared statements?
So my question is that using:
$Var = "UserInput Data Possible SQL Injection";
$mysqli->real_escape_string($Var);
does not provide protection against SQL Injection?
I want to use $mysqli->query(); so I can use fetch_array(MYSQLI_ASSOC); Because to be frank, I have no idea how to fetch the results as an array after using a prepared statement.
So If I have this in my Database Connection:
$STD = new mysqli('localhost', 'root', 'xx', 'xx');
$STD->set_charset('utf8');
if ($STD->connect_error) {
die("Standard Access Has Been Revoked. Please Contact Administration");
}elseif (!$STD){
die ("Other problem With Connecting To Database, Please Contact Administration");
}
as stated in the manual for real_escape_string
http://php.net/manual/en/mysqli.real-escape-string.php
The above lists:
Caution
Security: the default character set
The character set must be set either at the server level, or with the API function mysqli_set_charset() for it to affect mysqli_real_escape_string(). See the concepts section on character sets for more information.
Which links to: http://php.net/manual/en/mysqli.set-charset.php
My overall question can split into three options, the first would be asking for a fetch_array() equlivant for prepared statements, which will provide full SQL injection prevention due to prepared statements sending data as raw.
The first question in this format follows:
I'm using a Query as:
$GetCompletedQuery = $STD->query("SELECT Status FROM UserCompletion WHERE `UserID`=' ". $STD->real_escape_string($_SESSION['UID']) ."'");
$GetCompletedArray = $GetCompletedQuery->fetch_array(MYSQLI_ASSOC);
Which returns:
Array ( [Status] => 1 )
But using prepared statements:
$GetCompletedQuery = $STD->prepare("SELECT Status FROM UserCompletion WHERE `UserID`=?");
$GetCompletedQuery->bind_param('i', $_SESSION['UID']);
$GetCompletedQuery->execute();
$GetCompletedArray = $GetCompletedQuery->fetch_row;
print_r($GetCompletedArray);
Which returns:
Fatal error: Call to a member function fetch_row() on a non-object in /var/www/New/API/Constants.php on line 17
The same appears when I try fetch_array() which I know cannot be used with prepared statements.
So what would be the option for using prepared statements?
Second Question
If I use My Usual Query as:
$GetCompletedQuery = $STD->query("SELECT Status FROM UserCompletion WHERE `UserID`=' ". $STD->real_escape_string($_SESSION['UID']) ."'");
which enabled me to use fetch_array(); is data properly secured from SQL injection?
Third Question:
Should I be escaping/protecting from SQL injection for a $_SESSION['UID']; as this is assigned in the following manor:
$InnerJoinQuery = $STD->query("
SELECT Users.ID, Users.Username, Users.Password, UserInformation.LastName, UserInformation.Firstname, UserInformation.DOB
FROM Users
INNER JOIN UserInformation
ON Users.ID = UserInformation.UserID WHERE Users.Username = '".$_SESSION['real_name']."'");
$InnerJoinArray = $InnerJoinQuery->fetch_array(MYSQLI_ASSOC);
$_SESSION['UID'] = $InnerJoinArray['ID'];
$_SESSION['Password'] = $InnerJoinArray['Password'];
$_SESSION['Firstname'] = $InnerJoinArray['Firstname'];
$_SESSION['LastName'] = $InnerJoinArray['LastName'];
$_SESSION['DOB'] = $InnerJoinArray['DOB'];
This snippet explained:
User Logs in with username & password, the file gets information from the database based on $_SESSION['real_name'];
and adds to the $_SESSION array with the results, adding each into a different key.
The question for this chunk is should I even be escaping/protecting from SQL injection when the $_SESSION['UID']; is assigned through the database based on $_SESSION['real_name'];
Thankyou for your time for reading over this massive chunk.
http://php.net/manual/en/mysqli-stmt.get-result.php
Yes, but it is very bad practice:
it will help you in this case but only in this case and deceive with anything else
manual escaping is just silly, better let driver to do it for you
YES, because there is no such thing like SQL injection but improper formatting ONLY
is that using $mysqli->real_escape_string($Var); does not provide protection against SQL Injection?
I didn't change my mind: sure, it doesn't.
It will do only if you enclose the resulting value in quotes (and set proper encoding using mysqli_set_charset() to be strict).
Look, SQL injection not something essential, existing on it's own, but it's rather mere a consequence. A consequence of improperly formatted query.
When creating a query, you have to properly format every part of it. Not because of whatever "injection" but for the sake of it. When you're going to insert a string into query, you HAVE to put it into quotes, or you will get a syntax error. When you're going to insert a string into query, you HAVE to escape these quotes were used to delimit this string, or you will get a syntax error. And so on. It is proper formatting that should be your concern, not scaring tales about injection. And as long as you have every dynamic query part properly formatted according to it's type - no injection ever could be possible
So, the source of variable or it's value should never be your concern. But only it's place in the query:
strings have to be enclosed in quotes and have these quotes escaped.
numbers have to be cast to it's type.
identifiers have to be enclosed in backticks and have these backticks doubled
When it's going for the static part of the query, hardcoded in the script, we don't use such strict standards - say, we're not enclosing every identifier in backticks.
But when it's going for the dynamical part of the query, applying formatting rules should be strict rule, as we cannot know variable content for sure.
By the way, there is another way to format your strings and numbers - prepared statements. It is not as convenient as it should be, but because it is using placeholders to represent your data in the query, it it recommended to use over silly manual formatting.
So using %27 you can just SQL inject even though data is sanitized with mysql_real_escape_string
%27) SQL INJECTION HERE %2F*
What to do?
Edit with example:
$sql = sprintf("SELECT *, MATCH(post) AGAINST ('%s*' IN BOOLEAN MODE) AS score FROM Posts WHERE MATCH(post) AGAINST('%s*' IN BOOLEAN MODE)",
mysql_real_escape_string($_GET['searchterm']),
mysql_real_escape_string($_GET['searchterm']));
$results = $db->queryAsArray($sql);
If you pass in %27) SQL INJECTION HERE %2F* to the searchterm querystring, I get outputted on the page:
You have an error in your SQL syntax;
check the manual that corresponds to
your MySQL server version for the
right syntax to use near 'BOOLEAN
MODE)' at line 1
Thanks everyone for finding the problem in the db class..
Reasoning from the method name queryAsArray, it seems that you’re using this DbBase class from the comments of the MySQL functions manual page. If so, it’s the query method that removes the escape character from the escaped quotation marks:
function query($sql, &$records = null){
$sql = str_replace(array('\\"', "\\'"), array('"', "'"), $sql);
// …
}
Then it’s not a miracle that your example works (I simplified it):
$input = "', BAD SQL INJECTION --";
$sql = "SELECT '".mysql_real_escape_string($input)."'";
var_dump($sql); // string(33) "SELECT '\', BAD SQL INJECTION --'"
// everything’s OK ↑
$sql = str_replace(array('\\"', "\\'"), array('"', "'"), $sql);
var_dump($sql); // string(32) "SELECT '', BAD SQL INJECTION --'"
// Oops! ↑
The note mentioned in our manual has been marked for deletion. Once it propagates across all of the mirrors in our network, it will no longer appear attached to the official documentation.
~ Daniel P. Brown
Network Infrastructure Manager
http://php.net/
It's best to not to build statements like this at all, and instead use queries with parameters using mysqli or PDO. This will deal with the problem of MySQL injection and one day (not yet, unfortunately) it will perform better too, because the queries are cached without parameters, meaning you only got one query in the cache instead of dozens of different queries because of a single input value changing all the time. Other databases make use of this since long, but MySQL just managed not to make parameterized queries slower since the latest version.
It doesn't look plausible that %27 will actually terminate the string. It seems more like a possibility to embed quotes inside a string, but I'm not sure.
To be sure, I decided to sacrificed my server and test this. When I enter %27 in an input field and textarea that are escaped using mysql_real_escape_string and are then inserted in the database, I get no errors. The text %27 is just inserted. So no problem at all.
You are wrong. No injection possible here.
By following these three simple rules
Client's encoding properly set by mysql_set_charset()
Data being escaped using mysql_real_escape_string()
And enclosed in quotes
you can be sure that no injection possible
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.