Development

SecuringDevFrontend

You must first sign up to be able to contribute.

Version 7 (modified by rickb, 10 years ago)
Grammar, spelling, punctuation only

Securing the development frontend

One of the nice features of symfony is the ability to use different "loaders" for symfony that activate certain aspects of symfony. The main use of this is the development frontend, which greatly eases development by adding a little panel inside the output of HTML pages with debug information. However symfony requires these frontends to all reside inside the main web directory which obviously poses security issues. As a result these alternate frontends should not be put on production machines in the default location.

There are a number of solutions to solve this security risk, while still being able to leverage these frontends during development and even on the production machines. The below list was compiled from a thread on the developers list. Each approach has its own strengths and weaknesses, so the suggestions are not made in any particular order.

Suggestion 1: Symlink on demand

Move the development frontends into a separate directory and symlink them into the web directory as needed. When using this approach on production machines, you should at the very list use a non-standard name in the symlink. However, this is obviously "security through obscurity" (so not very secure). In case any automatic rsyncing solutions are used, additional precautions should be taken to prevent automatic propagation of these files to other servers.

Suggestion 2: Set variables in vhost

By using a virtual host ("vhost"), you can set the application, environment and debugging mode. The frontend code would need to be modified as follows:

<?php
define('SF_ROOT_DIR',     realpath(dirname(__FILE__).'/..'));
define('SF_APP',          isset($_SERVER['SF_APP'])?$_SERVER['SF_APP'] : 'frontend');
define('SF_ENVIRONMENT',  isset($_SERVER['SF_ENVIRONMENT'])?$_SERVER['SF_ENVIRONMENT'] : 'prod');
define('SF_DEBUG',        isset($_SERVER['SF_DEBUG'])?(bool)$_SERVER['SF_DEBUG'] : false);

A sample vhost for Apache could look like this:

<VirtualHost *:80>

  ServerName domain.com
  ServerAlias www.domain.com
  ErrorLog logs/domain_com-error.log
  CustomLog logs/domain_com-access.log common

  DocumentRoot /var/www/vhosts/domain.com/web
  <Directory /var/www/vhosts/domain.com/web>
    Order allow,deny
    Allow from all

    Options +FollowSymLinks +ExecCGI

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
  </Directory>

  Alias /sf /var/www/vhosts/domain.com/lib/vendor/symfony/data/web/sf
  <Directory /var/www/vhosts/domain.com/lib/vendor/symfony/data/web/sf>
    Order allow,deny
    Allow from all
  </Directory>

  SetEnv SF_APP frontend
  SetEnv SF_ENVIRONMENT dev
  SetEnv SF_DEBUG 1

</VirtualHost>

Note the three SetEnv lines and the corresponding $_SERVER[] expressions.

Suggestion 3: Limit access using SSL certificates

You can can limit access to specific URLs in the vhost, so frontend_dev.php could only be accessible to users presenting a client SSL certificate. You can find information on how to set this up over here.

Suggestion 4: Adapt url/asset helper url generation

You can make it possible to place the development frontends into a different directory, which can then be secured/exposed as required. Unfortunately, in Symfony 1.0.x this approach requires a fair number of hacks. Symfony 1.1 will make it easier to change behavior in the url and asset helpers.

1) Add a constant in the given front controllers that enables the host rewriting:

<?php
define('SF_REWRITE_ASSET_HOST', true);

2) Add a filter that rewrites the location (for most applications there will be a need to initialize the session, making sure that non authenticated users are directed at the right page, that the language/country cookie is set properly etc., which seems like a goof location for this code):

<?php
class initSessionFilter extends sfFilter
{
  /**
   * Execute filter
   *
   * @param FilterChain $filterChain The symfony filter chain
   */
  public function execute($filterChain)
  {
    if ($this->isFirstCall())
    {
      $context  = $this->getContext();
      $request  = $context->getRequest();
      $user     = $context->getUser();
      $action   = $context->getActionStack()->getLastEntry()->getActionInstance();

      if (defined('SF_REWRITE_ASSET_HOST') && SF_REWRITE_ASSET_HOST)
      {
        $asset_host = sfConfig::get('app_config_rewrite_asset_host');
        if (substr($asset_host, 0, 4) === 'http')
        {
          $request->setRelativeUrlRoot($asset_host);
        }
        elseif (preg_match('/s(\/.+\/)(.*)/', $asset_host, $matches))
        {
          $request->setRelativeUrlRoot(preg_replace($matches[1], $matches[2], $request->getRelativeUrlRoot()));
        }
        else
        {
          $request->setRelativeUrlRoot($request->getRelativeUrlRoot().$asset_host);
        }
      }
      ..
    }
  }
}

3) Add 'app_config_rewrite_asset_host' to your app.yml file

all:
  config:
#   rewrite_asset_host:         'http://foo.bar'
#   rewrite_asset_host:         's/admin\./'
    rewrite_asset_host:         '/../web'

Remember that you can have different configuration settings per environment and per server (following the above guidelines). However, there is a problem with this solution if the no_script_tag feature is disabled, because in that case the url helpers will use the $request->getRelativeUrlRoot() to construct the url. In that case you also need to do some further hacking:

4) First you need to store the original relative url root in a constant before modifying the value in your filter. Then you need to open up and store lib/symfony/controller/sfWebController.class.php in your application lib directory and modify the genUrl() method. Look for the call to getRelativeUrlRoot() and change the section of the code as follows:

<?php
$url = '';
if (!sfConfig::get('sf_no_script_name'))
{
  $url = $this->getContext()->getRequest()->getScriptName();
}
else if ($sf_relative_url_root = $this->getContext()->getRequest()->getRelativeUrlRoot())
{
  $url = (defined('RELATIVE_ROOT_URL')) ? RELATIVE_ROOT_URL : $sf_relative_url_root;
}