I have a mysql query which requires that parameters be enclosed in either "" or '',
if I have an array passed to this function:
function orderbyfield($column, array $selection)
{
// will it be alright (secure) to do this?
foreach ($selection as $s)
{
$s = '"' . $s . '"';
}
$string = implode(',', $selection)
return array($column, $string);
}
and pass it to
function generate_sql()
{
$fields = $this->orderbyfield(); // assuming the code is in a class
$sql = 'SELECT FIELDS FROM TABLE ORDER BY FIELD (' . $fields[0] . ',' . mysql_real_escape_string($fields[1]));
}
will there be any security issues with this approach?
EDIT
assume that code is in a class, made necessary addition of $this->
EDIT
typo on the foreach
As others have said you should be using mysql_real_escape_string at the point where you create the query string. Also, although the database may be able to cast between types, not all the variables need to be quoted in queries:
function enclose($val, $dbh)
{
if (($val==='') || (is_null($val))) {
return 'NULL';
}
// is it a number?
if (preg_match('/^[\+-]*\d+\.?\d*$/', $val)) {
return($val);
}
// its a string
return("'" . mysql_real_escape_string($val, $dbh) . "'");
}
The null handling might need to be tweaked. (the above is cut down from a generic interface I use which also reads the structure of the table using DESCRIBE to get hints on when to quote/use nulls etc)
C.
Because you are using the mysql_real_escape_string function, it is pretty safe as far as strings are concerned. See dealing with sql injection for more info.
You should add quotes arround your string, but there quotes inside your strings themselves should also be escaped -- this can be done using mysql_real_escape_string, mysqli_real_escape_string, or PDO::quote, depending on the kind of functions/methods you are using to connect to your database.
Doing this (As you are already doing -- which is nice) should prevent SQL injections (at least for string : you should also check that numerics are indeed corresponding to numerical data, for instance)
Another solution, maybe a bit easier once you get it, would be to use Prepared statements.
See :
PDO::prepare
or mysqli_prepare
(Those can't be used with the old mysql_* functions)
If you're using PDO's prepared statements, you do not have to worry about the escaping yourself. No quotes, no backslashes, no nothing.
Related
I was using php 5.2 earlier. Now I want to upgrade php 5.4. Magic quotes are removed now. I want to make my application work properly. Which function I should use for escaping data mysql_real_escape_string() or addslashes() ?
Which function from the above will give the same results as of magic_quotes_gpc setting??
It's always best to migrate to PDO and prepared statements as outlined by #alex above.
If that isn't feasible, absolutely escape incoming string data with mysql_real_escape_string(), and validate integer data, e.g. using filter_input() as shown in this answer.
addslashes() is not a suitable escaping method for mySQL queries.
Its better to use prepared statements as suggested here for security reasons. Mysql_real_escape_string might not be suffiecient to prevent sql injection e.g. because multibyte character sets can be abused despite the escape function ().mysql_real_escape_string() versus Prepared Statements.
Prepared statements in PHP can be used like this:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
More information on prepared statements in PHP. So in conclusion, if you have the possibility to change your application to prepared statements, that would be the best way to handle.
UPDATE (totally not recommended)
If you really want to keep the state, use addslashes() for every $GET and $POST variable. It does the same manually what magic_quotes switched on did with all $GET and $POST variables. But i really guess its less work to use mysqli with mysqli_real_escape_string or better, prepared statements :)
http://php.net/manual/de/function.addslashes.php
Because I can not introduce db layer on my application and I want a quick solution, I used addslashes() function because addslashes() escapes single quote ('), double quote ("), backslash () and NUL (the NULL byte) exactly what magic quotes escape.
Code:
foreach (array('_COOKIE','_GET', '_POST') as $_SG) {
foreach ($$_SG as $_SGK => $_SGV) {
$$_SGK = smartQuotes($_SGV);
}
}
function smartQuotes($value)
{
if( is_array($value) ) {
return array_map("smartQuotes", $value);
} else {
if( $value == '' ) {
$value = 'NULL';
} if( !is_numeric($value)) {
$value = addslashes($value);
}
return $value;
}
}
addslashes() gives the same results as of magic_quotes_gpc setting referring from Magic Quotes.
When on, all ' (single-quote), " (double quote), \ (backslash) and NULL characters are escaped with a backslash automatically. This is identical to what addslashes() does.
Use magic_quotes_gpc on PHP 5.4 above
If you still want run magic_quotes_gpc on PHP 5.4 or higher version for your legacy code, you can use yidas/magic-quotes:
https://github.com/yidas/php-magic-quotes
We need to addslashes in Request, Post, Get & cookie. You can achieve it below code. Included below code in your common file .
$la_magicQuotes = array('_REQUEST','_POST', '_GET','_COOKIE');
foreach($la_magicQuotes as $la_superGlobal )
{
if($$la_superGlobal && is_array($$la_superGlobal))
array_walk($$la_superGlobal, 'pr_addslashed_array');
}
function pr_addslashed_array(&$la_val,$lc_key)
{
if (is_array($la_val))
array_walk($la_val,'pr_addslashed_array');
else
$la_val = pr_addslashed($la_val);
}
function pr_addslashed($lc_string)
{
return $lc_string = addslashes($lc_string);
}
The string $title is expected only to be lowercase alpha-numeric or the symbol "-" .
In this situation which of the following methods is efficient defense against security vulnerability?
$title=$_GET["title"];
$title = strtolower(preg_replace("/[^a-z0-9\-]+/i", "-", $title));
< mysql query using $title goes here >
OR
$title=$_GET["title"];
$title = mysql_real_escape_string($title);
< mysql query using $title goes here >
You should do both.
$title = strtolower(preg_replace("/[^a-z0-9\-]+/i", "-", $title));
And then $title = mysql_real_escape_string($title);
It's always a good practice to escape your mysql values, in this case it's not useful to escape it but if in the future the rule for the title changes and you can put any character, maybe you won't remember to change it
Never use your own escaping methods to escape SQL queries; the database servers will do much better job with it.
To answer the question: regexps are generally really slow. I suppose the function call will be much faster.
Also, never rely to input by users.
I would use the mysql_real_escape_string, this will remove most of the mysql injection problems. If the title does not exist (because, for example, somebody is trying to do mysql injection) then no rows will be found and thus, you display a 404 error
A combination of both. You have to remember your regular expression may not be extensive.
You do not sanitize input!
You validate input (i.e. accept it or decline it) and sanitize output (i.e. change it's representation) using a method appropriate to the destination of the data.
Hence:
if (preg_match("/^[a-z0-9\-]+$/i", $_GET['title'])) {
$my_title=mysql_real_escape_string(strtolower($_GET['title']));
$sql=....'$my_title'....
} else {
print "Invalid value for title";
exit;
}
I'm no PHP/SQL expert, and I've juste discovered that i had to apply mysql_real_escape_string to secure my SQL INSERTS.
I made a function using several advice found on the net, here it is:
function secure($string)
{
if(is_numeric($string))
{ $string = intval($string); }
elseif (is_array($string))
{
foreach ($string as $key => $value) {
$string[$key] = secure($value);
}
}
else if ($string === null)
{
$string = 'NULL';
}
elseif (is_bool($string))
{
$string = $string ? 1 : 0;
}
else
{
if (get_magic_quotes_gpc()) { $value = stripslashes($string); }
$string = mysql_real_escape_string($string);
$string = addcslashes($string, '%_');
}
return $string;
}
Thing is, when I have a look at my tables content, it contains backslashes.
And then logically, when I retrieve data I have to apply stripslashes to it to remove these backslashes.
Magic Quotes are off.
QUESTION 1)
Now I think that even though I use mysql_real_escape_string to secure my data before SQL insertion, backslashes should not appear in my content ? Can you confirm this ?
QUESTION 2)
If not normal, why are these backslashes appearing in my phpMyAdmin content and retrievals ? What did I did wrong ?
QUESTION 3)
A guess I have is that mysql_real_escape_string could be applied twice, isn't it ?
If so, what could be a function to prevent mysql_real_escape_string being applied many times to a same string, leading to many \\ to a same escapable character ?
Thanks a lot by advance for your inputs guys !
oh, what a senseless function. I know it's not your fault but ones who wrote it in their stupid articles and answers.
Get rid of it and use only mysql_real_escape_string to escape strings.
you have mixed up everything.
first, no magic quotes stuff should be present in the database escaping function.
if you want to get rid of magic quotes, do it centralized, at the very top of ALL your scripts, no matter if they deal with the database or not.
most of checks in this function are useless. is_bool for example. PHP will convert it the same way, no need to write any code for this.
LIKE related escaping is TOTALLY distinct matter, and has nothing to do with safety.
is numeric check is completely useless, as it will help nothing.
Also note that escaping strings has nothing to do with security.
I's just a syntax rule - all strings should be escaped. No matter of it's origin or any other stuff. Just a strict rule: every time you place a string into query, it should be quoted and escaped. (And of course, if you only escape it but not quote, it will help nothing)
And only when we talk of the other parts of query, it comes to the SQL injection issue. To learn complete guide on this matter, refer to my earlier answer: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
Your stripslashed $string is stored to the wrong variable $value instead of $string:
if (get_magic_quotes_gpc()) { $value = stripslashes($string); }
should be
if (get_magic_quotes_gpc()) { $string = stripslashes($string); }
Are you sure you aren't calling mysql_real_escape_string more than once, each time you call it with escapable characters you will end up adding more and more slashes. You want to call it only once. Also, why are you also calling addcslashes? mysql_real_escape_string should be enough. If you call it only once, you should never have to call stripslashes on the data after retrieving it from the database.
You can't really tell if mysql_real_escape_string is applied more than once, I'd suggest going back and re-reading your code carefully, try debug printing the values just before they are inserted into the db to see if they are look 'over-slashed'.
Btw, if you are using prepared statements (e.g. via mysqli) you dont need to escape your strings, the DB engine does this for you, this could be the problem too.
Remove addslashes completely from all of your code. This is the leading cause for slashes being inserted into database.
function escape($string) {
if (get_magic_quotes_gpc()) {
$string = stripslashes($string);
}
return mysql_real_escape_string($string);
}
Always check if magic_quotes_gpc is enabled, if it is perform stripslashes and escape the data.
Escaped = "don\'t use addslashes"
When it goes into database the '\' is removed.
.
$row['NO'] = ''.$row['abc'].''
In the above statement, what is the meaning of escaping the values in that manner such as '\'. How can i learn this?
In the above statement i want to replace the php variable $row['TEST2'] with a static value 'OPEN', but i am getting a syntax error.
$row['NO'] = ''.$row['abc'].'';
Because the escaping is very confusing it might be better to use HEREDOC:
$row['NO'] =<<<EOC;
$row[abc]
EOC;
More about strings in the manual (including escaping and heredoc).
PS: Do not use $_REQUEST. Instead use $_GET or $_POST (the one that is appropriate here.)
In the above statement what is the
meaning of escaping the values in that
manner suchas '\'.How can we learn
this
Have a look at this tutorial :)
PHP and Escaping
For the error, make sure that you escape the strings correctly.
This is what I would suggest that you do instead of escaping + concatenating strings :
$html = "%s";
$row['NO'] = sprintf($html,
$row['bcd'],
$row['gfh'],
$row['test2'],
$_REQUEST['test1'],
$row['abc']
);
And then replace whatever you need to replace... Read the manual about sprintf for more details.
Also, if any argument after $html contain ' chars, you must call addslashes on those arguments.
** UPDATE **
About $_REQUEST, read here why it is not recommended to use it.
I have one "go" script that fetches any other script requested and this is what I wrote to sanitize user input:
foreach ($_REQUEST as $key => $value){
if (get_magic_quotes_gpc())
$_REQUEST[$key] = mysql_real_escape_string(stripslashes($value));
else
$_REQUEST[$key] = mysql_real_escape_string($value);
}
I haven't seen anyone else use this approach. Is there any reason not to?
EDIT - amended for to work for arrays:
function mysql_escape($thing) {
if (is_array($thing)) {
$escaped = array();
foreach ($thing as $key => $value) {
$escaped[$key] = mysql_escape($value);
}
return $escaped;
}
// else
if (get_magic_quotes_gpc()) $thing = stripslashes($thing);
return mysql_real_escape_string($thing);
}
foreach ($_REQUEST as $key => $value){
$_REQUEST[$key] = mysql_escape($value);
}
I find it much better to escape the data at the time it is used, not on the way in. You might want to use that data in JSON, XML, Shell, MySQL, Curl, or HTML and each will have it's own way of escaping the data.
Lets have a quick review of WHY escaping is needed in different contexts:
If you are in a quote delimited string, you need to be able to escape the quotes.
If you are in xml, then you need to separate "content" from "markup"
If you are in SQL, you need to separate "commands" from "data"
If you are on the command line, you need to separate "commands" from "data"
This is a really basic aspect of computing in general. Because the syntax that delimits data can occur IN THE DATA, there needs to be a way to differentiate the DATA from the SYNTAX, hence, escaping.
In web programming, the common escaping cases are:
1. Outputting text into HTML
2. Outputting data into HTML attributes
3. Outputting HTML into HTML
4. Inserting data into Javascript
5. Inserting data into SQL
6. Inserting data into a shell command
Each one has a different security implications if handled incorrectly. THIS IS REALLY IMPORTANT! Let's review this in the context of PHP:
Text into HTML:
htmlspecialchars(...)
Data into HTML attributes
htmlspecialchars(..., ENT_QUOTES)
HTML into HTML
Use a library such as HTMLPurifier to ENSURE that only valid tags are present.
Data into Javascript
I prefer json_encode. If you are placing it in an attribute, you still need to use #2, such as
Inserting data into SQL
Each driver has an escape() function of some sort. It is best. If you are running in a normal latin1 character set, addslashes(...) is suitable. Don't forget the quotes AROUND the addslashes() call:
"INSERT INTO table1 SET field1 = '" . addslashes($data) . "'"
Data on the command line
escapeshellarg() and escapeshellcmd() -- read the manual
--
Take these to heart, and you will eliminate 95%* of common web security risks! (* a guess)
If you have arrays in your $_REQUEST their values won't be sanitized.
I've made and use this one:
<?php
function _clean($var){
$pattern = array("/0x27/","/%0a/","/%0A/","/%0d/","/%0D/","/0x3a/",
"/union/i","/concat/i","/delete/i","/truncate/i","/alter/i","/information_schema/i",
"/unhex/i","/load_file/i","/outfile/i","/0xbf27/");
$value = addslashes(preg_replace($pattern, "", $var));
return $value;
}
if(isset($_GET)){
foreach($_GET as $k => $v){
$_GET[$k] = _clean($v);
}
}
if(isset($_POST)){
foreach($_POST as $k => $v){
$_POST[$k] = _clean($v);
}
}
?>
Your approach tries to sanitize all the request data for insertion into the database, but what if you just wanted to output it? You will have unnecessary backslashes in your output. Also, escaping is not a good strategy to protect from SQL exceptions anyway. By using parametrized queries (e.g. in PDO or in MySQLi) you "pass" the problem of escaping to the abstraction layer.
Apart from the lack of recursion into arrays and the unnecessary escaping of, say, integers, this approach encodes data for use in an SQL statement before sanitization. mysql_real_escape_string() escapes data, it doesn't sanitize it -- escaping and sanitizing aren't the same thing.
Sanitization is the task many PHP scripts have of scrutinizing input data for acceptability before using it. I think this is better done on data that hasn't been escaped. I generally don't escape data until it goes into the SQL. Those who prefer to use Prepared Statements achieve the same that way.
One more thing: If input data can include utf8 strings, it seems these ought to be validated before escaping. I often use a recursive utf8 cleaner on $_POST before sanitization.