Hi,

When running the apache server configured to run in the more memory efficient configuration of running php as a cgi process rather than an apache module the database credentials are not scrubbed and can be exposed by executing phpinfo() from the status report page or if enabling the php filter module and executing it in a basic page..

Since the vast majority of admins running VPS's would configure their servers to run php-cgi, php-fcgid or php-fpm instead of running mod_php I think its a requirement the scrubbing works on these configurations as well..

CommentFileSizeAuthor
#4 setenvif-1356150-5345986.patch2.18 KBblueprint

Comments

blueprint’s picture

If I understand the request correctly we should implement:

SetEnvIf, the Request_URI part to ensure that the _SERVER["db_passwd"] part is not exposed to scripts outside the scope of 'index.php'.

for instance:
SetEnvIf Request_URI "index.php$" db_passwd=secret

This is easy to change in provision by modifiying a template method.

I'm working on integrating cgi changes since it's also a problem that the aegir debian builds require mod_php (we use fastcgi....)

blueprint’s picture

to be explicit (for version 1.6 packages for debian):

in /usr/share/drush/commands/provision/http/apache_ssl/vhost_ssl.tpl.php

    SetEnvIf Request_URI  "^index.php$" db_type=<?php print urlencode($db_type); ?>
    SetEnvIf Request_URI  "^index.php$" db_name=<?php print urlencode($db_name); ?>
    SetEnvIf Request_URI  "^index.php$" db_user=<?php print urlencode($db_user); ?>
    SetEnvIf Request_URI  "^index.php$" db_passwd=<?php print urlencode($db_passwd); ?>
    SetEnvIf Request_URI  "^index.php$" db_host=<?php print urlencode($db_host); ?>
    SetEnvIf Request_URI  "^index.php$" db_port=<?php print urlencode($db_port); ?>

the complete file

<?php if ($this->ssl_enabled && $this->ssl_key) : ?>

  <VirtualHost <?php print "{$ip_address}:{$http_ssl_port}"; ?>>
  <?php if ($this->site_mail) : ?>
    ServerAdmin <?php  print $this->site_mail; ?>
  <?php endif;?>

    DocumentRoot <?php print $this->root; ?>

    ServerName <?php print $this->uri; ?>
    SetEnvIf Request_URI  "^index.php$" db_type=<?php print urlencode($db_type); ?>
    SetEnvIf Request_URI  "^index.php$" db_name=<?php print urlencode($db_name); ?>
    SetEnvIf Request_URI  "^index.php$" db_user=<?php print urlencode($db_user); ?>
    SetEnvIf Request_URI  "^index.php$" db_passwd=<?php print urlencode($db_passwd); ?>
    SetEnvIf Request_URI  "^index.php$" db_host=<?php print urlencode($db_host); ?>
    SetEnvIf Request_URI  "^index.php$" db_port=<?php print urlencode($db_port); ?>

    # Enable SSL handling.

    SSLEngine on

    SSLCertificateFile <?php print $ssl_cert; ?>

    SSLCertificateKeyFile <?php print $ssl_cert_key; ?>

<?php
if (sizeof($this->aliases)) {
  print "\n ServerAlias " . implode("\n ServerAlias ", $this->aliases) . "\n";

  if ($this->redirection) {
    print " RewriteEngine on\n";

    // Redirect all aliases to the main https url.
    print " RewriteCond %{HTTP_HOST} !^{$this->uri}$ [NC]\n";
    print " RewriteRule ^/*(.*)$ https://{$this->uri}/$1 [L,R=301]\n";
  }
}
?>

  <?php print $extra_config; ?>

      # Error handler for Drupal > 4.6.7
      <Directory "<?php print $this->site_path; ?>/files">
        SetHandler This_is_a_Drupal_security_line_do_not_remove
      </Directory>

    # Prevent direct reading of files in the private dir.
    # This is for Drupal7 compatibility, which would normally drop
    # a .htaccess in those directories, but we explicitly ignore those
    <DirectoryMatch "<?php print $this->site_path; ?>/private/(files|temp)/" >
       SetHandler This_is_a_Drupal_security_line_do_not_remove
       Deny from all
       Options None
       Options +FollowSymLinks
    </DirectoryMatch>

  </VirtualHost>
<?php endif; ?>

