Req #69090 [Com]: add prefix/xor to cache keys/check permissions or separate caches

From: Date: Fri, 04 Nov 2016 17:11:40 +0000
Subject: Req #69090 [Com]: add prefix/xor to cache keys/check permissions or separate caches
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-205186@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=69090&edit=1

 ID:                 69090
 Comment by:         jeff at mcneill dot io
 Reported by:        simon at ikanobori dot jp
 Summary:            add prefix/xor to cache keys/check permissions or
                     separate caches
 Status:             Analyzed
 Type:               Feature/Change Request
 Package:            opcache
 Operating System:   linux/debian
 PHP Version:        5.6.5
 Block user comment: N
 Private report:     N

 New Comment:

The suggestion as one user per site would have to apply all the way across the stack. So Apache
would have to have a single user, and no multi-site, no virtual sites, no resource sharing at all?
This would mean full resources for each and every site, which would be extremely wasteful. I have a
client with four websites. Does the single client need four users? I have several clients but I want
to run all clients and all their sites with a single configuration. Apache can do this, PHP can do
this, therefore opcache should be able to support such a configuration.


Previous Comments:
------------------------------------------------------------------------
[2016-11-04 16:54:30] jpauli@php.net

Excuse me but...

Wouldn't it be safer, more reliable, less hackish ... to separate the PHP pools of the website
? One pool per site, each pool listening on one port for CGI requests.

I mean, we do know for sure, that PHP cannot replace the OS and the configuration to secure
webservers in a shared architecture (shared hosting).
We've tried things such as safe_mode, open_basedir etc... since the beginning, with no success.
Nowadays, with mature OS, mature stacks, and a mature FCGI handler (PHP-FPM), it is easy to build a
shared hosting architecture not relying on the PHP language itself to isolate the virtualhosts.

Security - of the filesystem here - is the matter of the OS , not PHP.

Why don't you create several PHP-FPM pools, and secure them with one Unix user per pool ?
Obviously, the OPCache SHM wouldn't be shared here, but that is not what you want : OPCache SHM
shouldn't be crossed against several websites.

One pool per website = one SHM per website = one unix user per website = we solved every low level
security problems, right ?

------------------------------------------------------------------------
[2016-11-04 10:32:56] php-dev at coydogsoftware dot net

At this point two separate but related issues are being discussed:

 1. OPCache is prone to filename collisions across chroots. Adding inode number to the key would fix
this.

 2. All child PHP processes have access to all scripts in the cache, regardless of file permissions.
This renders OPCache unusable and dangerous on shared hosting servers, where OPCache may be used by
malicious users to bypass file permissions (and open_basedir FWIW). Adding inode to the cache key
would *not* fix this vulnerability, although in practice it would help in cases where parent
directory is unreadable.

I'll focus on the second issue: The obvious real-world example is a shared hosting server with
multiple CMS sites. Most PHP CMS's store database credentials and other sensitive information
in PHP scripts. Thus one malicious user (translation: compromised CMS) typically has full access to
databases for other users' CMS's if OPCache is enabled. If anyone doubts this, I can
provide working proof of concept exploit scripts targeted at WordPress. Fortunately this
doesn't seem to be exploited in the wild on any large scale, but I anticipate PHP malware will
start incorporating this technique as soon as it's more widely known.

I'm attaching a patch against the 5.6 branch which prepends a unique user identifier (username
in Windows, euid elsewhere) to the cache key. This should fix issue 2 in all situations where dl()
is not allowed (PHP5 with FPM would still in theory be vulnerable unless dl() is disabled).

It's not a perfect fix because it requires the PHP process to act as gatekeeper, essentially a
substitute for kernel enforcement of filesystem permissions. This is a problem if any PHP child
process with a descriptor for the shared opcache can be subverted, for example with a malicious
extension, hence my concern about dl()).
	
It will also fix issue 1 *only* in cases where the chroot environments are running PHP scripts with
separate user accounts. If the chroots use a shared web server user for PHP, issue 1 is still a
problem.

I agree with Rasmus that the script inode should also be added to the key, but I wasn't sure if
it would be appropriate to stat the script for its inode in the key generation function due to
performance concerns, and being unfamiliar with the extension and PHP itself I wasn't prepared
to do this in a more appropriate place.

I started with 5.6 because I believe this is a fairly serious security vulnerability which many
users are unaware of, which isn't adequately explained in the OPCache documentation, and which
should IMHO be fixed in a patch release. I'm willing to port it forward to 7.x if there's
interest and time allows, but I feel strongly that this patch or something similar should be
included in a 5.6 patch release ASAP.

Code is only minimally tested. Use at own risk. Apologies for any indentation issues; I did my best
to follow the style guide, but existing OPCache code did not. Feedback is welcome.

------------------------------------------------------------------------
[2016-11-02 07:20:32] jeff at mcneill dot io

Does this configuration of Opcache resolve the problem? See: http://stackoverflow.com/questions/20960469/php-fpm-5-5-does-opcache-run-per-domain

------------------------------------------------------------------------
[2016-10-27 05:39:36] kmark937 at gmail dot com

I'm concerned about this issue since it has the potential to be a security deal-breaker for
Zend OPcache when used on (as an example) a shared-hosting platform.

With that said I'm unable to replicate this using michal_micko's instructions for mod_php
and open_basedir configurations.

I used the same exact two VirtualHosts, directory tree, and file contents. I restarted apache,
accessed http://app2 first and then http://app1
but both displayed their proper "bad" and "good" texts respectively.

Ubuntu 14.04.5 LTS (as opposed to Debian 8.1)
Apache 2.4.7 (Ubuntu) (as opposed to 2.4.10, default config plus VirtualHosts)
PHP 5.6.27-1+deb.sury.org~trusty+1 (as opposed to 5.6.14-0+deb8u1, default config)

Possibly relevant:
opcache.enable = On
opcache.optimization_level = 0x7FFFBFFF
opcache.revalidate_path = Off
opcache.use_cwd = On
opcache.validate_timestamps = On

One potentially major difference between my config and michal_micko's is that I do not have any
XCache modules or Xdebug loaded. I would think, with except perhaps the PHP version, that is the
only major configuration difference between my set up and michal_micko's.

For me it looks safe to use PHP 5.6 OPcache + mod_php + open_basedir. When I dump the output of
opcache_get_status() I see only full paths for both the "full_path" value and the array
keys. I believe this is the use_cwd option doing its job. The only additional "securing"
that I see is setting restrict_api to something out of reach or adding opcache_* functions to
disable_functions.

I'd greatly appreciate it if anyone is able to correct or confirm what I've found.

------------------------------------------------------------------------
[2016-06-03 12:59:08] rgpublic at gmx dot net

This is probably related:

https://bugs.php.net/bug.php?id=67481

I also find it stunning that - at the very least - PHP doesn't warn about this or even better
refuse to start in this configuration. This is a serious security issue and all too easy to
overlook. Took me a while to figure out whats actually happening. This can lead to very mysterious
errors. Fragments or error messages of other unrelated sites on the same server suddenly showing up
and so on. 

Can anybody confirm that "opcache.optimization_level=0" is really a solid workaround that
can be used in production? There should be no information leak whatsoever. No files. No data. No
code. No nothing. That's why we have a chrooted environment in the first place. But obviously
I'd rather have an optimzation level 0 than no opcache at all.

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=69090


--
Edit this bug report at https://bugs.php.net/bug.php?id=69090&edit=1


Thread (38 messages)

« previous php.bugs (#205186) next »