php-developer-list | 2000101

[PHP-DEV] Webhosting Enhancements to php From: Jason Greene (jason <email protected>)
Date: 10/13/00

I have been working on a SAPI php for a webhosting environment. Our
webhosting platform currently uses php running as a cgi, which has been
secured, but does not have the performance that customers would really like.
So, for the new webhosting build, my goal was to secure php in a shared web
environment. Most shared webhosting providers lay out the filesystem for
customers based off of the name.

Ex. /data/p/h/www.php.net/sandbox/web.

Now this requires VirtualHost configurations in apache. Unfortunately,
apache requires a configuration block per website. This becomes quite
ridiculous because apache has to be restarted everytime a customer is added.
To get around this we use the mod_vhost_alias option in apache that allows
Dynamically generated virtual hosts. So you could say,

<Virtual Host 1.1.1.1>
...
VirtualDocumentRoot /data/%2.1/%2.2/%2.0/sandbox/web
..
</VirtualHost>

The problem with dynamically generated Document Roots is that the Document
Root configuration setting in apache is not set. mod_vhost_alias assumes
control of processing URI's to a file, instead of http_core, when it is
enabled. The only way to derive the Document Root is to subtract script
name(request URI) from the translated_path.

PHP does not allow for a dynamic base directory restriction, so my attached
patch searches for a special condition(open_basedir=VIRTUAL_DOCUMENT_ROOT)
and then derive the document_root, which would restrict users to there site
only. Now, I realize that this is not the best method, but I believe the
principal is good.

Another requirement is that fopen ("/subdir/data.txt","rw") is translated to
/data/p/h/www.php.net/sandbox/web/subdir/data.txt.

For a patch I used internally, I wrote a similar version to the open_basedir
section that existed in fopen_wrapper, and would rewrite the requested
virtual path to the true one. This one was not very clean because when Zend
makes a request to open the primary script, it is always pulled in as the
full path. So, I had to compare if the string to see if the document_root
was already at the beginning. This is not a good method but it does work.

The last requirement is that all error messages show the virtual path
instead of the true path. If customers can see the true path it will either
confuse them, or risk the security of knowing our layout. In order to solve
this, I rewrote zend_error to filter all arguments for a full path. This is
by far a terrible modification.

In summary my suggested enhancement has the 3 main requirements
1. Virtual Path Support
2. Security Restrictions to Virtual Path
3. Error Messages produced off of Virtual Path

What I would like to do, is brain storm with everyone on a good method that
could be standardized and included with php.
I am willing to write the code, but I have yet to be completely familiarized
with your API, and your modularization.

I would highly appreciate your ideas, and suggestions in this matter.

Thanks
Jason

*** fopen-wrappers.c.orig Thu Oct 12 21:57:21 2000
--- fopen-wrappers.c Fri Oct 13 10:52:22 2000
***************
*** 154,159 ****
--- 154,160 ----
  char resolved_name[MAXPATHLEN];
  char resolved_basedir[MAXPATHLEN];
  char local_open_basedir[MAXPATHLEN];
+ char *local_open_basedir_sub; /* Substring pointer for strstr */
  int local_open_basedir_pos;
  SLS_FETCH();

***************
*** 170,175 ****
--- 171,188 ----
  && (local_open_basedir_pos >= 0)) {
  local_open_basedir[local_open_basedir_pos--] = 0;
  }
+ /* Special case VIRTUAL_DOCUMENT_ROOT
+ When using mod_vhost_alias the DOCUMENT_ROOT = PATH_TRANSLATED -
SCRIPT_NAME(request_uri)
+ This allows for protected mass dynamic hosting
+ */
+ } else if ((strcmp(PG(open_basedir), "VIRTUAL_DOCUMENT_ROOT") == 0) &&
+ SG(request_info).path_translated &&
+ *SG(request_info).path_translated
+ ) {
+ strlcpy(local_open_basedir, SG(request_info).path_translated,
sizeof(local_open_basedir));
+
local_open_basedir_sub=strstr(local_open_basedir,SG(request_info).request_ur
i);
+ /* Now insert null to break apart the string */
+ if (local_open_basedir_sub) *local_open_basedir_sub = '\0';
  } else {
  /* Else use the unmodified path */
  strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));

-- 
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, e-mail: php-dev-unsubscribe <email protected>
For additional commands, e-mail: php-dev-help <email protected>
To contact the list administrators, e-mail: php-list-admin <email protected>