Yii 1.1: Moving project code outside of webroot (plus multiple project support)

20 followers

Yii's by-default directory organization works well enough, but there are several steps one can take that improve the security and serviceability of the system, especially in the context of multiple Yii projects on the same machine (including multiple versions of the same project).

In this article we'll use three versions of the same blog project: production, test, and development, and presumably changes migrate from the last to the first. The precise top-level folder doesn't matter, and though this example uses /var/www/, it could be anywhere else.

  • /var/www/blog/
  • /var/www/blog-test/
  • /var/www/blog-dev/

These instructions were written assuming Yii 1.1 on a Linux server with Apache.

Make symbolic links to the Yii framework code.

Since the Yii framework can be shared among projects, our normal practice is to put the Yii code up at the top level with the full version information in the directory name.

  • /var/www/yii-1.1.4.r2429/
  • /var/www/yii-1.1.5.r2654/
  • ...

and then symbolic link from within the top-level of the project:

# cd /var/www/blog
# ln -s /var/www/yii-1.1.5-r2654 yii

Now, /var/www/blog/yii/ refers to the proper framework, and this is a fixed relative position within the particular project.

This also makes it easy to upgrade just one project at at time by changing the symbolic link.

Move protected/ out of the webroot

Nothing in the protected/ folder needs to be fetched directly by a webuser, so it's a needless security risk to put where it has to be protected with mechanisms that are easy to get wrong (.htaccess): this is just asking for trouble with no upside.

Instead, move the protected/ folder to the same level as the webroot/ folder, so there is no possibility of accessing it via the browser.

Move runtime/ out of protected/

The protected/ folder (even after moving) is composed almost entirely of source code, except for the runtime/ folder, which is used by Yii for logs, state information, and the like. Though it's reasonable that Yii have such an area, it just doesn't really belong with the source code.

So my practice is to move it out of protected/ as well, up at the same level as the webroot.

Resulting structure

After making these changes, the Yii directory structure looks like:

/var/www/blog/webroot/...
/var/www/blog/webroot/css/...
/var/www/blog/webroot/assets/...
/var/www/blog/protected/...
/var/www/blog/runtime/...
/var/www/blog/yii  --> symlink to /var/www/yii-1.1.5.r2654

The protected/ folder is now composed entirely of source code, is invisible to the web user, and the runtime/ area is separate from everything else.

Config file changes

We certainly have to tell Yii where these parts are, and this involves editing both the main config file, plus the application entry script.

In the config file we should document well what we're doing (to make it easier for others to follow), and a small helper function assists by joining paths together and removing spurious /./ and /../ components. Though we don't currently use the $webrootPath variable, adding it anyway helps give a fuller context to where all the parts fit together.

NOTE - some have suggested using the DIRECTORY_SEPARATOR constant instead of literal /, but it's not clear that this is really necessary even on a Windows machine. Those who find a case where it really does matter are encouraged to Edit this page to add your findings.

// in protected/config/main.php
...
// Set up path variable to reflect the new directory structure
//   $WEBHOME/             -- $homePath
//   $WEBHOME/webroot/     -- $webrootPath
//   $WEBHOME/protected/   -- $protectedPath
//   $WEBHOME/runtime/     -- $runtimePath
 
function _joinpath($dir1, $dir2) {
    return realpath($dir1 . '/' . $dir2);
}
 
$homePath      = dirname(__FILE__) . '/../..';
$protectedPath = _joinpath($homePath, 'protected');
$webrootPath   = _joinpath($homePath, 'webroot');
$runtimePath   = _joinpath($homePath, 'runtime');
 
return array(
    'basePath'    => $protectedPath,
    'runtimePath' => $runtimePath,
    ...
);

These variables may be used in other places as well, such as configuring the location of the authFile for CPhpAuthManager

The entry script in webroot/index.php must be taught where to find both the protected/ folder as well as the Yii framework, and it's easy to make these modifications:

...
$yii    = dirname(__FILE__) . '/../yii/framework/yii.php';
$config = dirname(__FILE__) . '/../protected/config/main.php';
...

Because these paths are used only once, there's no real need to optimize with the realpath() function as was done in the configuration file itself.

NOTE - if you have console-mode programs, you'll need to make similar $yii and $config changes in that entry script as well, though the exact form of the path changes depends on where your entry scripts are located.

Permissions

After making all these structural changes, do ensure that the key folders have the same permissions as before. In particular, two folders must be writable by the webserver user, and this is achieved with a combination of user, group, and permissions:

  • webroot/assets/
  • runtime/

