About the Author

Chris Shiflett

Hi, I’m Chris: entrepreneur, community leader, husband, and father. I live and work in Boulder, CO.


Shared Hosting

  • Published in PHP Architect on 23 Mar 2004
  • Last Updated 23 Mar 2004
  • 38 comments

Welcome to another edition of Security Corner. This month, I have chosen a topic that is a concern for many PHP developers, shared hosting. Through my involvement with the PHPCommunity.org project, my contributions to various mailing lists, and by keeping tabs on PHP blogs and news sites, I have seen this topic brought up in various incarnations. Some people are concerned about hiding their database access credentials, some are concerned about safe_mode being enabled or disabled, and others just want to know what they should be concerned about, if anything.

As a result, I have decided to address these concerns in as much detail as possible, so that you will have a better understanding and appreciation of shared hosting security risks. After reading this article, you can decide if there is anything for you to be concerned about.

Shared Hosting

Since the advent of HTTP/1.1 and the required Host header, shared hosting has become very popular. Without this header, there is no direct way for a web client to identify the domain. It makes a TCP/IP connection to the host identified by the domain, and that's where it sends its reqquest, which can be as simple as the following:

GET /path/to/script.php HTTP/1.0

Notice that the URL presented in the request does not include the domain name. This is because it is unnecessary under the assumption that only one domain is served by a particular server. With HTTP/1.1, Host becomes a required header, so a simple request must be expressed as follows:

GET /path/to/script.php HTTP/1.1
Host: example.org

With this format, a single web server can server many domains, because the client must identify the domain. As a result, a hosting company can host many domains on a single server, and it is not necessary to have a separate public IP for each domain. This yields much more inexpensive hosting and has spurred a tremendous growth in the web itself. Of course, this has been a driving force behind early PHP adoption as well.

The downside to shared hosting is that it incurs some security risks that do not exist in a dedicated server environment. Some of these risks are mitigated by PHP's safe_mode directive, but it doesn't address the root cause of most security risks, and a solid understanding of these risks is necessary to appreciate what safe_mode does and doesn't do. Because of this, I begin by introducing some of the unique risks associated with shared hosting.

Filesystem Security

A true multi-user operating system such as Linux is built upon a fundamentally secure approach to user permissions. When you create a file, you specify a set of permissions for that file. This is achieved by assigning each file both user and group ownership as well as a set of privileges for three groups of people:

  • User
  • Group
  • Other

The privileges that you can assign each category of user include read, write, and execute (there are some other details, but they are irrelevant to the present discussion). To illustrate this further, consider the following file listing:

-rw-r--r--  1 chris shiflett 4321 May 21 12:34 security-corner

This file, security-corner, is owned by the user chris and the group shiflett. The permissions are identified as -rw-r--r--, and this can be broken into the leading hyphen (indicating a normal file), and three groups of permissions:

  • rw- (read, write, no execute)
  • r-- (read, no write, no execute)
  • r-- (read, no write, no execute)

These three sets of permissions correspond directly to the three groups of users: user (chris), group (shiflett), and other.

Linux users are probably familiar with these permissions and how to change them with commands such as chown and chmod. For a more thorough explanation of filesystem security, read Linux Security HOWTO: Files and File System Security.

As a user on a shared host, it is unlikely that you will have read access to many files outside of your own home directory. You certainly shouldn't be able to browse the home directory or document root of other users. However, with a simple PHP script, this can be possible.

Browsing with PHP

For this discussion, assume that the web server is Apache and that it is running as nobody. In order for Apache to be able to serve your web content, that content must be readable by nobody. This includes images, HTML files, and PHP scripts. Thus, if someone could gain the same privileges as nobody on the server, they would at least have access to everyone's web content, even if precautions are taken to prevent access by any other user.

Whenever Apache executes your PHP scripts, it of course does so as nobody. Combine this with Files and File system SecurityPHP's rich set of filesystem functions, and you should begin to realize the risk. To make the risk clearer, I have written a very simplistic filesystem browser in PHP (See Listing 1). This script outputs the current setting for the safe_mode directive (for informational purposes) and allows you to browse the local filesystem. This is an example of the type of script an attacker might write, although several enhancements would likely be added to make malicious actions more convenient.

Listing 1:

<?php
 
echo "<pre>\n";
 
if (ini_get('safe_mode')) {
    echo "[safe_mode enabled]\n\n";
} else {
    echo "[safe_mode disabled]\n\n";
}
 
