The Theo Spears Blog

Blogging Considered Harmful (Considered Harmful)?

The dangers of mod_rewrite and php

First posted 2006-07-04 00:00:00.000001+00:00

I came across an interesting security hole today. Many php frameworks, both widely used and homegrown, use a single entry point for all pages, in other words to load the page 'members' you might point your browser at http://www.example.com/index.php?members. This is ugly so often it is hidden behind mod_rewrite, so http://www.example.com/members is silently translated behind the scenes. For example you might use

RewriteRule ^/(.*) /index.php?$1

This all works nicely for your PHP files. However most sites also have images, css, or javascript files to load. These static files typically should not be loaded through index.php. One common way to address the issue is to avoid rewriting if the url corresponds to a file that exists. e.g.

RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^/(.*) /index.php?$1

You can then happily link to all your images and scripts and it will work nicely. Another less common option people sometimes take is not to rewrite certain types of file, for example to avoid rewriting image and css files you might use

RewriteCond %{REQUEST_URI} ! /(.css)|(.jpeg)|(.png)$/ RewriteRule ^/(.*) /index.php?$1

This way image and css files aren't rewritten, but other URLs are. Again this is fine. This has the added advantage that people can't load your php files directly bypassing your framework because they will be rewritten.

Stop. This is not fine.

It is true that http://www.example.com/test.php will be rewritten, as will http://www.example.com/test.php?evil-evasion.css however PHP is a bit special. Whilst with apache going to http://www.example.com/myfile.html/some/random/path/that/is/not/real will cause a 404 error, this is not true with http://www.example.com/test.php/some/random/path. In fact the latter will invoke test.php and will ignore the rest of the path. This means if we load http://www.example.com/test.php/evil-evasion.css we can avoid the rewrite rule and invoke test.php directly.

The moral of all this is if you have files you don't want the user to load don't rely on clever methods to stop them being loaded. If at all possible keep them outside the webroot.

(n.b. mod rewrite lines here have not been tested, but you get the idea)