The title sums it up pretty well. What's a reliable way of preventing the execution of functions that interact with files? We are talking about evil eval().
I have full access to the string that is about to be evaluated. I was thinking about simply removing the backstick operator, and replacing file function names (along with exec) with ones that do effectively nothing, but I'm unsure how reliable this approach is.
Example:
PHP code
eval("file_get_contents('passwords.ini')");
Currently this is replaced by, before execution:
PHP code
eval("fn_zero('passwords.ini')");
EDIT
Concerning evil eval(). What I am doing here is the execution of admin-created, database stored PHP code in a module based content-management-system. Editing the code requires an additional level of authentication that is separate from the main admin login, it is not entirely untrusted user input.
The admin can butcher his/her site if that's his/her intention, that is not my concern. My concern is the prevention of viewing the underlying PHP code of the CMS.
Addendum: each site has its own webroot and database. They can't harm each other this way.
To answer the question as asked, "sanitizing" the eval input is not possible. Simple example:
eval("\$f=strrev('stnetnoc_teg_elif');\$f('passwords.ini');");
Viable options to execute user-provided code are sandboxing or a userland VM like https://github.com/ircmaxell/PHPPHP.
Next code will help to avoid executions of functions you want to avoid:
//Just a code example
//Array of functions which should be avoid (file functions)
$patterns = [
'chgrp',
'chmod',
'chown',
'clearstatcache',
'copy',
'delete',
'fclose',
'fflush',
'file_put_contents',
'flock',
'fopen',
'fputs',
'fwrite',
'ftruncate',
'lchgrp',
'lchown',
'link',
'mkdir',
'move_uploaded_file',
'parse_ini_file',
'pclose',
'popen',
'rename',
'rmdir',
'set_file_buffer',
'symlink',
'tempnam',
'tmpfile',
'touch',
'umask',
'unlink',
];
foreach ($patterns as $key => $value) {
$patterns[$key] = '/~' . $value . '\(.*\)/U'; //~ is mask here
}
//clear of dangerous file operations
$conditions = '~' . $conditions; //set mask
$conditions = str_replace(' ', '~', $conditions); //set mask
$conditions = preg_replace($patterns, '~true', $conditions);//here replaced to 'true' but you can use here your own custom function
$conditions = str_replace('~', ' ', $conditions); //remove mask
if (!empty($conditions)) {
eval($conditions);
}
Expression like "mkdir() && is_single(77) && copy('path') && mycopy('a') && copy2('b')" will be translated to: "true && is_single(77) && true && mycopy('a') && copy2('b')"
Related
The standard way of sanitizing input would be to use commands such as
$url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%#$\|*\'()\\x80-\\xff]|i', '', $url);
$strip = array('%0d', '%0a', '%0D', '%0A');
preg_replace("/[^A-Za-z0-9 ]/", '', $string);
echo htmlentities($str);
However, I like it when my users are able to use nice things like parentheses, carats, quotes, etc in their inputs, comments/usernames/etcetc. Since HTML renders codes such as ( into symbols such as (, I was hoping to use this alternative approach to sanitizing their input.
Before I embarked on writing a function to do this for possibly harmful characters such as ( or ; or < (so injections such as sneaky eval() or <text/javascript> would not work) I tried searching up previous people's attempts at doing this type of sanitization.
I found none.
This makes me think that I must be clearly overlooking some incredibly obvious security flaw in my "creative" sanitization method.
I will not be using this function as the primary way to protect my mySQL database. I have the new mysqli class for that. Adding this sanitization overtop of the mysqli separation of input & query seems like a nice idea, though.
I am using a completely different function to clean up URLs. Those require a different approach.
This function will be used for user input to be displayed on the page, though.
So .... what could I possibly be missing? I KNOW there's gotta be something wrong with this idea since no one else uses it, right?! Is it possible to "re-render the rendered text" or something else horrific and obvious? My pretty little function so far:
Takes input strings like meep';) drop table or
alert(eval('document.body.inne' + 'rHTML'));
function santitize_data($data) {
//explode the string
//do a replacement for each character separately. Only do one replacement.
//dont do it with preg_replace because that function searches through a string in multiple passes
//and replaces already-replaced characters, resulting in horrific mishmash.
//put it back together with + signs iterating through array variables
$patterns = array();
$patterns[0] = "'";
$patterns[1] = '"';
$patterns[2] = '!';
$patterns[3] = '\\';
$patterns[4] = '#';
$patterns[5] = '%';
$patterns[6] = '&';
$patterns[7] = '$';
$patterns[8] = '(';
$patterns[9] = ')';
$patterns[10] = '/';
$patterns[11] = ':';
$patterns[12] = ';';
$patterns[13] = '|';
$patterns[14] = '<';
$patterns[15] = '>';
$patterns[16] = '{';
$patterns[17] = '}';
$replacements = array();
$replacements[0] = ''';
$replacements[1] = '"';
$replacements[2] = '!';
$replacements[3] = '\';
$replacements[4] = '#';
$replacements[5] = '%';
$replacements[6] = '&';
$replacements[7] = '$';
$replacements[8] = '(';
$replacements[9] = ')';
$replacements[10] = '/';
$replacements[11] = ':';
$replacements[12] = ';';
$replacements[13] = '|';
$replacements[14] = '<';
$replacements[15] = '>';
$replacements[16] = '{';
$replacements[17] = '}';
$split_data = str_split($data);
foreach ($split_data as &$value) {
for ($i=0; $i<17; $i++){
//testing
//echo '<br> i='.$i.' value='.$value.' patterns[i]='.$patterns[$i].' replacements[i]='.$replacements[$i].'<br>';
if ($value == $patterns[$i]) {
$value = $replacements[$i];
$i=17; } } }
unset($value); // break the reference with the last element
$data = implode($split_data);
//a bit of commented out code .. was using what seemed more logical before ... preg_replace .. but it parses the string in multiple passes ):
//$data = preg_replace($patterns, $replacements, $data);
return $data;
} //---END function definition of santitize_data
Spits out result strings like meep';) drop table or
alert(eval('document.body.inne' + 'rHTML'));
and the user sees these things rendered in the browser like like meep';) drop table and
alert(eval('document.body.inne' + 'rHTML'));
Without analyzing your code I can tell you that there is a high probability that you've overlooked something that an attacker could use to inject their own code.
The main threat here is XSS - you shouldn't need to "sanitize" to insert data into a database. You either use parameterised queries or you correctly encode characters that the database query language confers special meaning to at the point of entry into your database (e.g. ' character). XSS is normally dealt with by encoding at the point of output, however if you want to allow rich text then you need to take a different approach which is what I believe you are looking to achieve here.
Remember there is no magic function that sanitizes input in a generic manner - it very much depends on how and where it is used to determine whether it is safe or not in that context. (This bit added so if anyone searches and finds this answer then they are up to speed - I think you're already on top of this though.)
Complexity is the main enemy of security. If you cannot determine whether your code is safe or not it is too complicated and a sufficiently motivated attacker with enough time will find a way round your sanitization methods.
What can you do about this?
If you want to allow your users to enter rich text you could either allow BBCode to allow users to insert a limited, safe subset of HTML via your own conversion functions or you could allow HTML entry and run the content through a tried and tested solution such as HTML Purifier. Now, HTML Purifier won't be perfect and I'm sure that (another) flaw will be found in it at some point in the future.
How to guard against this?
If you implement a Content Security Policy on your site, this will prevent any successfully injected script code from executing in the browser. See here for current browser support for CSP. Don't be tempted to just use one of these methods - a good security model has layered security so if one control is circumvented, the other can catch it.
Google have now implemented CSP in Gmail to ensure any HTML email received cannot try anything sneaky to launch an XSS attack.
I'm trying to code "If (the query contains any common attack terms, over 80 characters etc) { execute function }"
I know preg_replace allows characters but is there a function to disallow set strings or how to build something like:
if(isset( contains['DROP, OR, 1-1, etc]) ) {
$message = $_SERVER["REMOTE_ADDR"];
$message = $_SERVER[""];
mail('admins#website.com', 'Shady Query Going on', $message);
}
I know various plugins send warnings of multiple login attempts and lockouts etc. I'm looking to do the same with SQLi attempts
UPDATE: Turns out that this is in fact a useful plugin - Better WP Security emails you when bad logins and other shady business goes on. No one answered my question either, seems like a bunch of elitist rants...
Thanks to #Rook and #Thawab tho!
I would recommend using http://phpids.org/ in your script since it can detect more attacks.
If you properly protect your site against SQL injection you absolutely don't need this. However, to implement it...
function checkForBlacklistedTerms($string)
{
$blacklisted = array('DROP', '--', 'KILL', 'BLAH', 'BLIH');
foreach($blacklisted as $bl) {
if( strpos($string, $bl) !== FALSE ) {
return true;
}
}
return false;
}
if( checkForBlacklistedTerms($searchquery) || strlen($searchquery) > 80 )
{
// warning code goes here
}
I am storing HTML layouts within a MySQL database. These layouts may contain tags within the HTML as show below..
{site.poll="fred,joe,john"}
and
{site.layout.header}
Currently i am searching the HTML template by executing multiple preg_matches to identify the tags, looping through the array then executing a str_replace(), replacing with another partial html template also pulled back from the db.. Example below..
if (preg_match_all('/{site\.layout\.(.)*}/', $data, $match) != FALSE)
{
foreach($match[0] as $value)
{
$value = trim($value, '{}');
$tmp_store = explode('.', $value);
$tmp_partial = $this->parse($this->get_layout(end($tmp_store)));
$data = str_replace('{'. $value .'}', $tmp_partial, $data);
}
}
I would need to execute a regex for each tag i required, then execute a str_replace on each instance of that tag.. The same again would need doing for each required partial template..
To me, this is all seeming to get heavy..
Is there a better way of doing this?
Thanks in advance..
Edit: I do not want to use an existing library, i would like to do this task myself and learn in the process..
Well you could use preg_replace to find and replace your tags in one shot.
The best approach in my opinion would be to use an existing template system such as Twig or Smarty. I know for sure that you can read data into Smarty (it doesn't have to be from a file). I'm sure Twig has something similar.
Twig and Smarty also provide caching options so you aren't rebuilding your template on every request. However they work best if the templates are stored in files.
If you really must roll your own template system you should build some kind of parser that actually checks the content character by character. This will likely be faster and more accurate than regular expressions (though more complex)
I don't expect this to answer your question as such, but thought it might give you something else to think about. Some code I've got for when my template class is overkill.
function replace_tags(&$xhtml, $tags) {
if( is_array($tags) && count($tags) > 0 )
foreach ($tags as $tag => $data) {
$xhtml = str_replace("{{" . $tag . "}}", $data, $xhtml);
}
if( $xhtml ) return $xhtml;
}
$tpl = "/templates/index.xhtml";
$tags = array(
"css" => null,
"js" => null,
"main_content" => null
);
$tags['main_content'] = file_get_contents("/home/main.xhtml");
echo replace_tags(file_get_contents($tpl), $tags);
edit
Thought I'd clarify on the reason the function receives $xhtml by reference, and also returns $xhtml. Basically just to make it dual purpose.
//Usage at the end of a page
echo replace_tags(file_get_contents($tpl), $tags);
//Usage for template in template
$tags['menu'] = file_get_contents($menu_tpl);
replace_tags($tags['menu'], $tags);
echo replace_tags(file_get_contents($tpl), $tags);
I'm searching for a very basic PHP templating system. Right now I'm using:
/**
* Renders a single line. Looks for {{ var }}
*
* #param string $string
* #param array $parameters
*
* #return string
*/
function renderString($string, array $parameters)
{
$replacer = function ($match) use ($parameters)
{
return isset($parameters[$match[1]]) ? $parameters[$match[1]] : $match[0];
};
return preg_replace_callback('/{{\s*(.+?)\s*}}/', $replacer, $string);
}
(from here: PHP - Extremely light templating system)
but I can only assign and display variables. I also need a way to use conditions like IF and loop arrays.
I found Rain TPL - http://www.raintpl.com/Quick-Start/#if - which is very close to what I'm looking for, but there are a few things that I don't like it it:
it allows the dude who is writing the template to run PHP functions (inside the IF condition).
it writes cache and php files, which I don't want
So, is there anything out there similar to this, but even more "basic", strict, and more secure?
Twig might be for you.
It can do conditions, and has a sandbox mode for untrusted code.
It does compilation and caching, but that seems to be possible to turn off.
There's also a Mustache port for PHP. The PHP port is here. The syntax is similar to what you're already doing, and supports simple IF and FOREACH-type loops.
And, does it without eval.
Have a look at Twig or H2O.
http://www.twig-project.org/
http://www.h2o-template.org/
From your requirements I am guessing you are wanting your website users to write some basic php scripts. You might not find a free template engine that does that.
I think it's better for you if you change an existing template engine to your needs.
You can change Rain TPL to disable some of its features that you don't want. For example you can do...
Disable function use in IF statements:
a. Locate elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
b. Replace $this->function_check( $tag ); with a new method something like $this->ifcondition_function_check( $tag );
c. Create the new method that will disable all functions in IF statements.
private function ifcondition_function_check($code)
{
$preg = '/[a-zA-z0-9]+\((.*?)\)/';
if (preg_match( $preg, $code, $match ) ){
// find the line of the error
$line = 0;
$rows=explode("\n",$this->tpl['source']);
while( !strpos($rows[$line],$code) )
$line++;
// draw the error line
$error = str_replace( array('<','>'), array( '<','>' ), array($code,$rows[$line]) );
$error = str_replace( $code, "<font color=red>$code</font>", $rows[$line] );
// debug the error and stop the execution of the script
die( "<div>RainTPL Sandbox Error in template <b>{$this->tpl['tpl_filename']}</b> at line $line : <i>$error</i></b>" );
}
}
d. Now functions are disabled.
Remove the cache file. (The cache file in Rain TPL is a PHP file with the template tags replaced by PHP code)
a. Go to method draw()
b. Locate unset( $this->tpl );
c. Just before this line remove the complied (cache) file #unlink($this->tpl['compiled_filename']);.
d. Now the cache file is just a temporary file to execute the PHP code.
Hope this helps
very easy to use
http://www.smarty.net/
When you want it really small and flexible maybe the best is to stay with your own stuff? I like handcrafting ;-) You can extend your existing function. Following, your function plus if and loop statement and escaping of variables for security:
<?php
function renderString($str, $parms)
{
// if
$str = preg_replace_callback('/{{if (?P<name>\w+)}}(?P<inner>.*?){{endif}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']])) {
// recursive
return renderString($match['inner'], $parms);
}
}, $str);
// loop
$str = preg_replace_callback('/{{loop (?P<name>\w+)}}(?P<inner>.*?){{endloop}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']]) && is_array($parms[$match['name']])) {
$str = '';
foreach ($parms[$match['name']] as $value) {
$parms['loop'] = $value;
// recursive
$str .= renderString($match['inner'], $parms);
}
return $str;
}
}, $str);
// var
$str = preg_replace_callback('/{{(?P<name>\w+)}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']])) {
return htmlspecialchars($parms[$match['name']]);
}
}, $str);
return $str;
}
$template = "<h1>{{title}}</h1>
{{if optional}}
<p>Optional: {{optional}}</p>
{{endif}}
{{if noop}}I'm not there{{endif}}
<ul>
{{loop numbers}}
<li>{{symbol}} {{loop}}</li>
{{endloop}}
</ul>";
echo renderString($template, array(
'title' => 'The Title',
'optional' => 'I am optional',
'numbers' => array( 'one', 'two', 'three'),
'symbol' => '>',
));
This script is tested in PHP 5.3 and you can copy it 1:1 to a file to play with it.
try PHPTAL: http://phptal.org/
the syntax for TAL templates does not break html, so you - and the designers can check if they going to look good.
see also:
http://wiki.zope.org/ZPT/TALSpecification14
http://wiki.zope.org/ZPT/TAL
I'm working on a project where the admin panel is just a shell that do some actions depending on what string you input.
By shell i mean an input box where you type for example
delete user 1
and the user with id 1 is deleted.
I have planned this for about 4 months and i have written all the commands that the app could manage.
I have some problem to make this system. I was thinking about this solution:
$c = explode(' ', $input);
if ($c[0] == 'delete' and $c[1] == 'user' and count($c) === 3)
{
$c[2] = $id;
delete_user_by_id($id);
}
But i think it is not that well designed and i'm sure that it could be improved.
I noticed that exists regular expression and that they could be better than this but i can't really figure out how to use them in the previous example.
Any idea?
{Notice that a part of the string is variable (delete user VARIABLE)}
Instead of a bunch of if statements, you should create a class for each command, which takes the information as an argument and does something. You just need to load the class when it's called.
$command = 'delete user 1';
$parsed = explode($command, ' ', 2);
load_class($parsed[0]); // hypothetical loader
if (class_exists($parsed[0])) {
$class = new $parsed[0]();
$class->execute($parsed[1]);
} else {
die('Sorry, invalid command');
}
I think exploding on spaces is cleaner than using a regex.
You might be able to clean up the code a bit with a switch statement, and trimming the input before you explode it:
$c explode(' ', trim($input));
switch(strtolower($c)) {
case 'delete' :
delete_user_by_id($c[2]);
break;
case 'update' :
update_user_by_id($c[2]);
break;
...
default :
echo 'Invalid command: '.$c;
}