PHP keeps escaping my form's input (adding \ behind my ') - php

So basically when I type something with an apostrophe, such as John's bike it will echo John\'s bike. The code below:
<?php
$searchname = $_POST["name"] ;
echo "$searchname";
My form uses the POST method. Is there any way to stop this?
Also to make input case insensitive how would I go about in this segment?
$searchsport = $_POST['sport'];
$sportarray = array(
"Football" => "Fb01",
"Cricket" => "ck32",
"Tennis" => "Tn43",
);
if(isset($sportarray[$searchsport])){
header("Location: ".$sportarray[$searchsport].".html");
die;
}
//what code is needed to make the if statement work? I've looked up some weird ways such as using array_change_key_case (which I clearly don't understand).

This is most likely because you have magic quotes turned on, try this:
if (get_magic_quotes_gpc())
{
$searchname = stripslashes($_POST["name"]);
echo "$searchname";
}
else
{
$searchname = $_POST["name"];
echo "$searchname";
}
In fact, you could create a function instead to do it automatically for you:
function fixIt($str)
{
if (is_array($str))
{
foreach ($str as &$value)
{
$value = fixIt($value);
}
return $str;
}
else
{
return stripslashes($str);
}
}
And then you can simply do:
$searchname = fixIt($_POST["name"]);
echo $searchname;
Note: You can also disable the ugly magic quotes from php.ini as they are problematic and rightly deprecated and out of the future versions of PHP.

There are a few ways.
Turn off magic_quotes_gpc in php.ini
magic_quotes_gpc = 0
In the beginning of the request, run stripslashes
if (get_magic_quotes_gpc() && !function_exists('FixMagicQuotesGpc')) {
function FixMagicQuotesGpc($data) {
if (is_array($data)) {
foreach ($data as &$value) {
$value = FixMagicQuotesGpc($value);
}
return $data;
} else {
return stripslashes($data);
}
}
$_GET = FixMagicQuotesGpc($_GET);
$_POST = FixMagicQuotesGpc($_POST);
$_REQUEST = FixMagicQuotesGpc($_REQUEST);
}
EDIT: Added the !function_exists part. This way, you don't need to worry if you ran it before, it'll just skip it if it's already been run (by another file, etc)

This is controlled by the magic_quotes_gpc configuration variable. It really is annoying (and deprecated!).
You should turn it off in php.ini, or ask your web host if they can do something about it.
If they can't, you can use addslashes and stripslashes to manually escape/un-escape. Beware, though - you should use something more secure than addslashes for submitting to a database. mysql_real_escape_string is a better option, or the function specific to your database:
mysqli_escape_string
sqlite_escape_string
a bigger list

I include the following script within my config file to fix magic quotes if necessary. That way I don't have to worry about the magic quotes settings of the host.
<?php
set_magic_quotes_runtime(0);
function _remove_magic_quotes(&$input) {
if(is_array($input)) {
foreach(array_keys($input) as $key) _remove_magic_quotes($input[$key]);
}
else $input = stripslashes($input);
}
if(get_magic_quotes_gpc()) {
_remove_magic_quotes($_REQUEST);
_remove_magic_quotes($_GET);
_remove_magic_quotes($_POST);
_remove_magic_quotes($_COOKIE);
}
return true;
?>