if (isset($_GET['dir'])) {
    ls($_GET['dir']);
} elseif (isset($_GET['file'])) {
    cat($_GET['file']);
} else {
    ls('/');
}
 
echo "</pre>\n";
 
function ls($dir)
{
    $handle = dir($dir);
    while ($filename = $handle->read()) {
        $size = filesize("$dir$filename");
 
        if (is_dir("$dir$filename")) {
            if (is_readable("$dir$filename")) {
                $line = str_pad($size, 15);
                $line .= "<a href="{$_SERVER['PHP_SELF']}?dir=$dir$filename/">$filename/</a>";
            } else {
                $line = str_pad($size, 15);
                $line .= "$filename/";
            }
        } else {
            if (is_readable("$dir$filename")) {
                $line = str_pad($size, 15);
                $line .= "<a href="{$_SERVER['PHP_SELF']}?file=$dir$filename">$filename</a>";
            } else {
                $line = str_pad($size, 15);
                $line .= $filename;
            }
        }
 
        echo "$line\n";
    }
 
    $handle->close();
}
 
function cat($file)
{
    $contents = file_get_content($file);
    echo htmlentities($content, ENT_QUOTES, 'UTF-8'); 
}
 
?>

One of the first places an attacker might want to glance is /etc/passwd. This is achieved by either browsing there from the root directory (where the script begins) or visiting the URL directly (by calling the script with ?file=/etc/passwd).

This gives an attacker a list of users and their home directories. Another file of interest might be httpd.conf. Assuming each user's home directory has a directory called public_html for their respective document roots, an attacker can browse another user's web content by calling the script with ?dir=/home/victim/public_html/.

A security-conscious user will most likely keep sensitive configuration files and the like somewhere outside of document root. For example, perhaps the database username and password are stored in a file called db.inc and included with code similar to the following:

<?php
 
include '../inc/db.inc';
 
?>

This is wise, but unfortunately an attacker can still view this file by calling the browse.php script with ?file=/home/victim/inc/db.inc. Why does this necessarily work? For the include call to be successful, Apache must have read access to the file. Thus, this script must also have access. In addition, because the user's login credentials are often the same as the database access credentials, this technique might allow an attacker to compromise any account on the server (and launch additional attacks from compromised accounts).

There is also the potential for an attacker to use this same script to gain access to anyone's session data. By just browsing the /tmp directory (?dir=/tmp/), it is possible to read any session that is stored therein. With a few enhancements to the script, it could be even easier to view and/or modify session data from these files. An attacker could visit your application and then modify the associated session to grant administrator access, forge profile information, or anything of the like. And, because the attacker can browse the source to your applications, this doesn't even require guesswork. The attacker knows exactly what session variables your applications use.

Of course, it is much safer to store session data in your own database, but we have just seen how an attacker can gain access to that as well. The safe_mode directive helps prevent these attacks.

Configuration Directives

The safe_mode directive is specifically designed to try to mitigate some of these shared hosting concerns. If you practice running the script from Listing 1 on your own server, you can experiment with enabling safe_mode and observing how much less effective the script becomes.

When safe_mode is enabled, PHP checks to see whether the owner of the script being executed matches that of the file being opened. Thus, a PHP script owned by you cannot open files that are not owned by you. Your PHP scripts are actually more restricted than you are from the shell when safe_mode is enabled, because you likely have read access to files not specifically owned by you. This strict checking can be relaxed somewhat by enabling safe_mode_gid. This directive relaxes the checking to the group instead of the user.

Because safe_mode can cause problems for users who have a legitimate reason to access files owned by another user, there are a few other directives that allow even more flexibility. The safe_mode_include_dir directive can specify one or more directories from which users can include files, regardless of ownership. I encourage you to read http://php.net/features.safe-mode for more information.

A similar PHP directive is open_basedir. This directive allows you to restrict all PHP scripts to only be able to open files within the directories specified by this directive, regardless of whether safe_mode is enabled.

Bypassing safe_mode

Of course, safe_mode only protects against people using PHP to gain access to otherwise restricted data. It does nothing to protect you against someone on your shared server who writes a similar program in another language. For example, why would the Perl interpreter care what's in php.ini? The manual states:

It is architecturally incorrect to try to solve this problem at the PHP level, but since the alternatives at the web server and OS levels aren't very realistic, many people, especially ISP's, use safe mode for now.

Consider the following CGI script written in Bash:

#!/bin/bash
 
echo "Content-Type: text/plain"
echo ""
cat /etc/passwd

