Home > Code, Security > Securing page requests – Phramework::findRequestedPage()

Securing page requests – Phramework::findRequestedPage()

The central idea behind this framework is to have each request handled by a centralised and secure core that manages repetitive tasks for us. In order to achieve this we redirected all requests to a single index.php file by implementing .htaccess RewriteRules to set the value of $_GET['page']. While these rules will only pass secure values to our script it is always possible to bypass them by directly accessing index.php?page=some-malicious-path and we therefore need to address this threat.

Before we can address the threat we need to have an understanding of exactly what we are trying to protect against. The final “clean” version of the requested page will be used for file includes which have a particular set of threats:

  1. Root directory access – a UNIX path that begins with a forward slash (/) is an absolute path. As this can access any directory on the system we clearly do not want this. Mitigation – check the first character.
  2. Directory traversal – UNIX designates the parent directory as two fullstops (..) which, if allowed, can once again permit access to any directory on the system. A sufficient number of parent directories (../../../../ etc.) is equivalent to root directory access. Mitigation – check for fullstops.
  3. Remote file includes – if we were to allow any piece of code to be included an attacker could create their own PHP script, host it anywhere and reference it in the path provided – e.g. index.php?page=http://malicious-domain.tld/script which is clearly not an ideal situation. Something important to know is that a reference to a remote script relies on either a fully qualified domain name or an IP address, both of which must contain at least one fullstop. Mitigation – check for fullstops – same as (2).
  4. Null bytes (%00) will cause PHP to truncate the end of a string. It is easy to believe that because we will be placing “.php” at the end of our include statement that we are safe. However, include “malicious-path%00.php”; and include “malicious-path”; are equivalent. Mitigation – check for a null byte.
  5. Unknown – it is very possible that we have not covered all threats here and it is definitely a sound security measure to include a final catch-all check. As this negates the need for all previous checks it is possible to only utilise this approach, however a defense in depth strategy is advisable. Mitigation – duplicate the regular expressions used in the RewriteRules.

For now, the response to a potentially malicious path will simply be to redirect the user to the home page main, but in the future we will always have the ability to log the incident and potentially temporarily block the IP address1. As all responses are the same (and so too is the response to an empty page request) we can utilise a try-catch statement and throw an exception any time we detect a problem. For brevity I have excluded the full function definition but note that the only parameter is $dirty which will receive $_GET['page'].

try {

if(empty($dirty)) throw new Exception();  //nothing provided

if(substr($dirty, 0, 1)==”/”) throw new Exception();  //point (1)

if(strpos($dirty, “.”)!==false) throw new Exception(); //points (2) and (3)

if(!preg_match(“/^(?:[\/a-z0-9_-]+)$/i”, $dirty)) throw new Exception(); //points (4) and (5)

$clean = strtolower($dirty); //it passed all the tests

}

catch(Exception $e){ //something is wrong so send them to the home page

$clean = “main”;

}

Should the “dirty” string fail any of our tests then we automatically default to the home page by catching the exception. Note the use of the negative double equality (!==) and not the single (!=); as zero (0) is registered as false by PHP the single will fail to throw an exception if the first character matches (returning an index of 0). The use of the double enforces matching of the boolean false (no match).

In conforming with the URL conventions we need to handle the case of a trailing slash (/) which is a request for the directory index main.

if(substr($clean, -1)==”/”) $clean .= “main”;

Now that we have a “clean” path we can set it as a constant using Phramework::define() as detailed in the last post. Later in the development it will prove useful to also have the path broken down into an array describing the directory structure. This storage of “clean” data is the exact reason why there is no unlocking function associated with define.

$this->define(‘PAGE’, $clean);

$this->define(‘PAGES’, explode(“/”, $clean));

If I have overlooked any issues please let me know via a comment. If anyone would like to write a unit test for this function it would be much appreciated.

Current text version of Phramework.php

1 Later in the development of Phramework I will detail implementation of an intruder detection system and our responses.

if(substr($dirty, 0, 1)=="/"){
Advertisement
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.