[Asked this at SU; suggestion there was to repost at SO]
We have PHP programs that use $_SERVER['DOCUMENT_ROOT'] as a path to a place where we can place a private directory (using a unique name) in a safe, known place on server. This directory holds data summaries collected from multiple perhaps thousands of served PHP pages; this summary has no impact on the main functionality of the pages served, and eventually can be ignored or deleted. While it exists, a PHP-based tool can inspect the summary data and provide a report.
This has worked well on a variety of PHP servers, and appears to be "automatically" (?) set according to the PHP Manual documentation:
'DOCUMENT_ROOT'
The document root directory under which the current script
is executing, as defined in the server's configuration file.
We installed several different PHP servers over the years, and they all seem to come with this pre-configured to a reasonable path without any explicit configuration effort on our part.
We have some clients using our software that complain that this path seems to be set to null, which seems inconsistent with the above definition. (Does null signify some special path?) (One of these clients claims they have (php -v) "PHP 5.6.16-3+deb.sury.org~trusty+1 (cli) ... Zend Engine v2.6.0, ... with Zend OPcache v7.0.6-dev, ..." and (uname -or) "3.19.0-25-generic GNU/Linux" [??Generic GNU/Linux??]
Pardon my ignorance, but is DOCUMENT_ROOT a configurable item? Where do I find the server "configuration file" for this? Is it the same place for all different PHP server implementations? Can I/should I change that configuration files to set this explicitly if it is not set?
If not, is there another way to provide a path to a safe long-term "temporary" local directory?
$_SERVER['DOCUMENT_ROOT'] is found in your apache httpd.conf and it can be configured. It's the directory out of which you will serve your documents. By default, all web requests are taken from this directory so it should not be null.
This link gives you quick run-down as to why documentroot is in place: http://httpd.apache.org/docs/2.4/urlmapping.html#documentroot.
I personally don’t see an issue using $_SERVER['DOCUMENT_ROOT'] for servers which I have full control, but I would be wary using this path reference on shared hosting solution’s due to aliases in the config. In this instance, I would just set the $_SERVER['DOCUMENT_ROOT'] manually.
<?php
echo $_SERVER['DOCUMENT_ROOT'];//Real Path
$_SERVER['DOCUMENT_ROOT'] = "/new/path/www";
echo $_SERVER['DOCUMENT_ROOT'];//New Path
?>
Related
Let's say we have a web directory with the following paths:
framework/
index.php
...
instance/
index.php -> ../framework/index.php (symlink)
...
If we now make a request for (...)/instance/index.php PHP will resolve the symlinks and set __FILE__ as (...)/framework/index.php
Is there a way around this so e.g. dirname(__FILE__) would be (...)/instance?
EDIT: Since it has been pointed out, I'm actually not sure if PHP itself is resolving the links or something else. In my case I'm using nginx as a webserver with php-fpm as FastCGI.
(The background is that I want to have multiple instances of a CMS or Framework referencing a common code base.)
The best approximation for your case is dirname($_SERVER['SCRIPT_FILENAME']) to get the "instance" dir, if you can live with the general warning in the doc about the $_SERVER vars:
There is no guarantee that every web server will provide any of these;
servers may omit some, or provide others not listed here.
Also, the PHP manual does not explicitly promise that symlinks will be preserved, but that seems to be the case (unless of course your server thinks otherwise). See also this answer.
(Note: this also works in CLI mode. If the script was launched via a relative path (to the CWD), PHP will keep it that way, so you may end up with "." then.)
However, for the general problem (of getting any script's dir with symlinks preserved), this doesn't work. It only works for the script that has been reached via HTTP. Since any other scripts you include later on will all have the same $_SERVER['SCRIPT_FILENAME'] (of course), regardless of their location, dirname($_SERVER['SCRIPT_FILENAME']) will yield the wrong dir if they are located elsewhere...
I don't think PHP has a solution for that today (as of v7.2).
(And you were correct initially: PHP is doing the symlink-resolving for __FILE__, irrespective of your server. And it's unlikely that this would ever change, because it has stayed this way for too long (and too much code depends on it), even though there's realpath() if we wanted the resolved path, while there's no bullet-proof solution for the symlinked case.)
I am a PHP newbie and a have a php security question. Is it possible for somebody to get the source code of a php script file running on a server with default configuration? If so, what is the best way to be protected? I am asking this because I happened to download a php file when I requested a page from a site and what triggered my concerns. I think that maybe apache configuration was wrong and served that file to me like a simple file but I am not sure. Also what is the best place to keep "sensitive" data such as database or smtp configuration?
Thank you,
Alex
For the most sensitive information, I'd suggest putting it outside of your web root folder, and including it through "require" or "include". This way, even is some configuration gets botched on the server, the visitor will only get served the line "include('secret_stuff.php');" and not the actual script.
Exactly what David Dorward said but i would advise you take a look at the following patch(s) that would modify apache to not send source code's regards if there is a misconfiguration.
http://mirror.facebook.net/facebook/patches/ap_source_defense.patch
Patch like so:
cd apache-1.3.x
patch -p1 -i ap_source_defense.patch
More Patches from Facebook Development Team: http://mirror.facebook.net/facebook/patches/
The best way to protect your much needed source is to place them outside the public root directory, as if apache is running it will not be able to serve files directly from the folder up public_html
for example:
C:/server/apache/
C:/server/apache/htdocs/
C:/server/apache/htdocs/includes/
People can specifically view the files my going to
http://hostname.tld/includes/
but having the directory structure of:
C:/server/apache/
C:/server/apache/includes/
C:/server/apache/htdocs/
and then within
C:/server/apache/htdocs/index.php
you have
<?php
require_once('../includes/config.php');
?>
this should protect all major files bar the view file (index.php)
If the server is not configured to handle PHP files, then it will treat them like any other unknown file (and serve them as either text/plain or application/octet-stream.
PHP support is, as far as I know, always provided as an extension or external program (for CGI, FastCGI, etc) and never as a built in for an HTTP server.
If the server is properly configured to run PHP code, then people without direct access to the server cannot view the PHP source code. You don't have to do anything else.
It is only because that server was not configured to run PHP, and instead served it as text, that you could see the source.
If you have this line in your apache.httpd.conf file,
AddType application/x-httpd-php .php
Apache should deal with data, rather than showing them...
Also you need to start php services.
What you describe as "default configuration" is a webserver without php installed (or with php disabled). In these cases, it is certainly possible to download the php script.
Make sure php is installed (as it will be on ~100% of production php servers) and/or block access to your configuration file with an .htaccess file like this:
<FilesMatch "^config.php$">
Order allow,deny
Deny from all
</Files>
If you want to be extra-tricky (and work even on servers where .htaccess files are ignored), prefix the configuration file with .ht, like .ht.config.php. Most Apache(and some other webserver) configurations will refuse serving files beginning with .ht. However, in general, the only way you could be sure no webserver serves your file is to move it to a directory outside of the server's document directory. On most hosts you or your php script won't be able to access those though.
Your second problem are misconfigurations. There's not much you can do, albeit there might(?) be options to construct a rewriterule to prevent accidential accessibility.
The best prevention however is to keep all scripts outside of the DOCUMENT_ROOT. Just leave a single index.php there, and include all dependencies from there. This is also the best strategy to avoid leaking of configuration data (also don't use ini files for sensitive data, but always .php scripts).
Another worry are shared hosting servers however. All concurrent users on a server can read out your scripts (if not through PHP then via Perl/bash CGIs). Nothing you can do about that, unless you change to a professional hoster which supports running everthing through suexec and thus allowing individual permissions.
Well, "default configuration" is a vague term, but as long as the web server is configured to parse .php files through PHP, you should be fine from that angle. If your scripts themselves happen to parse other PHP files (for eg. a template system) then you need to make sure there aren't any loopholes in that too, since the PHP scripts have full access to your files on the server.
Assuming these are taken care of, you don't need to keep the "sensitive" data in any special place -- just put them in your .php files, but make sure all your scripts end in .php (for eg. config.inc.php) so that they are always parsed via PHP and not sent as plain text.
This may be a really stupid question...I started worrying last night that there might be someway to view PHP files on a server via a browser or someother means on a client machine.
My worry is, I have an include file that contains the database username and password. If there were a way to put the address of this file in to a browser or some other system and see the code itself then it would be an issue for obvious reasons.
Is this a legitimate concern?
If so how do people go about preventing this?
Not if your server is configured right. I think discussion on how that is done belongs on serverfault.
To add on to the other answers:
If you use a file extension like .inc there's indeed a higher risk. Can you open the file directly in your browser?
The most important advice is missing:
Only the files that should be accessed by a browser, should be in a publicly accessible location. All the other code (and configuration) should be in a completely separate directory.
For example
root
- webroot
- includes
- config
Only 'webroot' is exposed by your webserver (apache). Webroot can for example contain a single index.php, along with all your assets (javascript, css, images).
Any code index.php needs to load comes from 'includes' and all the configuration from 'config'. There's no way a user could ever directly access anything from those 2 directories, provided this is done correctly.
This depends on the file extension you have given the include file.
If the extension is one that is known and executed by the web server, it will be protected. If you browse to the file, the server will try to execute the code rather than just returning it as plain text.
If the extension is not known by the web server it will serve it as plain data, so anyone (who can guess the file name) can browse to the file and see the source code.
A Directory Traversal Vulnerability can used to obtain files off of the remote mahine. Alternatively you can use MySQL based sql injection to read files using load_file(). You can also test your system with w3af's urlfuzzer which will look for "backup files", such as index.php.zip. Also make sure that all files have .php extensions, a .inc can be viewed from the public. I would also disable Apache directory listing.
Normally there should be no way to view the PHP files remotely... it would be absolutely pointless. This completely depends on what web server you are using and how it's setup though.
Having looked around I can see that it is possible to protect a directory via the .htaccess by adding these lines:
Order allow,deny
Deny from all
This apparently protects the directory so that only local non web-access is possible.
This allows me to keep my includes in a subdirectory of the main site directory which is good for organisation and it can be used on the projects where I do not have access to folders outside the web root.
Does anyone else use this method?
Just for good measure I've put the directory permissions to execute only.
And the include extension is PHP as suggested by others.
In the same way that you can generate specific content based on browser type is there a way to generate specific content based on the server running PHP without reference to the server or site name?
For example, a way for PHP to automatically detect the environment it was in and configure things like DB connections, ini_set for errors etc. depending if it was a development, ITS, UAT or production environment.
The 2 ways I thought of were to recognise an HTTP header indicating development and QA environments or to have custom properties in php.ini.
I have woken up slightly and found out the php function to read the http headers but php overrides anything I set in the web server and I do not know if they can be set in php.ini at all.
I have no idea if it is possible to add custom values to php.ini but I had a test and ini_get would not find it (I had restarted the web server after changing php.ini of course).
you can specify an environment variable in apache (conf, vhost, .htaccess or as an httpd daem) and then acces it via the ˆ$_ENVˆsuperglobal
I use the following to load different settings for different servers:
switch ($_SERVER['SERVER_NAME']) {
case 'web-host': case '10.0.0.208':
# Set DB Settings
case 'mydomain.com': default:
# Live server settings
}
Not had a problem with it so far
$_ENV / http://www.php.net/manual/en/reserved.variables.environment.php
Using FastCGI on IIS you can set Environment variables. They do not seem to be available to $_ENV but can be retrieved with getenv("varname").
To configure FastCGI environment variables in IIS 5 or 6 you need to edit:
C:\%systemdrive%\system32\inetsrv\fcgiext.ini
For example:
[Types]
php=d:\Dev\PHP\php-cgi.exe
php:1=PHP Site 1
*=Wildcard Mapping
[d:\Dev\PHP\php-cgi.exe]
QueueLength=999
MaxInstances=20
InstanceMaxRequests=500
[PHP Site 1]
ExePath=d:\Dev\PHP\php-cgi.exe
EnvironmentVars=PHPRC:d:\Dev\PHP\,SiteType:Developer
In this instance it is IIS 5 so there is only one site and the site ID is 1 as indicated in line 2 of [Types].
On IIS 6 you may have multiple sites and the following link tells you how to find the Site ID: http://weblogs.asp.net/owscott/archive/2005/07/29/how-to-find-the-siteid-in-iis5-and-iis6.aspx.
IIS 7 can be configured via the UI apparently once the Administration Pack for IIS 7 has been installed.
Another alternative that hasn't been mentioned yet would be to create a server-specific (but with the same name) configuration file that would be included in the beginning of your site script. In that server-specific config file you could set configuration variables as constants. That way, if there was a 'generic' configuration file loaded later, its values could be overridden in the server-specific configuration file as constants can't be redefined. You would want to either exclude the server-specific configuration file name from the synchronizations, or keep it in a path outside of the main content so that it is not accidentally overwritten.
For this purpose you can even configure Constant Arrays with help of Constant Array 2 class.
When server-specific configuration files are used you don't have to worry about the current SERVER_NAME -- this makes it easier for you to define the intended environment regardless of the current system name which could be handy also for QA purposes.
I'm trying to determine the best way of having a PHP script determine which server the script/site is currently running on.
At the moment I have a switch() that uses $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] to determine which server it's on. It then sets a few paths, db connection parameters, SMTP paramters and debug settings based on which server it's on. (There maybe additional parameters depending on the site needs.)
This means that I can simply drop the site onto any of the configured servers without having to change any code (specifically the configuration). If it's a new server, then I simply add a new case and it's ready from then on.
We have done loading config files based on the same SERVER_NAME:SERVER_PORT combination, but found that it's another file you have to maintain, plus we weren't sure on the speed of parsing ini files, (although having extra cases for each server may be just as slow).
Another problem we have is when a site is often moved between 2 servers, but we use the same SERVER_NAME and SERVER_PORT on each. This means we need to temporarily comment one case and ensure it doesn't get into the repo.
Another other ideas? It needs to be available on all servers (sometimes SERVER_NAME and SERVER_PORT are not). It would also be nice if it worked with the CLI PHP.
How about using $_SERVER['SERVER_ADDR'] and base your identity off the IP address of the server.
UPDATE: In a virtual host situation, you might also like to concatenate the IP with the document root path like so:
$id = $_SERVER['SERVER_ADDR'] . $_SERVER['DOCUMENT_ROOT'];
We use the $_SERVER['HTTP_HOST'] variable to create the filename of a PHP include file which contains all the vhost-specific information (we deploy the same software to a lot of vhosts)
I like this technique, as you can get clever with it to build hierarchies of configurations, e.g. for www.foo.com,
try to load com.config.php
try to load foo.com.config.php
try to load www.foo.com.config.php
Doing it this way lets you set options for all your live sites globally, and tweak individual sites on as as-needed basis. We have our own internal root domain name for developer sandboxes too, so we can enable all the developer level options in internal.config.php
You can also do this in reverse, i.e. try to load www.foo.com.config.php, and only if not found would you try to load foo.com.config.php, and so on. More efficient, but a little less flexible.
Here are some variables you can check:
$_SERVER['HTTP_HOST'];
I use this one for checking which server I'm on when php is running through apache.
$_SERVER['USER'];
$_SERVER['LOGNAME'];
I use these two for when I know I'm running from the console. One of those invariably resolves to a usable username. There seem to be no other host-defining variables in console mode.
This might not help you enough; If you find you still have a hard time being able to uniquely identify what server you are on you can give it a little bit of a "push." In my situation I have a small config file which is unique to each server, basically setting a php variable defining which environment I'm running in (e.g. development or production.) This way you only need to maintain one small, easy to recreate file outside of your source control.
Ive always kept a config.php on my sites, storeing such infomation which may be percificic to that server.
Being php parseing it is nearly (eg the file needs to be opened, closed, etc) as fast as having the code at the top of each script, and much faster than ini and xml config solutions
Centralised location for the sites configuration on each server, so easy to keep upto date (server doesn't change that oftern, updateing the config is simple with an update script).
Can be generated by the script, all my sites have a function that rebuilds the config file useing the $config[] assoc array.
Updates that effect the config file are as simple as "$config['key'] = 'new value';config_update()"
I have been using the following mechanism:
if(__FILE__ === '/Sites/mywebsite.com/includes/config.php')
define('SERVER', 'DEV');
else
define('SERVER', 'PRODUCTION');
My development environment has a rather distinct path structure so this works well, and I don't need to worry if additional domains are added to $_SERVER[HTTP_HOST], or a client that provides an incorrect HTTP_HOST value (although that would be rare ...).
Why don't you have configuration files for each host stored outside of the project directory and read it from the php code?
Having host specific code is not really a good practice.
$posix_uname = function_exists('posix_uname') ? posix_uname() : null;
$this_hostname = !empty($_SERVER["HOSTNAME"]) ? $_SERVER["HOSTNAME"] : $_ENV["HOSTNAME"];
$this_hostname = !empty($this_hostname) ? $this_hostname : $posix_uname['nodename'];
We use environment variables for this (ENVPHP environment variable which will contain the specific server environment - ie. development/test/production). This approach works very well for CLI scripts as well (for CLI you set the OS environment variables, for Apache you can use SetEnv switches in the host configuration). In PHP you access this environment variable using getenv('ENVPHP')...