PHP 5.3 and Delayed Cross Site Request Forgeries/Hijacking

October 1st, 2008 | by Stefan Esser |

Although PHP 5.3 is still in alpha stage and certain features like the PHAR extension or the whole namespace support are still topics of endless discussions it already contains smaller changes that could improve the security of PHP applications a lot.

One of these small changes is the introduction of a new php ini directive called request_order. request_order is the response of the PHP developers to me preaching for years that using $_REQUEST is not only deprecated but actually dangerous for PHP applications. With request_order it is now possible to control in what order $_REQUEST is created and what variable sources are taken into account. This finally allows removing cookie data from $_REQUEST without removing them from $_COOKIE also.

Because removing cookies from $_REQUEST might break badly written software request_order is not set by default. However the recommended setting by the PHP developer is to set it to “GP” which means only $_GET and _POST data is merged into $_REQUEST with $_POST data overwriting $_GET data.

To learn why using $_REQUEST is a bad idea and what Delayed Cross Site Request Forgeries/Hijacking are continue reading…

$_REQUEST and Cookies

PHP application developers often use $_REQUEST instead of $_GET or $_POST because they do not care about the source of input. This has been considered bad practice for a while, because it is actually a violation of the idea behind GET and POST requests: GET is meant to retrieve data, POST is meant to manipulate data. And others have wrongly claimed that using POST instead of GET is a protection against CSRF. Nevertheless PHP application developers still use $_REQUEST a lot, because they cannot see the security threat in using it.

What most of them forget is that $_REQUEST does also contain $_COOKIE data and cookies unlike $_GET and $_POST data are shared among a (sub-)domain. So an application must actually expect that there might be cookies alien to the application. These cookies could be an attack on the application but aren’t necessary. However their presence in $_REQUEST results in application input filters going crazy or they might influence the application logic.

Therefore it is important to remember that cookies might be legal cookies of another application on the same host. And it is also important to remember that some browsers like Firefox 2 does allow an application to set cookies valid for a whole country if the domain is like *.co.uk or *.co.kr. While this allows cross domain user tracking, it also means any application in the country could set a cookie that is seen by your application and considered an attack.

Cookie Denial of Service (DOS)

Taking this into account it should be obvious that any application blocking requests because of some value appearing in $_COOKIE or $_REQUEST is vulnerable to denial of service. Once an attacker is able to get a cookie into the browser of a user, which is possible through XSS in one application on the same domain or cross domain vulnerabilities in browsers, it will trigger the request blocking in the application with every request. The user will not be able to use the application anymore unless the cookie expires or he manually deletes it. However the average user does not have a clue how to delete a cookie, which means he will be denied access forever.

NOTE: Due to PHP’s way of ignoring trailing spaces in variable names and because cookies might be set (sub-)domain wide or path wide, it is not possible for the application to kill the cookie by issuing a simple Set-Cookie HTTP header.

Many open source PHP applications are vulnerable to Cookie Denial of Service because they introduced code similar to the following example to protect against PHP’s GLOBALS overwrite problem.

if (isset($_REQUEST['GLOBALS'])) {
   die("GLOBALS Overwrite attack attempt!!!");
}

