|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2015-02-20 14:37 UTC] simon at ikanobori dot jp
Description: ------------ PHP's opcache seems to create keys for files it caches based on their filepath (including the cwd when the option opcache.use_cwd is set). When turning on opcache in commonly used hosting environments where users are chrooted it is very easy to get key collissions as the full path of a file in a chroot can commonly be /wp-config.php. The file that was accessed on this path first will be stored by opcache and be used by any interpreters executing the same file later on. Even without a chroot it is often easy to predict where files of another user on the same server will be located and they can still be included circumventing any file permissions set on these files even if PHP executes as the correct user (made even more trivial to figure out interesting files if access to the opcache_get_status() function is not restricted by the host). Example is in the "test script" below which shortly shows my relevant config lines. It'd be neat if opcache could implement a runtime config variable to give to an interpreter with a value that mashes up the key by prefixing or xor'ing it without the possibility of being overwritten from within the script. Alternatively it might be possible to use different parts of shm based on a configuration option so the cache is per-user. Test script: --------------- # Permissions on both directories and files are set to rwx for user only root@debian:/home# ls -l . total 12 drwx------ 2 one one 4096 Feb 20 12:45 one drwx------ 2 two two 4096 Feb 20 12:45 two root@debian:/home# ls -l one/ total 4 -rw------- 1 one one 25 Feb 20 12:42 one.php root@debian:/home# ls -l two/ total 4 -rw------- 1 two two 46 Feb 20 12:44 two.php # one.php just sets a single variable root@debian:/home# cat one/one.php <?php $one = "one"; ?> # This file tries to include the non-existent "/one.php" in its chroot root@debian:/home# cat two/two.php <?php include "/one.php"; print $one; ?> # FPM processes are configured to run as the user root@debian:/home# grep -r "user =" /etc/php5/fpm/pool.d/ /etc/php5/fpm/pool.d/1.conf:user = one /etc/php5/fpm/pool.d/2.conf:user = two # FPM processes also run chrooted into the homedirs root@debian:/home# grep -r "chroot " /etc/php5/fpm/pool.d/ /etc/php5/fpm/pool.d/1.conf:chroot = /home/one /etc/php5/fpm/pool.d/2.conf:chroot = /home/two # Request one.php on pool one root@debian:/home# curl http://1.localhost/one.php # Request two.php on pool two root@debian:/home# curl http://2.localhost/two.php one # That's the content of /one.php which is owned by user one, in its own chroot # being served by user two from a different chroot while user two doesnt even # have read permissions on the file. Expected result: ---------------- Users can only access their own files (when configured correctly). Actual result: -------------- Users can access other users' files when previously accessed by opcache cross chroots and ignoring filesystem permissions. Patchesbug69090.diff (last revision 2016-11-15 15:37 UTC by dmitry@php.net)validate_permission.diff (last revision 2016-11-15 11:21 UTC by dmitry@php.net) opcache_bug69090_user_id_keys (last revision 2016-11-04 10:35 UTC by php-dev at coydogsoftware dot net) Pull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Oct 26 09:00:01 2025 UTC |
I replicate this problem with two VirtualHosts (using open_basedir). OS: Debian 8.1 php -v PHP 5.6.14-0+deb8u1 (cli) (built: Oct 4 2015 16:13:10) Copyright (c) 1997-2015 The PHP Group Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies with XCache v3.2.0, Copyright (c) 2005-2014, by mOo with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies with Xdebug v2.2.5, Copyright (c) 2002-2014, by Derick Rethans with XCache Optimizer v3.2.0, Copyright (c) 2005-2014, by mOo with XCache Cacher v3.2.0, Copyright (c) 2005-2014, by mOo with XCache Coverager v3.2.0, Copyright (c) 2005-2014, by mOo Apache: 2.4.10 VirtualHosts: ------------- cat 050-opcache-fail.conf <VirtualHost *:80> ServerName app1 DocumentRoot /var/www/app1/web php_admin_value open_basedir /var/www/app1 <Directory /var/www/app1/web> Options Indexes FollowSymLinks MultiViews AllowOverride None #Order allow,deny #Allow from All Require all granted </Directory> </VirtualHost> <VirtualHost *:80> ServerName app2 DocumentRoot /var/www/app2/web php_admin_value open_basedir /var/www/app2 <Directory /var/www/app2/web> Options Indexes FollowSymLinks MultiViews AllowOverride None #Order allow,deny #Allow from All Require all granted </Directory> </VirtualHost> Example - PHP apps - typical using with composer (dummy): --------------------------------------------------------- apps directory structures: /var/www/ app1 vendor third party lib.php autoload.php web index.php app2 vendor third party lib.php autoload.php web index.php web/index.php (same app1 and app2): ----------------------------------- <?php require_once __DIR__ . '/../vendor/autoload.php'; echo (new third\party\lib())->getName(); ?> vendor/autoload.php (same app1 and app2): ----------------------------------------- <?php require_once __DIR__ . '/third/party/lib.php'; ?> vendor/third/party/lib.php (app1): ---------------------------------- <?php namespace third\party; class lib { public function getName() { return 'good library'; } } ?> vendor/third/party/lib.php (app2): ---------------------------------- <?php namespace third\party; class lib { public function getName() { return 'bad hacked library'; } } ?> Security issue: --------------- It would be a problem in webhostings. After web server restart just run bad app2 as first. Workaround: ----------- opcache.optimization_level=0I would to update this bug with releated issue. # php-fpm -v PHP 7.2.10 (fpm-fcgi) (built: Oct 3 2018 08:52:25) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.2.10, Copyright (c) 1999-2018, by Zend Technologies # uname -a FreeBSD websrv1 11.2-RELEASE-p4 FreeBSD 11.2-RELEASE-p4 #0: Thu Sep 27 08:16:24 UTC 2018 root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC amd64 php-fpm pools: [aaa] user = aaa group = nobody listen.owner = aaa listen.group = www listen.mode = 660 listen = /var/sockets/php72-aaa.sock chroot = /home/aaa (...) [bbb] user = bbb group = nobody listen.owner = bbb listen.group = www listen.mode = 660 listen = /var/sockets/php72-bbb.sock chroot = /home/bbb (...) php.ini quotation: (...) [opcache] opcache.use_cwd = 1 opcache.validate_permission = 1 opcache.validate_root = 1 echo "some small php code" > /home/aaa/public_html/index.php chown aaa /home/aaa/public_html/index.php echo "some huge php code" > /home/bbb/public_html/index.php chown bbb /home/bbb/public_html/index.php Even with these above new settings opcache caches only one sample of index.php from two totals. When visiting website of user aaa then index.php of this user is located in opcache cache. When visiting website of user bbb only index.php of bbb is located in the cache instead. We are never seeing these two files cached same time although these are two different files. These has two different non-chrooted paths and different persmission (owner). We have found this issue looking at list of cached files as seen on opcache-status-master/opcache.php. Even if file list is innacurate we had compared also used/free memory and that looks like only one file is cached same time. We haven't found crosscache problem nor other security problem. Just no cache profit when chrooted paths and names of files are the same (which is common and expected in multiuser chroot environment)