I am keeping record of every request made to my website. I am very aware of the security measurements that need to be taken before executing any MySQL query that contains data coming from query strings. I clean it as much as possible from injections and so far all tests have been successful using:
htmlspecialchars, strip_tags, mysqli_real_escape_string.
But on the logs of pages visited I find query strings of failed hack attempts that contain a lot of php code:
?1=%40ini_set%28"display_errors"%2C"0"%29%3B%40set_time_limit%280%29%3B%40set_magic_quotes_runtime%280%29%3Becho%20%27->%7C%27%3Bfile_put_contents%28%24_SERVER%5B%27DOCUMENT_ROOT%27%5D.%27/webconfig.txt.php%27%2Cbase64_decode%28%27PD9waHAgZXZhb
In the previous example we can see:
display_errors, set_time_limit, set_magic_quotes_runtime, file_put_contents
Another example:
/?s=/index/%5Cthink%5Capp/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=ctlpy.php&vars[1][]=<?php #assert($_REQUEST["ysy"]);?>ysydjsjxbei37$
This one is worst, there is even some <?php and $_REQUEST["ysy"] stuff in there. Although I am able to sanitize it, strip tags and encode < or > when I decode the string I can see the type of requests that are being sent.
Is there any way to detect a string that contains php code like:
filter_var($var, FILTER_SANITIZE_PHP);
FYI: This is not a real function, I am trying to give an idea of what I am looking for.
or some sort of function:
function findCode($var){
return ($var contains PHP) ? true : false
}
Again, not real
No need to sanitize, that has been taken care of, just to detect PHP code in a string. I need this because I want to detect them and save them in other logs.
NOTE: NEVER EXECUTE OR EVAL CODE COMING FROM QUERY STRINGS
After reading lots of comments #KIKO Software came up with an ingenious idea by using PHP tokenizer, but it ended up being extremely difficult because the string that is to be analyzed needed to have almost prefect syntax or it would fail.
So the best solution that I came up with is a simple function that tries to find commonly used PHP statements, In my case, especially on query strings with code injection. Another advantage of this solution is that we can modify and add to the list as many PHP statements as we want. Keep in mind that making the list bigger will considerably slow down your script. this functions uses strpos instead of preg_match (regex ) as its proven to perform faster.
This will not find 100% PHP code inside a string, but you can customize it to find as much as is required, never include terms that could be used in regular English, like 'echo' or 'if'
function findInStr($string, $findarray){
$found=false;
for($i=0;$i<sizeof($findarray);$i++){
$res=strpos($string,$findarray[$i]);
if($res !== false){
$found=true;
break;
}
}
return $found;
}
Simply use:
$search_line=array(
'file_put_contents',
'<?=',
'<?php',
'?>',
'eval(',
'$_REQUEST',
'$_POST',
'$_GET',
'$_SESSION',
'$_SERVER',
'exec(',
'shell_exec(',
'invokefunction',
'call_user_func_array',
'display_errors',
'ini_set',
'set_time_limit',
'set_magic_quotes_runtime',
'DOCUMENT_ROOT',
'include(',
'include_once(',
'require(',
'require_once(',
'base64_decode',
'file_get_contents',
'sizeof',
'array('
);
if(findInStr("this has some <?php echo 'PHP CODE' ?>",$search_line)){
echo "PHP found";
}
Related
I have following code:
<?php
$param = $_GET['param'];
echo $param;
?>
when I use it like:
mysite.com/test.php?param=2+2
or
mysite.com/test.php?param="2+2"
it prints
2 2
not
4
I tried also eval - neither worked
+ is encoded as a space in query strings. To have an actual addition sign in your string, you should use %2B.
However, it should be noted this will not perform the actual addition. I do not believe it is possible to perform actual addition inside the query string.
Now. I would like to stress to avoid using eval as if it's your answer, you're asking the wrong question. It's a very dangerous piece of work. It can create more problems than it's worth, as per the manual specifications on this function:
The eval() language construct is very dangerous because it allows
execution of arbitrary PHP code. Its use thus is discouraged. If you
have carefully verified that there is no other option than to use this
construct, pay special attention not to pass any user provided data
into it without properly validating it beforehand.
So, everything that you wish to pass into eval should be screened against a very.. Very strict criteria, stripping out other function calls and other possible malicious calls & ensure that 100% that what you are passing into eval is exactly as you need it. No more, no less.
A very basic scenario for your problem would be:
if (!isset($_GET['Param'])){
$Append = urlencode("2+2");
header("Location: index.php?Param=".$Append);
}
$Code_To_Eval = '$Result = '.$_GET['Param'].';';
eval($Code_To_Eval);
echo $Result;
The first lines 1 through to 4 are only showing how to correctly pass a character such a plus symbol, the other lines of code are working with the data string. & as #andreiP stated:
Unless I'm not mistaking the "+" is used for URL encoding, so it would
be translated to a %, which further translates to a white space.
That's why you're getting 2 2
This is correct. It explains why you are getting your current output & please note using:
echo urldecode($_GET['Param']);
after encoding it will bring you back to your original output to which you want to avoid.
I would highly suggest looking into an alternative before using what i've posted
So I'm busy building a site and trying to test out a sort of filter for certain words but trying to determine which is the best function to use and through what language. I've done a bit of research and in PHP I can use the strpos() function so for example:-
if (strpos($checkstring, 'geordie') !== false) {
$checkstring = 'invalid name';
}
I'm unsure as to whether there is a decent JQuery function that could be used to achieve the same thing. Basically I want to be able to block my friends from using my name or nickname so it would include any and all variations of 'geordie' including lowercase and uppercase as well as getting past it using 'GeoRdie' or something to that affect but also want to stop variations which would be my full nickname 'geordie dave' or 'geordie ****' or even 'geordie dave is a ****'.
I realise that this is probably a bit of a complicated one but there must be a way using perhaps an array of names?
Any help on a function to use would be great and if anyone could possibly give me an example of code that could be used would also be beneficial.
You should probably do it in javascript and in php (client side and server side). The javascript eqivalent of strpos is indexOf. If you only check with javscript, someone could forge a post packet and it would still be accepted by the server. If you are only going to check in one place, make it server side, but for user-friendly-ness, both is preferred.
I think that you should also use PHP strtolower function on $checkstring variable.
In JavaScript, you can use String#indexOf(String) to match exact strings, or RegExp#test(String) for more complicated matching.
if (str.indexOf("geordie") !== -1) {
// `str` contains "geordie" *exactly* (doesn't catch "Geordie" or similar)
}
if (/geordie/i.test(str)) {
// `str` contains "geordie", case-insensitive
}
And I'll second what Alfie said: You can't just do this on the client, because client requests can be spoofed. Client-side validation is purely for making a nice user experience; server-side validation is always required.
I'm trying to find the best way to sanitize requests in PHP.
From what I've read I learned that GET variables should be sanitized only when they're being displayed, not at the beginning of the "request flow". Post variables (which don't come from the database) either.
I can see several problems here:
Of course I can create functions sanitizing these variables, and by calling something like Class::post('name'), or Class::get('name') everything will be safe. But what if a person who will use my code in the future will forget about it and use casual $_POST['name'] instead of my function? Can I provide, or should I provide a bit of security here?
There is never a one-size-fits-all sanitization. "Sanitization" means you manipulate a value to conform to certain properties. For example, you cast something that's supposed to be a number to a number. Or you strip <script> tags out of supposed HTML. What and how exactly to sanitize depends on what the value is supposed to be and whether you need to sanitize at all. Sanitizing HTML for whitelisted tags is really complex, for instance.
Therefore, there's no magic Class::sanitize which fits everything at once. Anybody using your code needs to think about what they're trying to do anyway. If they just blindly use $_POST values as is, they have already failed and need to turn in their programmer card.
What you always need to do is to escape based on the context. But since that depends on the context, you only do it where necessary. You don't blindly escape all all $_POST values, because you have no idea what you're escaping for. See The Great Escapism (Or: What You Need To Know To Work With Text Within Text) for more background information on the whole topic.
The variables are basically "sanitized" when PHP reads them. Meaning if I were to submit
"; exec("some evil command"); $blah="
Then it won't be a problem as far as PHP is concerned - you will get that literal string.
However, when passing it on from PHP to something else, it's important to make sure that "something else" won't misinterpret the string. So, if it's going into a MySQL database then you need to escape it according to MySQL rules (or use prepared statements, which will do this for you). If it's going into HTML, you need to encode < as < as a minimum. If it's going into JavaScript, then you need to JSON-encode it, and so on.
You can do something like this... Not foolproof, but it works..
foreach($_POST as $key => $val)
{
//do sanitization
$val = Class::sanitize($val);
$_POST[$key] = $val;
}
Edit: You'd want to put this as close to the header as you can get. I usually put mine in the controller so it's executed from the __construct() automagically.
Replace the $_POST array with a sanitizer object which is beheaving like an array.
I'm posting it for a clarification in a specific situation, though user input sanitization/validations is a cliche subject.
A section of the code contain
$haystack=$_GET['user'];
$input is never used for 'echo' or 'print' or in any SQL query or in any such thing. The only further use of the user input ( $haystack ) is to check if the string contains a predefined $needle.
if (preg_match($needle,$haystack)) {
$result="A";
} else {
$result="B";
}
My worry is the execution of a malicious code, rather than the presence of it in the user input.
So the question is, if the user input is used only in the context (no usage in echo,print,SQL etc) mentioned above, is there still a possibility of a malicious code in the user input get executed.
I wanted to add the security measures that is just required for the context than overdoing it.
If used only in the context, there's no way to execute malicious code from the user input.
You should be careful with eval, preg_replace (with modifier e, thanks Pelshoff), database queries and echo (& print, sprintf…).
Its not possible to just execute arbitrary code by being able to alter a string. Only when you output the string directly, or use it in SQL should you be really worried.
preg_match won't end up executing your input. It's too simple and straightforward to have a hidden exploitable bug. If you toss $haystack after running preg_match on it, then it can't possibly hurt you.
While the $haystack may not be reflected, it can obviously affect program flow. The (extremely short) code you posted certainly doesn't look directly vulnerable, but not sanitizing your input may enable code execution in conjunction with other vulnerabilities.
I have a primarily Ajax-driven site, the content of which is populated by making requests to an "operator" PHP script.
While typically these requests originate in Javascript, there are occasions when it's useful to query my operator from within another PHP script.
The method I have been using is to pass a URL with query string through file_get_contents() — and then to parse the returned JSON with json_decode().
For multiple reasons, I'd like to avoid this implementation, though... I see in my error logs that the URL requests are a lot more susceptible to failure for whatever reason — and I've read that it's not very efficient.
My 1st attempt to make a generic query_operator($query_string)-type function simply require()-ed operator.php within an output buffer, captured with ob_get_contents(). I also temporarily reset the $_REQUEST array with parameters parsed from the $query_string.
This approach had too many shortcomings — problems with variable scope and the MySQL connection, specifically.
My 2nd attempt involved using the backtick operator (equivalent to shell_exec()), and mapping the $argv arguments to the $_REQUEST array.
This approach actually works very well, but on the host I'm using, the PHP (cli) version is set a 4.4.8 — and I need 5.2.x. Assuming I can't switch the (cli) version, what's the next best way to sandbox a request to another PHP script, with a query string? Any suggestions greatly appreciated.
Here's what my 2nd attempt looks like:
function query_operator($query) {
$query = '--'.str_ireplace('&', ' --', $query);
$contents = `php operator.php $query`;
if ($json = json_decode($contents, true)) {
return $json;
} else {
return $contents;
}
}
The best thing to do, in the long run, is to factor your code.
Whatever logic operator.php is doing that is needed should live in some library, which can then be used by operator.php and any other script that needs it.
When you do that, you'll avoid all the overhead of an extra PHP process, communication between two processes, and probably all your json-encoding/decoding.
If factoring is too much work to take on now, either of the strategies you describe should work as a kludge. It might be worth looking into why your make-a-loopback-http-request method (the first thing you described) caused errors. It really ought to work well, even if it's inefficient.