This will output the contents of /etc/passwd as long as Apache can read that file. So, we're back to the same dilemma. While the attacker can't use the script in Listing 1 to browse the filesystem when safe_mode is enabled, this doesn't prevent the possibility of similar scripts written in other languages.

What Can You Do?

You probably knew that a shared host was less secure than a dedicated one long before this article. Luckily, there are some solutions to a few of the problems I have presented, but not all. There are basically two main steps that you want to take on a shared host:

  • Keep all sensitive data, such as session data, stored in the database.
  • Keep your database access credentials safe.

How do you keep your database access credentials safe? If another user can potentially have access to any file that we make available to Apache, it seems that there is nowhere to hide them. My favorite solution to this problem is one that is described in the PHP Cookbook by David Sklar and Adam Trachtenberg.

The approach is to use environment variables to store sensitive data (such as your database access credentials). With Apache, you can use the SetEnv directive for this:

SetEnv DB_USER "myuser"
SetEnv DB_PASS "mypass"

Set as many environment variables as you need using this syntax, and save this in a separate file that is not readable by Apache (so that it cannot be read using the techniques described earlier). In httpd.conf, you can include this file as follows:

Include "/path/to/secret-stuff"

Of course, you want to keep these include statements within each user's VirtualHost block, otherwise all users could access the same data.

Because Apache is typically started as root, it is able to include this file while it is reading its configuration. Because each child process handling a request runs as nobody, it can no longer access this file directly, so other users cannot access this information with clever scripts.

Once these environment variables are set, you can access them in the $_SERVER superglobal array. For example:

<?php
 
mysql_connect('localhost', $_SERVER['DB_USER'], $_SERVER['DB_PASS']);
 
?>

Because this information is stored in $_SERVER, you need to take care that this array is not output in any of your scripts. For example, a call to phpinfo() reveals everything from $_SERVER, so you should ensure that you have no public scripts that execute this function.

Until Next Time...

Hopefully you now understand some of the risks involved with shared hosting and can take some steps to mitigate them. While safe_mode is a nice feature, there is only so much help it can provide in this regard. It should be clear that these risks are actually independent of PHP, and this is why other steps are necessary.

As always, I'd love to hear about your own solutions to these problems. Until next month, be safe.

About this article

Shared Hosting was last updated on 23 Mar 2004. Follow me on Twitter.

38 comments

1.Peter Brodersen wrote:

Hi,

I would like to point out that even when on a shared host where all users have no other scripting access besides PHP in an safe_mode- and open_basedir-restricted environment, some exploits are still possible, as mentioned in

http://stock.ter.dk/session.php

First of all, sessions are shared among virtual hosts, allowing a user to read and alter his session on another virtual host.

Second, even if each host has its oven session.save_path, hijacking of other users' sessions on other hosts would still be pretty easy, as glob() discloses file names in its error messages, and the actual session.save_path could easily be retrieved by providing an invalid session id (forcing the server to throw an error, disclosing the save_path).

I would like to have PHP to disclose less and glob() to perform a more clueful UID-check, since the administrator and the user would have a hard job to prevent these types of attacks (even when in safe_mode, open_basedir, individual save_path and no other scripting opportunities)

- Peter

Sun, 26 Sep 2004 at 18:33:44 GMT Link


2.Chris Shiflett wrote:

That's a nice demonstration of one of the exploits I'm talking about. Well done.

However, I agree with Derick that this is not a bug, and I don't think it's practical to try to solve these types of problems at the PHP level. It doesn't make sense, and our experience has shown that people gain a false sense of security when features are added to PHP that help to mitigate these types of concerns (safe_mode and open_basedir).

Sensitive data (session data included) needs to be in the database, because it's practically impossible to secure access to any file on a shared host that the Web server can read. If a PHP developer takes this approach and also uses my recommendation for securing the database access credentials (borrowed from the PHP Cookbook), then that's about the best you can do on a shared host.

Sun, 26 Sep 2004 at 18:52:29 GMT Link


3.Peter Brodersen wrote:

I just think it's a pity to have implemented e.g. safe_mode support, and at any bug the reply would just be "Well, safe_mode is a poor solution anyway". That might be, but as long as it's officially a part of PHP, functions might as well be implemented in the spirit of safe mode. Especially since one currently can't configure his way out of the problems (besides jailing or one-server-per-customer)

As of bug #28932, the behaviour of glob() simply doesn't make any sense - I don't see why "This is broken anyway" should be an argument for not fixing up the arbitrary "first file check"-behaviour.