Magic Quotes... I'll be so happy when PHP 6 finally arrives and removes this monster of incompatibility.
The best solution is to turn it off in php.ini by setting
magic_quotes_gpc = Off
If you don't have access to php.ini but are using Apache, you can also disable it in an .htaccess file:
php_flag magic_quotes_gpc Off
The last ditch scenario is to disable it in your application. the PHP Manual's Disabling Magic Quotes page suggests using this:
<?php
if (get_magic_quotes_gpc()) {
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
?>

Related

Sanitizing array values before mysql inserts

I have the following code blocks in a PHP form-handler:
function filter($data) {
$data = trim(htmlentities(strip_tags($data)));
if (get_magic_quotes_gpc()) {
$data = stripslashes($data);
}
$data = mysql_real_escape_string($data);
return $data;
}
foreach($_POST as $key => $value) {
$data[$key] = filter($value);
}
I am modifying my form to now include checkbox groups:
eg:
<input type="checkbox" name="phone_prefs[]" value="prefer_home">
<input type="checkbox" name="phone_prefs[]" value="prefer_cell">
<input type="checkbox" name="phone_prefs[]" value="prefer_work">
etc.
Because of this code I now have arrays in my _POST variables rather than just strings.
Am I correct in thinking that my filter() function the will not actually sanitize arrays properly? What changes do I need to make to my filter() function to make sure the arrays for the checkboxes are sanitized completely and not an easy target for SQL injection attacks?
As for the sql injection, I would switch to PDO using a prepared statement.
You can use a simple is_array() on your values to check for an array and then loop through it. You are correct, as it is, your filter function will not handle arrays correctly.
Edit: If you use PDO and a prepared statement, you don´t need mysql_real_escape_string anymore. strip_tags, htmlentities and trim are also not needed to store the information safely in a database, they are needed when you output information to the browser (trim not of course...), although htmlspecialchars would be sufficient for that. It´s always better to prepare your information / output correctly for the medium you are outputting to at that moment.
Your function is pretty good, but if you make it recursive it'll crawl nested arrays for you
function filter(&$array) {
$clean = array();
foreach($array as $key => &$value ) {
if( is_array($value) ) {
filter($value);
} else {
$value = trim(strip_tags($value));
if (get_magic_quotes_gpc()) {
$data = stripslashes($value);
}
$data = mysql_real_escape_string($value);
}
}
}
filter($_POST); # filters $_POST and any nested arrays by reference
Edit: Leave out htmlentities(). If you need it, then use it when outputting the values - not when getting them as input.
array_walk_recursive($array,function(&$item){
$item=mysql_real_escape_string($item);
});
You're using a foreach on the $_POST which only loops once, using the Array and handling it like a string.
Try using:
foreach($_POST['phone_prefs'] as $key => $value)
EDIT:
I believe I misunderstood your question:
foreach($_POST as $key => $value)
if (is_array($value))
foreach($_POST[$key] as $key2 => $value2)
/* Setting stuff */
else /* Setting same stuff */
Instead of sanitizing the input manually, use always prepared statements with placeholders. That will transparently pass the input to the database in such a way that it does not need to be escaped and thus is not vulnerable to SQL injection. This is the best current practice.
See the following for more information: http://php.net/manual/en/pdo.prepared-statements.php
I use this on various sites I have created:
public function clean($dirty) {
if (!is_array($dirty)) {
$dirty = ereg_replace("[\'\")(;|`,<>]", "", $dirty);
$dirty = mysql_real_escape_string(trim($dirty));
$clean = stripslashes($dirty);
return $clean;
}
$clean = array();
foreach ($dirty as $p => $data) {
$data = ereg_replace("[\'\")(;|`,<>]", "", $data);
$data = mysql_real_escape_string(trim($data));
$data = stripslashes($data);
$clean[$p] = $data;
}
return $clean;
}
Using mysql_real_escape_string means a MySQL connection is required before using the function, which is not best convenient. I used to use a work around in that case :
function real_escape_string($aQuery) {
if (!is_string($aQuery)) {
return FALSE;
} else {
return strtr($aQuery, array( "\x00" => '\x00', "\n" => '\n', "\r" => '\r', '\\' => '\\\\', "'" => "\'", '"' => '\"', "\x1a" => '\x1a' ));
}
}
But definitely, the best is to use PDO prepared statement instead of mysql. You will enjoy it.

\r\n \" printing out

I have begun using ADOdb and parameterized queries (ex. $db->Execute("SELECT FROM users WHERE user_name=?;",array($get->id);)to prevent SQL injections. I have read this is suppose to protect you on the MySQL injection side of things, but obviously not XSS. While this may be the case, I'm still a bit skeptical about it.
Nevertheless, I always filter my environmental variables using shotgun approach towards safety at the beginning of my wrapper code (kernel.php). I notice the combination of using ADOdb and the following functions produces browser-visible carriage returns (\r\n \" \'), which is something I don't want (although I do want to store that information!). I also don't want to have to filter my output before display, since I already properly filter my input (aside from BBcode and that sort of thing). Below you will find the functions I'm referring to.
While in general I have isolated this problem to the mysql_real_escape_string portion of the sanitize function, do note that my server is running PHP 5.2+, and this issue does not exist when I use my own simplified db abstraction class. Also, the site is ran on mostly my own code and not built on the scaffold of some preexisting CMS). Thus, considering these factors, my only guess is there is some double-escaping going on. However, when I looked at adodb.inc.php file, I noticed $rs->FetchNextObj() doesn't utilize mysql_real_escape_string. It appears the only function that does this is qstr, which encapsulates the entire string. This leads me to worry that relying on parameterized queries may not be enough, but I don't know!
// Sanitize all possible user inputs
if(keyring_access("am")) // XSS and HTML stripping exemption for administrators editing HTML content
{
$_POST = sanitize($_POST,false,false);
$_GET = sanitize($_GET,false,false);
$_COOKIE = sanitize($_COOKIE,false,false);
$_SESSION = sanitize($_SESSION,false,false);
}
else
{
$_POST = sanitize($_POST);
$_GET = sanitize($_GET);
$_COOKIE = sanitize($_COOKIE);
$_SESSION = sanitize($_SESSION);
}
// Setup $form object shortcuts (merely convenience)
if($_POST)
{
foreach($_POST as $key => $value)
{
$form->$key = $value;
}
}
if($_GET)
{
foreach($_GET as $key => $value)
{
$get->$key = $value;
}
}
function sanitize($val, $strip = true, $xss = true, $charset = 'UTF-8')
{
if (is_array($val))
{
$output = array();
foreach ($val as $key => $data)
{
$output[$key] = sanitize($data, $strip, $xss, $charset);
}
return $output;
}
else
{
if ($xss)
{
// code by nicolaspar
$val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!##$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++)
{
$val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$found = true;
while ($found == true)
{
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++)
{
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++)
{
if ($j > 0)
{
$pattern .= '(';
$pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?';
$pattern .= '|(&#0{0,8}([9][10][13]);?)?';
$pattern .= ')?';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2);
$val = preg_replace($pattern, $replacement, $val);
if ($val_before == $val)
{
$found = false;
}
}
}
}
// Strip HTML tags
if ($strip)
{
$val = strip_tags($val);
// Encode special chars
$val = htmlentities($val, ENT_QUOTES, $charset);
}
// Cross your fingers that we don't get a MySQL injection with relying on ADOdb prepared statements alone… ? It works great otherwise by just returning $val... so it appears the code below is the culprit of the \r\n \" etc. escaping
//return $val;
if(function_exists('get_magic_quotes_gpc') or get_magic_quotes_gpc())
{
return mysql_real_escape_string(stripslashes($val));
}
else
{
return mysql_real_escape_string($val);
}
}
}
Thank you very much in advance for your help! If you need any further clarifications, please let me know.
Update the backslash is still showing up in front of " and ', and yes I removed the extra mysql_real_escape_string... now I can only think this might be get_quotes_gpc, or ADOdb adding them...
~elix
It turned out to be a side effect of qstr in ADOdb, even though I didn't reference that particular function of the class, but must be called elsewhere. The problem in my particular case was that magic quotes is enabled, so I set the default argument for the function to $magic_quotes=disabled. As for not needing any escaping with this, I found that ADOdb by itself DOES NOT utilize mysql_real_escape_string through the basic Execute() with binding alone! How I recognized this was due to the fact that the characters " ' threw errors (hence didn't render on my server where error_reporting is disabled). It appears the combination of the functions with fixing that small issue with ADOdb has me both well protected, and accepts most/all input the way I want it to: which in the case of the double quote prevented any quotes from being entered as content into the database, which meant at the very least no HTML
Nevertheless, I appreciate your suggestions, but also felt that my follow-up might help others.

Does this php code secure me from SQL injection and XSS?

I have sanitize class that I run before anything else on every page of my site. I'm pretty sure addslashes is the same as escaping with mysql_real_escape_string heres the class.
class sanatize
{
private static $singleton;
function __construct(){
$_CLEAN_POST = array();
$_CLEAN_GET = array();
$_CLEAN_REQUEST = array();
foreach($_REQUEST as $key => $value)
{
$key = addslashes(trim(strip_tags($key)));
$value = addslashes(trim(strip_tags($value)));
$_CLEAN_REQUEST[$key] = $value;
}
foreach($_GET as $key => $value)
{
$key = addslashes(trim(strip_tags($key)));
$value = addslashes(trim(strip_tags($value)));
$_CLEAN_GET[$key] = $value;
}
foreach($_POST as $key => $value)
{
if(is_array($value)){
foreach($value as $key2 => $value2){
$key2 = addslashes(trim(strip_tags($key2)));
$value2 = addslashes(trim(strip_tags($value2)));
$_CLEAN_POST[$key][$key2] = $value2;
}
}
else{
$key = addslashes(trim(strip_tags($key)));
$value = addslashes(trim(strip_tags($value)));
$_CLEAN_POST[$key] = $value;
}
}
$_POST = array();
$_GET = array();
$_REQUEST = array();
$_POST = $_CLEAN_POST;
$_GET = $_CLEAN_GET;
$_REQUEST = $_CLEAN_REQUEST;
}
function __destruct()
{
//echo "cleaned";
}
public static function getInstance()
{
if(is_null(self::$singleton))
{
self::$singleton = new sanatize();
}
return self::$singleton;
}
}
and then i'll call it using
$sanatize = sanatize::getInstance();
"I'm pretty sure addslashes is the same as escaping with mysql_real_escape_string heres the class."
First, it's not. mysql_real_escape_string is aware of the connection, and takes that connection's character set into account.
Second, you're basically replicating the failed magic_quotes design. Not all of those fields are going into the database, so you're doing unnecessary work. You also have to be careful never to re-escape something in a "clean" array; double-escaping is a very common problem.
In my opinion, the simplest solution to SQL injection is prepared statements. I recommend using either PDO or mysqli.
EDIT: Since you're already using mysqli, you should forget about this CLEAN idea, and simply use MySQLi_STMT. mysqli::prepare gives an example of how to create and bind variables to a prepared statement. Note the ? place-holder. Also look at mysqli_stmt::bind_param.

my GET variable is being escaped?

I'm really confused here, can someone explain this to me?
request:
http://example.com/test.php?var=String's
$a = $_GET["var"];
$b = "String's";
echo $a . "<br/>";
echo $b . "<br/>";
$output = mysql_real_escape_string($a);
$output = mysql_real_escape_string($b);
echo "<hr/>";
echo $a . "<br/>";
echo $b . "<br/>";
result:
String\'s
String's
----------------
String\'s
String's
Could someone explain to me not only why my GET variable is being transformed like this, but how I can remove this behavior such that my input is exactly as it was sent? I'm having an issue where my SQL wrapper passes this through mysql_real_escape_string() and ends up being String\\\'s :(
It's called "magic quotes".
You can and should disable magic quotes.
prefered mode
set them off in php.ini
.htaccess mode
add this to your htaccess file
php_flag magic_quotes_gpc off
php5 runtime mode
<?php
if (get_magic_quotes_gpc()) {
function stripslashes_gpc(&$value)
{
$value = stripslashes($value);
}
array_walk_recursive($_GET, 'stripslashes_gpc');
array_walk_recursive($_POST, 'stripslashes_gpc');
array_walk_recursive($_COOKIE, 'stripslashes_gpc');
array_walk_recursive($_REQUEST, 'stripslashes_gpc');
}
?>
php4 runtime mode
<?php
if (get_magic_quotes_gpc()) {
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
?>
Welcome to the magic_quotes hater's club! :)
You probably have magic quotes turned on. This automatically escapes GET, POST and COOKIE data. Magic quotes is bad and should not be relied upon to properly escape data.
If you have access to php.ini, you can turn magic quotes off.
If you don't, you can run stripslashes on the data to remove the slashes. In order to make your code portable, you should first check get_magic_quotes_gpc() to see if it is turned on and only then run stripslashes. In this way, if you move your code to a server that has magic quotes turned off, your code will still work.
if(get_magic_quotes_gpc()) {
$a = stripslashes($_GET["var"]);
}
else $a = $_GET["var"];

PHP - Shorter Magic Quotes Solution

I'm writing a app that needs to be portable. I know I should disable magic quotes on the PHP configuration but in this case I don't know if I can do that, so I'm using the following code:
if (get_magic_quotes_gpc() === 1)
{
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process))
{
foreach ($val as $k => $v)
{
unset($process[$key][$k]);
if (is_array($v))
{
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
}
else
{
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
To simplify the process of disabling magic quotes I had the following idea:
if (get_magic_quotes_gpc() === 1)
{
foreach (array('GET', 'POST', 'COOKIE', 'REQUEST') as $array)
{
${'_'.$array} = unserialize(stripslashes(serialize(${'_'.$array})));
}
}
But I tried and I got an error I'm unable to understand, for instance with ?name=O'Reilly:
serialize($_GET); // a:1:{s:4:"name";s:9:"O\'Reilly";}
stripslashes(serialize($_GET)); // a:1:{s:4:"name";s:9:"O'Reilly";}
But unserialize(stripslashes(serialize($_GET))) gives me this weird error:
Notice: unserialize(): Error at offset 30 of 32 bytes
EDIT: Due to the length attribute in serialize() I changed the code to use JSON functions:
if (get_magic_quotes_gpc() === 1)
{
foreach (array('GET', 'POST', 'COOKIE', 'REQUEST') as $array)
{
${'_' . $array} = json_decode(stripslashes(json_encode(${'_' . $array})), true);
}
}
However now the $_GET array is coming up empty, can anyone explain me why?
I don't think the second version will work. Serialized strings are stored along with their length, if you are removing characters, you would need to update that length value. I would rather implement it this way to improve readability:
function strip_slashes_recursive(&$value) {
if (!is_array($value)) {
$value = strip_slashes($value);
} else {
foreach (array_keys($value) as $key) {
$arrayValue = strip_slashes_recursive($value[$key]);
unset($value[$key]);
$value[strip_slashes($key)] = $arrayValue;
}
}
}
foreach (array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST) as &$array) {
strip_slashes_recursive($array);
}
// don't forget to unset references or it can lead to very nasty bugs
unset($array);
Solved it, I had to use the JSON_HEX_APOS flag in json_encode():
if (get_magic_quotes_gpc() === 1)
{
$_GET = json_decode(stripslashes(json_encode($_GET, JSON_HEX_APOS)), true);
}
Before (mqgpc.php?name[got'cha]=O'Reilly):
Array
(
[name] => Array
(
[got\'cha] => O\'Reilly
)
)
After (mqgpc.php?name[got'cha]=O'Reilly):
Array
(
[name] => Array
(
[got'cha] => O'Reilly
)
)
I usually solve that problem this way:
function smagic($params){
if(get_magic_quotes_gpc()){
if(!is_array($params))
return stripslashes($params);
else
return array_combine( array_map('stripslashes',array_keys($params)), array_map('smagic',array_values($params)) );
}
}
And then, for $_GET:
$_GET = smagic($_GET);

Categories