Total 8 comments

#17193 report it
jpj at 2014/05/12 03:39am
Should be the default

I think Yii should provide by default a secure directory structure. It's the aim of a framework. Relying on .htaccess to deny access to protected ressources is not satisfaying and most frameworks use a public folder which is the only one accessible by the webserver.

#16587 report it
malkabani.com at 2014/03/08 04:53am
themeManager baseUrl

Where should I put themes folder i got error when i used themeManager=>baseUrl

#15146 report it
krowe at 2013/10/11 11:15am
Directory separators aren't that flexible

Some of the comments here seem to suggest that Linux will allow \ as a directory separator. This of course is completely untrue. Windows is the only OS that I know of to support both styles and that is mainly because the \ separator is non standard on the Web and if they didn't support / separators it would make many aspects of serving webpages from a Windows machine much more difficult. I'd imagine that it is only a coincidence that by allowing both they've also made Linux based code much easier to make x-platform compatible.

#2877 report it
Trejder at 2011/02/20 01:00pm
Multiple project version on the same machine? #2

What you wrote is of course correct. I'm only thinking if there is many early-stage developers that would keep both development and production versions of their app. I personally would rather prefer Subversion or other versioning system, set on some kind of secure server, with stable (production) versions kept separate out of trunk (developer) versions, and being able to download (checkout) to my home (working) computer, whatever version I want. But this is my personal feeling only.

About case risen by dis: I can think about only one situation - that is Microsoft IIS Server with PHP support enabled. As Microsoft doesn't actually believe Linux is existing (:) I wouldn't be surprised if IIS+PHP combo could fail on paths including / separator. Any other server (Apache-based, Apache code-based or written from scratch) I can think about, would support both path separators, no matter in what environment or system it would be run.

#2876 report it
Steve Friedl at 2011/02/20 12:05pm
Multiple on the same machine: it's ok.

@trejder - This is a fair point, but in practice, most Yii developers don't start out their projects in such a structured way under the supervision of a chief security officer. Instead, many just start hacking things together, to learn or build a proof of concept, and in this case there's no problem whatsoever doing it all on the same machine.

By the time the project grows to the point where separating roles becomes relevant, the developers by then will have a very strong handle on the overall Yii layout, and can tailor it to the better security practices you've suggested. They won't need to be told this.

This wiki article is tailored to the early-stage developer, before s/he has gained the wisdom you already have, to start structuring things in a more optimal way, knowing they'll do even more in the future should their needs warrant it.

In the #yii IRC channel, most people who are given this link are just asking for how to move protected/ out of the webroot, and for that purpose this page works really well.

@dis - Can you name a Windows environment where / or \ is unsafe?

#2691 report it
Trejder at 2011/01/29 10:40am
Multiple project version on the same machine?

This is a very good article and it touches many security-related problems, but there is one thing that concerns me. Is it a secure approach at all to hold both test and development versions of application on the same machine as production one?

I'm working in kind of large enterprise and for such thinking our chief security manager could kill! :] There is a strong pressure that development versions should be kept entirely on computers inside developers network, test versions in similar testing network and production versions on customers computers only or on separate production server system. And never, ever on the same machine, server or cluster! :]

#2435 report it
lubosdz at 2011/01/01 08:56am
Generic web project structure

In my experience, each web project (regardless technology) needs both private/public directories with additional writable/non-writable subdirectories. So optimal project structure for any web project would be:

/wwwroot/public ...(e.g. images, assets, css, downloads, ..) /wwwroot/public/writable ...(e.g. uploaded temporary image) /wwwroot/private ...(application, framework) /wwwroot/private/writable ...(e.g. logs)

Yii actually does not have optimally suggested directory structure from this point of view and developers must often do some minor configuration tweaks:-( In forthcoming Yii 2.0 release it would be nice to optimize also this aspect of Yii.

Cheers Lubos

#2326 report it
samdark at 2010/12/12 10:15pm
DIRECTORY_SEPARATOR and protected dir

As I know, it's relatively safe to use / under both Windows and Linux.

In my practice I'm often renaming protected to app since it's shorter, it goes first when sorted and it fits application alias more.

Leave a comment

Please to leave your comment.

Write new article
  • Written by: Steve Friedl
  • Category: How-tos
  • Yii Version: 1.1
  • Votes: +22 / -1
  • Viewed: 40,449 times
  • Created on: Dec 12, 2010
  • Last updated: Aug 13, 2011
  • Tags: config, security