For an otherwise fine setup in a shared environment (no shell access, no access out of home dir using (s)ftp, no access to cgi), I can't see why we can't find any better solutions to the obvious caveats.

Sun, 26 Sep 2004 at 22:01:00 GMT Link


4.Leendert Brouwer wrote:

You can bypass safe_mode if PHP runs as a CGI binary. I wrote a few things about it in an article you can find here somewhere, http://www.php-mag.net/itr/ausgaben/psecom,id,160,_psframe,,linkobject,source_,nodeid,112.html

(basically when in that situation, you can put a php.ini in the directory the executing script is in, so you can just override the safe_mode directive in there).

Wed, 29 Sep 2004 at 07:35:05 GMT Link


5.Alex Bronstein wrote:

Thank you for this article. I was recently shocked to learn that a file with database credentials can be so easily read by anyone else on the same server. A huge number of people have their db passwords exposed like this on a shared server that allows CGI access. I agree that the only truly secure approach seems to be placing database credentials in the httpd.conf file. But am I correct in assuming that only the server administrator can do that? If you have an account on a shared server that does not do this, is there anything you can do as a developer (besides finding a better hosting service)?

Fri, 29 Oct 2004 at 02:45:09 GMT Link


6.Arnold Shore wrote:

I'v e taken a different approach, one based on field-level encryption. Here, the key isn't retained in the clear, but rather itself encrypted by the user password, a hash of which is stored.

So, at login time, part of the authentication process computes the decryption key. While the latter may be treated as a session variable, I'm passing it form to form via POST.

Mon, 31 Jan 2005 at 01:38:24 GMT Link


7.Richard Lynch wrote:

Arnold:

But passing that decryption key around in POST data exposes it.

The only "gain" here is that you're doing something obscure enough that not many people will know it -- but it won't even slow down a determined individual with half a clue.

Security by obscurity is seldom truly effective.

Chris:

I suspect most hosts don't have a user-editable 'include' file in their httpd.conf, even inside their own VirtualHost since that would, in their minds, give WAY too much control over to their client.

So while the PHP Cookbook solution is "Good" I don't think it's being implemented in most shared hosting environments.

Perhaps if Apache provided a way to d this:

<VirtualHost _client_>

<AllowDirective SetEnv>

include /home/client/private/local.httpd.conf

</AllowDirective>

</VirtualHost>

and *ONLY* the SetEnv directive would work.

This would allow the Host to give up only the control they choose, rather than whatever a malicious client can dream up...

Mon, 31 Jan 2005 at 19:04:50 GMT Link


8.Mark Page wrote:

Given that I have never seen an architecturally correct approach

proffered, I would be interested in getting feedback from anyone

on the following approach for improving user account isolation

for web directories.

Any observations received with interest.

The key issue I am fixated on here is the fact that normally

-r--r----- myuser myuserwww home/myuser/www/my.host.com/somefile.php

cannot be read by Apache as "nobody" but, instead of degrading the

user restriction (as is conventional) to

-r--r--r-- myuser myuserwww home/myuser/www/my.host.com/somefile.php

so that "nobody" may read it, we can specifically use an Apache user

for example "apache" and invite it into the user's www group

myuserwww

As a result all files are hidden from users that Linux treats as

other.

In addition Apache cannot read a file at all unless the user has

OKed it by chgrping to their corresponding www group.

Here is the relevant config (PHP is running as an Apache module):

php.ini:

=======================

safe_mode On

safe_mode_gid On

=======================

/etc/group:

=======================

myuserwww:x:482:myuser,apache

baduserwww:x:482:baduser,apache

=======================

Consider these files:

-rw-r----- myuser myuserwww /home/myuser/web_app/config/sensitive.info

-rw-r----- myuser myuserwww /home/myuser/www/my.host.com/myscript.php

Here is myscript.php:

=======================

<?

include ("/home/myuser/web_app/config/sensitive.info");

// Do stuff with it...

?>

=======================

Now consider a malicious script:

-r--r----- baduser baduserwww /home/baduser/evil/snoop.php

Here is snoop.php:

=======================

<?

$fp = fopen ("/home/myuser/web_app/config/sensitive.info");

if ($fp) {

// Read and print...

}

?>

=======================

baduser@machine $ php evil/snoop.php

Running as baduser you cannot read -rw-r---- myuser myuserwww

because you are classified by Linux as other.

Assume that the root user has assigned a web document root within

