How to Prevent PHP Sessions Being Shared Between Different Apache Vhosts

How to prevent PHP sessions being shared between different apache vhosts?

Edit is also the reason why you ALWAYS should set your session_save_path ( http://php.net/manual/en/function.session-save-path.php ) or use database session handling ( http://php.net/manual/en/class.sessionhandler.php ) if you are on an shared webhosting. Somebody can create an session id on there site and chmod it to 777 and use that session id on your site to bypass logins/or get more privileges. It can also be used for SQL injections.

This works because PHP doesn't enforce what session IDs belongs to what site. I know this because I've analysed the C/C++ source code behind sessions in PHP, and because I wondered how this could be possible. So never put too much trust that the $_SESSION array is safe on shared web hosting and you can't safely use this value in a SQL query.

Some code (file session.c) in PHP from C function php_session_start(); yes, this function is called when you call session_start() from PHP (and the only check I saw was in these lines of code):

/* Check whether the current request was referred to by
* an external site which invalidates the previously found id. */

if (PS(id) &&
PS(extern_referer_chk)[0] != '\0' &&
PG(http_globals)[TRACK_VARS_SERVER] &&
zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER"), (void **) &data) == SUCCESS &&
Z_TYPE_PP(data) == IS_STRING &&
Z_STRLEN_PP(data) != 0 &&
strstr(Z_STRVAL_PP(data), PS(extern_referer_chk)) == NULL
) {
efree(PS(id));
PS(id) = NULL;
PS(send_cookie) = 1;
if (PS(use_trans_sid) && !PS(use_only_cookies)) {
PS(apply_trans_sid) = 1;
}
}

The only check is the HTTP Header "HTTP_REFERER", but we all know it can be faked, so this is "security through obscurity". The only safe method is to use session_save_path or use a database session handler.

To set session_save_path in the php.ini, you should find more information here http://php.net/manual/en/session.configuration.php.

Or, if PHP is running as an Apache module, you can configure it in the htaccess file of vhost container:

php_value session.save_path "path"

Or even better a PHPINIDir per vhost:

<VirtualHost ip>
[...]
PHPINIDir /var/www/...
[...]
</VirtualHost>

UPDATE [Panique]:

I'm just adding the full solution to this answer, as this might help other people too. A sample full vhost setup:

<VirtualHost *:81>
DocumentRoot /var/www/xxx1
<Directory "/var/www/xxx1">
AllowOverride All
php_value session.save_path "/var/mysessionforproject_1"
</Directory>
</VirtualHost>

<VirtualHost *:82>
DocumentRoot /var/www/xxx2
<Directory "/var/www/xxx2">
AllowOverride All
php_value session.save_path "/var/mysessionforproject_2"
</Directory>
</VirtualHost>

Prevent Apache/PHP from running code that affects another vHost

While open_basedir would help, there are several ways of bypassing this constraint. While you could break a lot of functionality in php to close off all the backdoors, a better solution would be to stop executing the php as a user whom has access to all the files. To do that, you need to use php-fpm with a separate process pool/uid/gid for each vhost.

You should still have a separate uid for the php execution from the uid owning the files with a common group allowing a default read only access to the files.

You also need to have separate storage directories for session data.

A more elaborate mechanism would be to use something like Apache traffic server in front of a container-per owner with each site running on its own instance of Apache - much better isolation, but technically demanding and somewhat more resource intensive.

Bear in mind, if you are using mariadb or similar, that the DBMS can also read and write arbitrary files (SELECT INTO OUTFILE.../LOAD DATA INFILE)

UPDATE

Rather than the effort of maintaining separate containers, better isolation could be achieved with less effort by setting the home directory of the php-fpm uid appX to the base directory of the vhost (which should contain, not be, the document_root - see below) and use apparmor to constrain access to the common files (e.g .so libs) and @{HOME}. Hence each /var/www/appX might contain:

 .htaccess
.user.ini
data/ (writeable by fpm-appX)
html/ (the document root)
include/
sessions/ (writeable by fpm-appX)

Transfer Session Data Between Apache Virtual Hosts

As far as I'm aware, PHP sessions are not (by default) virtual-host aware: you would need to pass the session ID as part of the redirect and then set it in the other virtual host. So something like:

$sessionid = session_id();
Header("Location: $redirectURL?session=$sessionid");
exit;

And then in the target of the redirect:

session_id($_GET['session']);
session_start();

Try that and let me know how it works.

How can I stop multiple Apache instances on Windows sharing session information

Thanks for the replies. I didn't know where to set the path in the Apache config rather than the PHP.INI, but I have set it here and it works:

<IfModule php5_php5.c>
php_value session.save_path "C:\APACHE\sessions\v2"
</IfModule>

Thanks again.

How do I maintain PHP sessions across multiple domains on the same server?

Depending upon your preferred method of modifying PHP variables (Apache's config, .htaccess), change the session.cookie_domain value to be a consistent value.

I have multiple sub-domains, and each VirtualHost section in the Apache config file contains the following line:

php_value session.cookie_domain mydomain.com

The syntax should be similar if you make the changes in a .htaccess file.

Updated for bobert5064's comment:

For multiple domains (ie domain1.com, domain2.org), I think it is only necessary to choose a common domain name (ie domain1.com). I have never tried this, so I cannot verify that it works, but the logic seems accurate.

There is also a method to set the variables direction in PHP described at http://us.php.net/manual/en/function.session-set-cookie-params.php. The documentation makes no reference to the ability or inability to set cookies on a different domain.

Use cakephp session with two different domains

There's a common technique to run different sites/domains on a single server, called vhosts. As you can specify the session path for every vhosts, it's also possible to define the same path for all vhosts, which will result in shared sessions. A session created in project A is then also available in session B and other way round.

The vhosts don't care if you use pure PHP, CakePHP or whatever. Session is Session.

The vhost rules you need are (note that the session folder is the same in both definitions):

<VirtualHost *:80>
ServerName www.sample.com
DocumentRoot /var/www/xxx1
<Directory "/var/www/xxx1">
AllowOverride All
php_value session.save_path "/var/mysessionfolder"
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerName www.example.com
DocumentRoot /var/www/xxx2
<Directory "/var/www/xxx2">
AllowOverride All
php_value session.save_path "/var/mysessionfolder"
</Directory>
</VirtualHost>

To get more info on that it might be useful to read this thread: How to prevent PHP sessions being shared between different apache vhosts? which ask the opposite of what you want.

Apache unique Vhost user, PHP sessions not working

After a lot of Googling I have found a solution.

I also needed to ensure that php-fpm was installed and adding the following lines of configuration to the Apache VHost pointing to a unique PHP FPM socket per user fixed my issue.

<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php-fpm-client1.sock|fcgi://localhost"
</FilesMatch>

My PHP FPM configuration file looks like:

vi /etc/php/X/fpm/pool.d/client1.conf
[client1]

user = www-client1
group = www-client1
listen = /run/php/php-fpm-client1.sock
listen.owner = www-client1
listen.group = www-client1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[log_errors] = 1
php_admin_value[error_log] = "/var/www/logs/client1/php-error.log"
php_admin_value[open_basedir] = "/var/www/html/client1"
php_admin_value[session.save_path] = "/var/www/html/client1/_php/session"

bash /etc/init.d/php*-fpm restart

I also found that all php_admin_value values defined in the VHost needed to be moved to the FPM pool.



Related Topics



Leave a reply



Submit