<?php
  include('http/apache/vhost.tpl.php');
?>

Tested with aegir 1.6 debs from the aegir project. (hostmaster fine, sites fine).
WARNING: there are probably things which will break here!

blueprint’s picture

Just realized I TESTED slightly different code:

    SetEnvIf Request_URI  index\.php$ db_type=
    SetEnvIf Request_URI  index\.php$ db_name=
    SetEnvIf Request_URI  index\.php$  db_user=
    SetEnvIf Request_URI  index\.php$ db_passwd=
    SetEnvIf Request_URI  index\.php$  db_host=
    SetEnvIf Request_URI  index\.php$ db_port=
blueprint’s picture

StatusFileSize
new2.18 KB

Attaching patch

blueprint’s picture

Status: Active » Needs review
blueprint’s picture

I had missed a case (well, many actuall, when setenv works as expected) ...

try:
SetEnvIfNoCase Request_URI \/$ HTTP_r_uri=slash
SetEnvIfNoCase Request_URI \/index\.php$ HTTP_r_uri=index

That is, you need two cases for calls to drupal in a certain root. Match \/$ and \/index\.php$

Otherwise you'll miss everything with no query :)

anarcat’s picture

Status: Needs review » Needs work

I don't understand this setup very well, so I am not sure I am in the right position to review this.

I would like to hear more details about how those servers are configured. Is this done through php-fpm? Because if it is, it seems to me the environment from the webserver shouldn't necessarily contaminate the FastCGI processes...

Also, how do we ensure that database credentials *are* passed to the site? If we do not set that environment and cloaking is enabled in settings.php, this will just break sites...

I'd be curious to understand how this is done in Nginx too.

omega8cc’s picture

Version: 6.x-1.x-dev » 6.x-2.x-dev

Not sure if this still happens in Apache - after all we unset all those variables immediately after reading them:

  /**
   * Now that we used the credentials from the apache environment, we
   * don't need them anymore. Clear them from apache and the _SERVER
   * array, otherwise they show up in phpinfo() and other friendly
   * places.
   */
  if (function_exists('apache_setenv')) {
    apache_setenv('db_type', null);
    apache_setenv('db_user', null);
    apache_setenv('db_passwd', null);
    apache_setenv('db_host', null);
    apache_setenv('db_port', null);
    apache_setenv('db_name', null);
    // no idea why they are also in REDIRECT_foo, but they are
    apache_setenv('REDIRECT_db_type', null);
    apache_setenv('REDIRECT_db_user', null);
    apache_setenv('REDIRECT_db_passwd', null);
    apache_setenv('REDIRECT_db_host', null);
    apache_setenv('REDIRECT_db_port', null);
    apache_setenv('REDIRECT_db_name', null);
  }
  unset($_SERVER['db_type']);
  unset($_SERVER['db_user']);
  unset($_SERVER['db_passwd']);
  unset($_SERVER['db_host']);
  unset($_SERVER['db_port']);
  unset($_SERVER['db_name']);
  unset($_SERVER['REDIRECT_db_type']);
  unset($_SERVER['REDIRECT_db_user']);
  unset($_SERVER['REDIRECT_db_passwd']);
  unset($_SERVER['REDIRECT_db_host']);
  unset($_SERVER['REDIRECT_db_port']);
  unset($_SERVER['REDIRECT_db_name']);

I have committed cloaked credentials support in Nginx, and when used with PHP-FPM it *doesn't* expose credentials via phpinfo() if the request comes via Drupal (because they are unset already).

It *does* expose credentials when you create whitelisted PHP file, like /update.php and you will put just phpinfo() there, but at least in Nginx configuration we already deny any unknown, non-Drupal PHP files from being executed, so we don't need any extra SetEnvIfNoCase-like tricks in Nginx.

I guess we need similar allow/deny rules in the Apache config to deny arbitrary PHP files from being executed.

Note also that index.php is not the only legitimate Drupal PHP file which should have an access to the db credentials.

anarcat’s picture

Status: Needs work » Closed (won't fix)

This is exactly what I wanted to hear, thank you so much!!! :)

As for php5-cgi (aka people running Aegir not under Apache or Nginx: how do you *do* that anyways??): I would say we will simply not fix this until that install process is streamlined and documented, with driver à l'appui.

Cheers!