/home/baduser - for example

-rwxrwx--- baduser baduserwww /home/myuser/www/another.host.com

If the user attempts to run as the user apache he MUST make an

http request to http://another.host.com/snoop.php

He will be running as Apache but all scripts accessed *from* that

script will be gid matched against it:

-rw-rw---- baduser baduserwww /home/myuser/www/another.host.com/snoop.php

The only groups the user can chgrp his file to are those he has been

invited into within /etc/group by the root user.

Therefore he is inevitably foiled by either Linux file permissions

or PHP safe_mode_gid

UNLESS, of course, he can find some other way make a request to

Apache to run an arbitrary program with no equivalent to PHP safe

mode -- the aforementioned architectural bone of contention.

Is there no practical way of preventing this?

Even something as drastic as banning CGI, perl, whatever?

mark.page@whitelamp.com

Wed, 20 Apr 2005 at 20:57:01 GMT Link


9.Peter Brodersen wrote:

Mark; baduser could easily (even in safe_mode) create a file owned by the apache user, assuming the user has FTP access (which most webhosting users have)

1. Create a new folder.

2. chmod this folder to 0777

3. Upload a php file that writes a new php file in the folder. The new file will be owned by the apache user.

4. Request the new php file via a browser. Presto, the new file is member of both myuserwww and baduserwww groups

If you just want to snoop around and search for file names, my pet peeve (and otherwise useful function) glob() could be exploited easily, bypassing safe_mode and open_basedir:

print_r(glob("{.,/other/users/homedir}/*",GLOB_BRACE));

Tue, 03 May 2005 at 19:35:22 GMT Link


10.Marthinus Heyns wrote:

What about .htaccess deny from all? (I tried it inside doc root).

1. create folder in doc root

mkdir test

2. change access permissions to 701

3. add .htaccess file to dir

4. create your password file in /test

5. chmod 544 test

a.Apache reads the script fine.

b.You can't browse or access files in the directory directly (.htaccess - deny from all)

c.I ran Chris' script on the shared host to test it. I could browse everybody else's files no problem - but couldn't browse my own.

Can anybody tell me about other weaknesses here? How it can be exploited?

Fri, 20 May 2005 at 17:24:58 GMT Link


11.Khael wrote:

Richard: I don't see the problem for shared hosts. The way I plan to set up our servers after reading this article, is like this:

1) Only PHP for my customers. No cgi or anything else.

2) Run phpmyadmin + webmail and other shared programs my users need on a common ssl virtual domain.

2) Run PHP with safe_mode On. Maybe turn off for a few domains, like i.e. phpmyadmin need to access /tmp to upload files to import cvs data etc.

3) Add open_basedir and SetEnv's to the VirtualHost setting of each domain:

<VirtualHost *:80>

ServerName www.somedomain.tld

SetEnv DB_USER "myuser"

SetEnv DB_PASS "mypass"

php_admin_flag open_basedir /path/to/basedir

...

</VirtualHost>

And I can tell my client to use $_SERVER["DB_USER"] + $_SERVER["DB_PASS"] to access his mysql database.

My virtual hosts files are owned by root by default and located in /etc/apache2/sites-available/

Sun, 05 Jun 2005 at 19:29:49 GMT Link


12.Andreas Korthaus wrote:

Hi!

What I don't understand - why do you want to use mod_php in a shared hosting enviroment at all? _Every_ large german hoster I know uses php-cgi together with suexec or suphp, so the php/cgi process will run with user-rights.

So you can use the following file-permissions:

static content(HTML, images, CSS...): 640

directories: 710

CGI: 700

PHP-CGI/includes(passwords): 600

In my opinion this is the only solution to the shared-hosting problem. Of course it is up to the users to set proper permissions.

best regads

Andreas

Tue, 14 Jun 2005 at 09:45:21 GMT Link


13.Martin Corona wrote:

I am using pair networks and I am little ticked that ALL users can ssh into the system, cd to the directory where my files are and read all my include files (with mysql passwords).

What I think the problem is..

Every user is a member of the group 'users'.

Since apache is seen as "other', all directories and files must have the last bit set to read or read and execute for directories.

My suggestion was to create unique ids for ALL users and then add apache to those new groups.

That way apache can still read and cd into sub directories while other users that are not in my unique group have no read access to my files.

SUX BALLZ

Thu, 23 Jun 2005 at 15:44:45 GMT Link


14.Jp wrote:

Ok, I'm VERY alarmed with this article. Never thought much about this, as a simple user I just assumed shared hosting was safe, and that admins knew what they were doing =)

Not saying that they don't, but now I'm aware of the limitation they face.

What I got from the article was that this problems mainly come from the fact that apache runs as an user (nobody, for instance) and that this user must have read access to the scripts - tell me to re-read the article if I'm wrong so far!

We wouldn't have this problems if we had multiple instances of apache - one for each user - and each running with the respective user's previledges. This way, a script that I own (or my group, perhaps) would be run with an instance of apache with my previledges, and wouldn't be able to access other user's data (unless it's 777, of course).

AFAIK, apache already runs in a 'threaded'sort of way (each thread handles a diferent request?), so wouldn't it be feasible to run this diferent threads with diferent previledges?

Tue, 12 Jul 2005 at 22:48:52 GMT Link


15.Jp wrote:

I've stuck my head into the apache documentation (unfortunatly, only) after my last post. I also must have skipped Andreas Korthaus' post above, that mentions suEXEC and suPHP (whose existence was a mistery to me, hence my previous post...).

Don't these solve the problems mentioned in this article?

Tue, 12 Jul 2005 at 23:19:55 GMT Link


16.Ben Parish wrote:

There is one problem with the 'SetEnv' solution in the PHP Cookbook. The environmental variable is available to all users of that virtual host. If they have phpinfo.php or loop through $_SERVER, they will see the password from any file under the root of that virtual host.

To restrict exposure to a particular directory or specific file:

1. First put an 'Include' to the secret file in httpd.conf. For example:

Include "/web/private/databases/secret.txt"

2. In the password file, use the 'SetEnvIf' directive to enable the environmental variables by directory only or within a specific file. For example:

- For all PHP files in the directory:

SetEnvIf Request_URI "/path/to/my/directory" ORACLE_PASS=5gHj790j

- For a specific file in the directory

SetEnvIf Request_URI "/path/to/my/directory/connection.oracle.php" ORACLE_PASS=5gHj790j

Thu, 14 Jul 2005 at 13:09:11 GMT Link


17.Kevin M wrote:

Hi Guys , just reading through this thread , and wondered what is wrong with using a wrapper script to wrap the php executes as the user who owns the script ? , ie , suPHP .

This then has the benefit of any files being created in the /tmp folder , being owner by a unique user with 600 , and if unix filesystem permissions are set correctly , makes it to that the php scrip can not traverse out of the folders which the user has access to .

Thu, 04 Aug 2005 at 16:19:32 GMT Link


18.Sidney Markowitz wrote:

My ISP addressed this problem by installing PHP as a cgi (keeping it also installed as mod_php for backwards compatibility for existing customers) and using a FAQ I wrote up on how to use .htaccess and a shell script wrapper cgi to secure a PHP application.

http://www.sonic.net/support/faq/advanced/phpwrap/

This is similar to Kevin M's suggestion in the previous comment, but with the details worked out so you can install an existing PHP application with no change to the code and have the file permissions set to be readable only by the owner, which is the user not the web server.

To answer jp's question: Some ISPs use suexec and suphp as Andreas mentioned in his comment. My ISP could not since they were already set up using Apache 1.3 and mod_php and could not make such a drastic break from what was running for their customers. That is not an uncommon situation.

But I just realized that Chris' solution using environment variables should work fine in the .htaccess file. Is there anything insecure about putting

SetEnv SECRET_DB_PASSWORD "GoOgoLpLeX"

in a .htaccess file?

In my ISP's setup my PHP script can see that in $_SERVER['SECRET_DB_PASSWORD'] when the script is run by mod_php, or in $_SERVER['REDIRECT_SECRET_DB_PASSWORD'] and also in $_ENV['REDIRECT_SECRET_DB_PASSWORD'] when the script is run using the secure redirection as a CGI as described in the above FAQ.

After all the work I did on figuring out how to hide the file containing the db password, adding a single SetEnv in a .htaccess seems like a trivially easy solution that would just work.

Tue, 09 Aug 2005 at 18:58:47 GMT Link


19.Sidney Markowitz wrote:

I realized after I posted the above that using the SetEnv directive in .htaccess does not fix the problem on shared servers. The .htaccess file must be readable by the userid of the web server, which makes it readable to anyone else with an account on the shared server if they can write a cgi in shell script or some other language besides safe-mode PHP.

That must be why Chris talked about putting the SetEnv directive in httpd.conf. Unfortunately that is not accessible to ordinary customers of a shared web host provider.

That leaves as the remaining solutions having a web host provider who has set up their system to provide PHP only through suexec or suphp, or using PHP as a cgi called through a CGIWrap shell script as I describe in http://www.sonic.net/support/faq/advanced/phpwrap/

Tue, 09 Aug 2005 at 21:53:33 GMT Link


20.phpgurru wrote:

Hey just reading through this thread and it has excellent information.. Thanx to all of you.

Best Regards

Shafiq

Wed, 17 Aug 2005 at 08:49:57 GMT Link


21.yosoyminero wrote:

And what about the proposal from Marthinus Heyns: would it work??

Thu, 25 Aug 2005 at 16:04:25 GMT Link


22.jtheory wrote:

I don't think Marthinus Heyns' idea will work either, unfortunately (.htaccess deny from all). I'm actually not sure why it stopped the PHP browsing script, but I'm pretty sure it wouldn't stop a simple cgi. There's no reason a bash script would honor a .htaccess file.

That's always the tough part, it seems -- there are hacks to secure PHP by itself, but the average shared host must offer more than just PHP to get customers (I know I certainly couldn't use a host that didn't give me shell access, CGI, etc.)... and every program or script that can possibly be run through Apache has basically the same access as the programs and scripts that YOU need to run from Apache.

The only workable solution I've seen so far is separate Apache processes for the different users -- but I haven't actually seen this in practice, so I don't know how practical it is on a server with hundreds of users (plus there would need to be a mapping of domain name to owner user for this to work).

What else is there that really solves the issue? VPS's are pretty cheap some places....

Wed, 31 Aug 2005 at 04:30:34 GMT Link


23.yosoyminero wrote:

jtheory:

Thanks a lot!

Thu, 01 Sep 2005 at 00:43:53 GMT Link


24.Daniel Northam wrote:

ALSO:

In PHP php.ini; for added security, I would recommend enabling the following :

open_basedir = the beginning of your web hosting directory

Keep the root of your web directories owned by root or something that does not have the same UID as your web server. This will prevent the script above from working and you will have to know the pathing in order to get around.

Thu, 01 Sep 2005 at 16:26:02 GMT Link


25.Kiyono Yamamoto wrote:

Hi Guys , just reading through this thread , and wondered what is wrong with using a wrapper script to wrap the php executes as the user who owns the script ? , ie , suPHP .

This then has the benefit of any files being created in the /tmp folder , being owner by a unique user with 600 , and if unix filesystem permissions are set correctly , makes it to that the php scrip can not traverse out of the folders which the user has access to .

Mon, 12 Sep 2005 at 19:41:24 GMT Link


26.optimizehosting.com wrote:

I would like to have PHP to disclose less and glob() to perform a more clueful UID-check, since the administrator and the user would have a hard job to prevent these types of attacks (even when in safe_mode, open_basedir, individual save_path and no other scripting opportunities)

Sun, 02 Oct 2005 at 17:27:09 GMT Link


27.Dinis Cruz wrote:

Just a quick note to say that this issue also occurs with asp.net and the .Net framework in IIS 5.0 and 6.0 when they are executed with Full Trust (which is the default).

And the bottom line is the same, the users of these services have no idea how insecure they are and the ISPs make no efforts to tell them.

see http://owasp.net/blogs/dinis_cruz/archive/2005/11/16/79.aspx

Dinis Cruz

Owasp .Net Project

www.owasp.net

Wed, 16 Nov 2005 at 07:00:07 GMT Link


28.Alex Fraser wrote:

I'm using Sidney Markowitz's suggestion. Thanks, Sidney! It looks safe to me, and works very well.

Thu, 20 Jul 2006 at 01:54:53 GMT Link


29.Ivan wrote:

"That must be why Chris talked about putting the SetEnv directive in httpd.conf. Unfortunately that is not accessible to ordinary customers of a shared web host provider."

That's very true.

Wouldn't putting the SetEnv directive in some text file outside of the web root system and then using "Include /path/to/that/text/file" in your local .htaccess file work as well?

Thu, 27 Jul 2006 at 07:58:34 GMT Link


30.Ivan wrote:

Oops,

A little presumptuous of me. I've just checked it out and, no, sadly you can't have an Include in a .htaccess file, which means that unfortunately you have to completely rely on your host to maintain it in the <VirtualHost> directive in the global httpd.conf file.

Bummer.

Fri, 28 Jul 2006 at 00:57:39 GMT Link


31.Brandon Hauff wrote:

I have placed a .htaccess file in my public_html directory containing the SetEnv commands. However, the .htaccess file must be at least chmod 644 for any webpages to load. Thus, any other user on the server can just cat .htaccess and get the user name and password. I don't really know much about .htaccess files, so perhaps there is a way to fix this.

Thanks,

Brandon

Thu, 14 Dec 2006 at 19:38:54 GMT Link


32.Oscar Duron wrote:

Again, people please help.

I think all of you instead of solving this problem you do enjoy the game.

What do hackers do? .... Hide

So, until the day, that all programs have the option to have a key, including OS, the problems, if not solved, at least I think will be reduced 90% or more.

How?

Lets say PHP

select a suffix (XYZ) , 2,4, 6 characteres or more, you choose. From now on, PHP will only interpret functions suffixed with your key.

mysql_connectXYZ()

$_SESSIONXYZ[]

Lets say MySQL

SELECTXYZ

UPDATEXYZ

Lets say Apache

.....

This way, how can a hacker get into your system, if does not know your suffix commands?

ls is no more ls, it is now lsXYZ

sh is no more sh, is shXYZ

This way, you dont have to learn new commands or languages, use the same, but with a suffix only you know

Al might begin at the OS level per user

when you login, in your password file, there will be too your suffix

USERADD ..... bla bla

Suffix - XYZ

The OS only interprets commands suffixed

Of course, this is complex, but practical

When the hacker could guess the suffix you use?

May be somebody gets your username and password

but what if does not know your suffix? he cant do any thing in that server.

Mon, 19 Feb 2007 at 19:16:51 GMT Link


33.Bill Gross wrote:

Instead of placing "problem specific" fields like DB_USER DB_PASSWORD in an include file. Would it not be more generic and simpler for shared hosting services to simply provide each user with a unique encryption key that they could use to encrypt anything they want kept safe.

This key would be set, and used in exactly the same manner as the technique Chris described in this article except that it could be implemented for every user on a shared host and probably done in an automated way.

The shared hosting company might also provide some kind of web/ui interface to allow their users to alter this key.

Sat, 02 Feb 2008 at 19:00:38 GMT Link


34.sean wrote:

Hi. I'm very glad I'm aware of how insecure shared hosting is, yet I don't see how this can be implemented. Another poster brought it up previously, but it was never addressed: if on a shared host, use httpd.conf to secure DB credentials, but on a shared host, you won't have access to httpd.conf!!!

Also, I changed the permissions on my php include file (outside the webroot) to -rw------- and my php scripts can still access the variables - isn't this sufficient (owner-only read/write, how could anyone else access it)? Also, does that mean that my php scripts are running as "me" ie under my user?

Cheers!

- Sean

Tue, 17 Feb 2009 at 05:20:22 GMT Link


35.Don Raikes wrote:

Hello,

Wow this is a fantastic article. I just went through a detailed website security training course, nad now to see how it applies to php as well has been great. My problemis that I am developing a php-based web application for a friend, and it will be running on a shared hosting environment. I don't have access to the httpd.conf file to add an include for the database secret information there, and as was noted in an earlier comment you can't put the include statement inside of the .htaccess file.

Also, these accounts do not have cgi access, so how can I secure this kind of information? any tips / suggestions would be appreciated.

Tue, 17 Feb 2009 at 19:06:58 GMT Link


36.sean wrote:

I did a lot of testing and found out that many hosts are running scripts as the owner of the script. If that applies to your host, you may be able to put the DB credentials in a file with -rw------- permissions and have it be both unreadable by other people's scripts and readable by your scripts.

- Sean

Thu, 19 Feb 2009 at 02:36:47 GMT Link


37.Don Raikes wrote:

Sean,

Thanks for the tip. As it turns out, my host does allow the scripts to be chmod'ed to -rw-------, so that solves my issue.

Thanks!

Wed, 25 Feb 2009 at 09:42:30 GMT Link


38.jerry wrote:

does anyone have a feel for "how bad it would be" to just run a separate apache for each user, as mentioned by jtheory (#22)? This is sort of like "running suphp" but for the whole web-server, not just php. So it "fixes" the perl/bash/etc issues. And apache is pretty lightweight as these things go, anyway. So maybe it's not that bad? Having the os-level isolation between users just seems much more solid than any of these crazy safe_mode hacks or whatever.

Sat, 24 Apr 2010 at 19:41:38 GMT Link


Hello! What’s your name?

Want to comment? Please connect with Twitter to join the discussion.