Because of this it is possible to deny a user access to a lot of sites in Korea or the UK by just creating a GLOBALS=1 cookie for *.co.kr/*.co.uk in older Firefox versions. Another attack that hits many PHP applications is creating a cookie called action=logout.

Delayed Cross Site Request Forgeries/Hijacking

When $_REQUEST is used by an application to react on user input instead of just $_GET or $_POST an attacker can inject certain values or actions into an application through injected cookie data instead of just killing it with a Cookie DOS. This attack is similar to a Cross Site Request Forgery with the difference that it actually hijacks a user request instead of simply forging it and that it happens with a delay: After the cookie is injected the CSRF is executed the next time the user uses the site.

Because the actual attack relies on hijacking an actual user request that is abused by manipulating by adding or modifying some input variables people might prefer to call it Cross Site Request Hijacking. It is important to realise that a delayed hijacking is different from just forging the request. This also means that traditional CSRF protections like form tokens or asking the user for his password are ineffective against this kind of attack. Because the attack just changes some of the variables within a request the secret form tokens and/or user submitted passwords of the original request will be transmitted correctly.

A vulnerability like this was found and disclosed in phpMyAdmin a while ago. Within phpMyAdmin it was possible to inject a cookie that would execute arbitrary SQL statements on the server the next the user logged into his phpMyAdmin account.

Another incarnation of this vulnerability class was found within a form software where the admin configuration was handled by code similar to this.

   // save only modified admin options
   // BEWARE THIS CODE IS VULNERABLE
   foreach ($_REQUEST[‘options‘] as $key => $val) {
      if (isset($options[$key]) && $options[$key] != $val) {
         saveOption($key, $val);
      }
   }

In this example an attacker that is able to inject a cookie titled options[includePath] into the admin’s browser will be able to remotely include arbitrary PHP code. Because the attack relies on Delayed Cross Site Request Forgery/Hijacking the attack is even possible when the admin is not currently logged into the administrative interface. The payload sleeps and will become effective once the admin tries to change the forum configuration the next time, although there is a CSRF protection in place.

Conclusion

Once PHP 5.3 is out it is recommended for hosters to set request_order to “GP” on all the servers running arbitrary PHP applications to protect applications from this kind of Cookie DOS or Delayed Cross Site Request Forgery/Hijacking vulnerabilities.

PHP Application developers on the other hand should finally move away from using $_REQUEST for user input, because it is cleaner and more secure.

  1. 14 Responses to “PHP 5.3 and Delayed Cross Site Request Forgeries/Hijacking”

  2. By kuza55 on Oct 1, 2008 | Reply

    Yeah, I found this waaay back in 2006: http://kuza55.blogspot.com/2006/03/request-variable-fixation.html but no-one seems to have noticed or cared…

    Though I noticed that you could use variables_order to do the same as request-order, and I don’t really see the point of having two, since variables_order would also protect apps which rely on register_globals.

  3. By kuza55 on Oct 1, 2008 | Reply

    Oh, also, thanks for the tip about whitespace and php vars!

    And Safari also allows you to inject cookies into .co.uk and .co.kr, (afaik) even in the most recent releases.

  4. By Stefan Esser on Oct 1, 2008 | Reply

    @kuza55:

    ehmm if you remove C(ookie) from variables_order the $_COOKIE array will not get populated with cookie data. Therefore a new setting was needed.

    BTW: I have no idea when I spoke about _REQUEST for the first time. It also must be several years ago when I gave a talk at the local PHP usergroup about it. But who cares. Most probably someone else noticed the same thing way back in 1999.
    It is like this recently hyped “insecure cookie” nonsense. The media hypes these vulnerabilities as NEW when someone like google was hit, but in reality that session identifiers for ssl sites are sniffable when no secure flag is set is know for like EVER….

  5. By Greg Beaver on Oct 1, 2008 | Reply

    Stefan,

    Thanks for this series of blog posts, your explanation of security issues and how to address them (and how not to) is extremely clear, and is a great service to the community.

  6. By rvdh on Oct 1, 2008 | Reply

    I knew it since around 2001 :) Yeah Stefan I know you wrote about it quite some time ago, I thought I read it in a slide from you. On the other hand, no-one seems to talk -and know- about the GLOBALS issue, it’s old but people still use it. Like: index.php?GLOBALS[foo]=manchu which again can be set from REQUEST, GET, COOKIE, and SESSION. Probably because it was quite hot some odd years ago and no-one bothered to see if people knew it today.

    it’s good that you keep programmers informed, it’s desparetly needed today

    ;)

  7. By kuza55 on Oct 2, 2008 | Reply

    @Stefan:
    Ah, ok, thanks.

    Hehe, no-ones cares, but I remember you saying much the same when I posted something, so I thought I might as well :p

  8. By Dudley teeh Dog on Oct 2, 2008 | Reply

    Good post, Stefan. Of course, I knew about this way back in 1982 - must have been a vision of the future inspired by another kind of cookie.

  9. By donwalrus on Oct 9, 2008 | Reply

    It is dumbfounding to see developers still using $_REQUEST after all these years. Developers in ASP still use “Request” instead of “Request.Form” and “Request.Querystring” as well, resulting in numerous user-manipulation errors.

  10. By ire on Oct 20, 2008 | Reply

    Thanks Stefan,
    talking about the php.ini configuration file, do you still have plans to do an article on your recommended best practices? I know it sounds very basic but I am certain too many developers would be grateful if you have such an article available even if it does not cover all the directives.

  11. By Stefan Esser on Oct 20, 2008 | Reply

    @ire:

    I will publish such an article soon. At the moment I am still busy creating new slides for several new talks.

  12. By Bong Bong on Dec 12, 2008 | Reply

    I’ve adopted to and recommend using a wrapping function. In this function i check $_SERVER['REQUEST_METHOD'] and when it’s either POST or GET, i return the requested value, optionally escaped for dynamic database queries and/or converted to a specific data type and/or html encoded. For requests, that are bound to come from either GET or POST i ignore the request method and check for the bound method. My wrapper returns false on error and all calling code treats this as an error.

    I use cookies to store the session id. The cookie is valid for the browser session. Everything else is stored on the server, where i can control the what, where and how.

    If possible, i use whitelisting for user input.

    I think this is a pretty robust solution. I’ve had several people deliberatly trying to break it and none did.

  1. 3 Trackback(s)

  2. Oct 2, 2008: Stefan Esser’s Blog: PHP 5.3 and Delayed Cross Site Request Forgeries/Hijacking : Dragonfly Networks
  3. Oct 3, 2008: vivanno.com::aggregator » Archive » request_order arrive en PHP 5.3
  4. Oct 6, 2008: Objekte und Sessions - php.de

Post a Comment