HTTP Headers input

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

Post Reply
User avatar
tecktalkcm0391
DevNet Resident
Posts: 1030
Joined: Fri May 26, 2006 9:25 am
Location: Florida

HTTP Headers input

Post by tecktalkcm0391 »

Hello, I know that you need to validate any user supplied data, including HTTP Headers... so how could I secure this:

Code: Select all

    var $page = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // Requested page
 
It's for a cache creating class... if you need more of the code, just let me know.

Thanks,
Chris
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Re: HTTP Headers input

Post by Oren »

Well it depends on how and what you gonna do with $page.
User avatar
tecktalkcm0391
DevNet Resident
Posts: 1030
Joined: Fri May 26, 2006 9:25 am
Location: Florida

Re: HTTP Headers input

Post by tecktalkcm0391 »

cache the file with the following:

Code: Select all

 
    // Settings
    var $cachedir = YB_ROOT_S.'public/cache/'; // Directory to cache files in (keep outside web root)
    var $cachetime = 600; // Seconds to cache files for
    var $cacheext = 'cache'; // Extension to give cached files (usually cache, htm, txt)
    // Script Vars
    var $page = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // Requested page
    var $cachefile = $this->cachedir . md5($page) . '.' . $this->cacheext; // Cache file to either load or create
    // Defaults - DO NOT EDIT
    var $ignore_page = false;
    var $started = true; 
    var $cachefile;
    
    // Ignore List
    var $add_list = array(
        '*.com/rss.php',
        '*.com/search/'
    );
     
    function start(){
 
        for ($i = 0; $i < count($this->add_list); $i++) {
            $add_list = (strpos($this->page, $this->add_list[$i]) !== false) ? true : $this->add_list;
        }
         
        $cachefile_created = ((@file_exists($this->cachefile)) and ($this->add_list === false)) 
                                ? @filemtime($this->cachefile) : 0;
        @clearstatcache();
         
        // Show file from cache if still valid
        if (time() - $this->cachetime < $cachefile_created) {
         
        //ob_start('ob_gzhandler');
        @readfile($cachefile);
        //ob_end_flush();
        exit();
         
        }
         
        // we need to generate a cache file
         
        ob_start();
    }
 
User avatar
tecktalkcm0391
DevNet Resident
Posts: 1030
Joined: Fri May 26, 2006 9:25 am
Location: Florida

Re: HTTP Headers input

Post by tecktalkcm0391 »

tecktalkcm0391 wrote:cache the file with the following:

Code: Select all

 
    // Settings
    var $cachedir = YB_ROOT_S.'public/cache/'; // Directory to cache files in (keep outside web root)
    var $cachetime = 600; // Seconds to cache files for
    var $cacheext = 'cache'; // Extension to give cached files (usually cache, htm, txt)
    // Script Vars
    var $page = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // Requested page
    var $cachefile = $this->cachedir . md5($page) . '.' . $this->cacheext; // Cache file to either load or create
    // Defaults - DO NOT EDIT
    var $ignore_page = false;
    var $started = true; 
    var $cachefile;
    
    // Ignore List
    var $add_list = array(
        '*.com/rss.php',
        '*.com/search/'
    );
     
    function start(){
 
        for ($i = 0; $i < count($this->add_list); $i++) {
            $add_list = (strpos($this->page, $this->add_list[$i]) !== false) ? true : $this->add_list;
        }
         
        $cachefile_created = ((@file_exists($this->cachefile)) and ($this->add_list === false)) 
                                ? @filemtime($this->cachefile) : 0;
        @clearstatcache();
         
        // Show file from cache if still valid
        if (time() - $this->cachetime < $cachefile_created) {
         
        //ob_start('ob_gzhandler');
        @readfile($cachefile);
        //ob_end_flush();
        exit();
         
        }
         
        // we need to generate a cache file
         
        ob_start();
    }
 
    function finish(){
        // Now the script has run, generate a new cache file
        $fp = @fopen($this->cachefile, 'w');
         
        // save the contents of output buffer to the file
        @fwrite($fp, ob_get_contents());
        @fclose($fp);
         
        ob_end_flush(); 
    }
 
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Re: HTTP Headers input

Post by Oren »

You can save whatever you want to a file - if you are not using it later anywhere then why should you care? but I guess it's not the case, if you cache it then you are going to use it somewhere in one way or another and that's what matters. Tell us what exactly you gonna do with it and where you gonna use it, not how you process it and save it. Anyway, I don't see how this is a new security issue... why do you think it is different than any other security issues that you deal with when, for example, you save a user reply to a blog post?
Of course if you save it to a database, then you'll have to take care for SQL injections as well... that's why I said that it depends what exactly you do with it and where exactly you gonna use it.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: HTTP Headers input

Post by Mordred »

Step 1.
To check if it's used safely, first follow the instances where it is used:
1. md5($page) --> safe, md5() can eat anything.
2. strpos($this->page,...) -> safe, strpos can eat anything too.
3. If we were paranoid, we would check how these string functions behave with huge string. As we're dealing with URLs, which have a limited lenght, it's probably safe. If we were trully paranoid, we would check anyway ;)
4. If at any point the value is assigned to another variable, follow that one as well.

Step 2.
To check if it's used correctly, follow the instances again:
1. You use md5() to "compess" the URL in the cache filename. URLs are not regular strings though.

Code: Select all

index.php?a=b&c=d
index.php?c=d&a=b
These are different strings, but the same URL. This may lead to cache misses, and can be used to exhaust cache resources, even if you check the validity of the URL. An URL with N parameters can be written in N! (N-factorial) ways, which will md5() to different cache names, even though they are the same URL.

2. strpos(...) -- this logic is not working, and it has never worked as it is written. Now let's suppose it is working. URLs are not regular strings, remember? Substring matching is not the correct way to handle this logic.
blabla.php?a=b&c=d&dummy=foo.com/rss.php will happily match, even though it's not what you had in mind.
So the attacker also has a way to stop the caching mechanism. He may choose a "heavy" page, pad the URL with a dummy value, and then post it on slashdot, so that the failed caching will DOS the server.
Post Reply