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.
Related
I need to sanitize the values in a JSON file (e.g., a composer.json file from github). I json_decode($file) converting it to a stdClass object. (I need it as an object, not as an array - I am aware of that option).
I need to recursively sanitize all the values which might be objects as well (and maybe the keys too?).
I need to remove any and all "dangerous" characters, etc from the file but would like it to remain multilingual, so was planning to use filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW).
Advice and suggestions please. maybe I'm missing the obvious, but this seems harder than it should.
Object can be iterated by foreach:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = sanitizeValue($value);
continue;
}
sanitize($value);
}
return $data;
}
Answer by Михаил-М was close. I needed to adjust it slightly to be:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = sanitizeValue($value);
continue;
}
$value = sanitize($value);
}
return $data;
}
of course, this doesn't address the issue of actually sanitizing the data which I did with the filter_var method I mentioned above. so I finally solved it with this:
function sanitize($data) {
foreach ($data as &$value) {
if (is_scalar($value)) {
$value = filter_var($value, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
continue;
}
$value = sanitize($value);
}
return $data;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What are the best PHP input sanitizing functions?
A while back I found this, what I thought to be great, snippet in someones code to filter POST and GET data from injections.
function filter($data) { //Filters data against security risks.
$data = trim(htmlentities(strip_tags($data)));
if(get_magic_quotes_gpc()) $data = stripslashes($data);
$data = mysql_real_escape_string($data);
return $data;
}
foreach($_GET as $key => $value) $filterGet[$key] = filter($value);
foreach($_POST as $key => $value) $filterPost[$key] = filter($value);
And I've been using it ever since. But today, while sending an array through ajax I got tons of errors. Most of them say strip_tags() expects parameter 1 to be string, array given in...
What the best way to filter data? All this data is going to a database. But what about cases where it isn't going to a database?
Here is the function you need:
function filter($data) { //Filters data against security risks.
if (is_array($data)) {
foreach ($data as $key => $element) {
$data[$key] = filter($element);
}
} else {
$data = trim(htmlentities(strip_tags($data)));
if(get_magic_quotes_gpc()) $data = stripslashes($data);
$data = mysql_real_escape_string($data);
}
return $data;
}
As clear by the error message, this is happening for cases where an array is passed via GET/POST. You can parse each value of the array for such cases.
foreach($_GET as $key => $value){
if(is_array($value)){
foreach($value as $val){
$filterGet[$key][] = filter($val);
}
}
else{
$filterGet[$key] = filter($value);
}
}
What you should do is first check to see if $data is the correct format that you need it to be in. What you describe is that an array was passed into the $data parameter of your function, and PHP needs you to break it down into a string. Some extra logic is needed such as:
function filter($data) {
if(is_array($data)) {
foreach($data as $key => $value) {
// Do stuff...
}
} else {
// Do stuff...
}
}
You should check if the input is array. If so, loop it and strip tags for every array member, if not, then just strip tags for the input.
you can use array_walk
<?php
function wsafe(&$value,$key)
{
return safe($value);
}
function safe($value)
{
if(is_array($value))
{
foreach($value as $key=>$val)
{
$value[safe($key)] = safe($val);
}
}
else
{
$value = trim(htmlentities(strip_tags($value)));
if(get_magic_quotes_gpc()) $value = stripslashes($value);
$value = mysql_real_escape_string($value);
}
}
array_walk($_POST,'wsafe');
array_walk($_GET,'wsafe');
check out the method below. If entered value in text box is \ mysql_real_escape_string will return duble backslash but preg_replace will return SQL with only one backslash. Im not that good with regular expression so plz help.
$sql = "INSERT INTO tbl SET val='?'";
$params = array('someval');
public function execute($sql, array $params){
$keys = array();
foreach ($params as $key => $value) {
$keys[] = '/[?]/';
if (get_magic_quotes_gpc()) {
$value = stripslashes($value);
}
$paramsEscaped[$key] = mysql_real_escape_string(trim($value));
}
$sql = preg_replace($keys, $paramsEscaped, $sql, 1, $count);
return $this->query($sql);
}
For me it basically looks like you're re-inventing the wheel and your concept has some serious flaws:
It assumes get_magic_quotes_gpc could be switched on. This feature is broken. You should not code against it. Instead make your application require that it is switched off.
mysql_real_escape_string needs a database link identifier to properly work. You are not providing any. This is a serious issue, you should change your concept.
You're actually not using prepared statements, but you mimic the syntax of those. This is fooling other developers who might think that it is safe to use the code while it is not. This is highly discouraged.
However let's do it, but just don't use preg_replace for the job. That's for various reasons, but especially, as the first pattern of ? results in replacing everything with the first parameter. It's inflexible to deal with the error-cases like too less or too many parameters/placeholders. And additionally imagine a string you insert contains a ? character as well. It would break it. Instead, the already processed part as well as the replacement needs to be skipped (Demo of such).
For that you need to go through, take it apart and process it:
public function execute($sql, array $params)
{
$params = array_map(array($this, 'filter_value'), $params);
$sql = $this->expand_placeholders($sql, $params);
return $this->query($sql);
}
public function filter_value($value)
{
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
$value = trim($value);
$value = mysql_real_escape_string($value);
return $value;
}
public function expand_placeholders($sql, array $params)
{
$sql = (string) $sql;
$params = array_values($params);
$offset = 0;
foreach($params as $param)
{
$place = strpos($sql, '?', $offset);
if ($place === false)
{
throw new InvalidArgumentException('Parameter / Placeholder count mismatch. Not enough placeholders for all parameters.');
}
$sql = substr_replace($sql, $param, $place, 1);
$offset = $place + strlen($param);
}
$place = strpos($sql, '?', $offset);
if ($place === false)
{
throw new InvalidArgumentException('Parameter / Placeholder count mismatch. Too many placeholders.');
}
return $sql;
}
The benefit with already existing prepared statements is, that they actually work. You should really consider to use those. For playing things like that is nice, but you need to deal with much more cases in the end and it's far easier to re-use an existing component tested by thousand of other users.
It's better to use prepared statement. See more info http://www.php.net/manual/en/pdo.prepared-statements.php
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.
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);
}
?>