HTTP Only cookies without PHP 5.2

by Matt Mecham on September 12, 2006

For a while, Microsoft have had a flag for cookies called ‘httponly’. This doesn’t sound particularly exciting, but it is a vital step forward for web application security.

This flag tells Internet Explorer to make this cookie ‘invisible’ to javascript (and other scripting languages) which means that an XSS attack will no longer be able to steal your sensitive cookies.

The problem is that ‘http only’ support has only just been added into PHP 5.2. This makes this feature unavailable to most webservers.

However, there appears to be a way to force this flag to be written regardless of your PHP version by simply adding “; HttpOnly” at the end of the domain name when setting the cookie. PHP’s “setcookie” function merely formats the data into a “set-cookie” header. Fortunately, PHP doesn’t appear to filter out or escape the semi-colon so it’s added to the end of the “set-cookie” request.

if ( PHP_VERSION < 5.2 )
{
  @setcookie( $name, $value, $expires, $path, $domain. '; HttpOnly' );
}
else
{
  @setcookie( $name, $value, $expires, $path, $domain, NULL, TRUE );
}

I've tested this out and it appears to work fine. IE7 shows the "sensitive" cookie data in the document.cookie string without adding the flag. Adding the flag onto the domain string causes the sensitive cookies to disappear from the document.cookie string.

Firefox ignores it and sets cookies as does Safari and Opera. I'll do some more testing and report in on my findings. I also have a Firefox friendly version to stop access to the document.cookie which I'll post up tomorrow.

UPDATE 14th September
I've downloaded the source to PHP 5 to make confirm that this 'hack' will work across different platforms. The source code confirms that no cleaning takes place on the domain name attribute (or indeed any other than the cookie name and value).

These snippets are from the head.c document, function php_set_cookie:

if (name && strpbrk(name, "=,; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
zend_error( E_WARNING, "Cookie names can not contain any of the folllowing '=,; \t\r\n\013\014' (%s)", name );
return FAILURE;
}

if (!url_encode && value && strpbrk(value, ",; \t\r\n\013\014") != NULL) { /* man isspace for \013 and \014 */
zend_error( E_WARNING, "Cookie values can not contain any of the folllowing ',; \t\r\n\013\014' (%s)", value );
return FAILURE;
}

That shows the check for the key and value:


if (path && path_len > 0) {
strcat(cookie, "; path=");
strcat(cookie, path);
}
if (domain && domain_len > 0) {
strcat(cookie, "; domain=");
strcat(cookie, domain);
}

That shows that no cleaning takes place and this 'hack' will execute perfectly.

{ 11 comments… read them below or add one }

1 Philip Withnall September 12, 2006 at 6:36 pm

Nice; I’ll be using this. :-)

2 Adam September 12, 2006 at 10:57 pm

Thanks Matt. I actually was recently just looking into something similar. I appreciate the info.

3 Kier Darby September 14, 2006 at 10:54 am

HttpOnly cookies are a great system that we considered should be more accessible to all web application developers.

When we integrated HttpOnly cookies into vBulletin 3.6.1 a few weeks ago, we were disappointed to see that PHP did not natively support them through the setcookie() function, so we submitted a patch to PHP, which was accepted.

Glad you appreciate our efforts! :)

4 Matt September 14, 2006 at 11:01 am

I know that it was Scott that wrote the little patch for PHP.

The great thing is that you don’t need it — or at very least, don’t need to wait for PHP 5.2 :D

I’m going to post up my Firefox version later today that you may want to consider using too.

5 Kier Darby September 14, 2006 at 11:42 am

In the absense of PHP 5.2 and its native HttpOnly support, we are actually rolling our own cookies using header() calls, as you may have seen. Your hack of the setcookie() function is nice, I’m surprised PHP doesn’t clean for the semi-colon, but there we go.

I’ll be interested to see how you’ve tackled the Firefox issue.

6 Matt September 14, 2006 at 11:51 am

I was surprised too. I can only assume they didn’t consider they needed to clean the domain name as no valid domain name allows for a semi-colon.

7 Matt September 14, 2006 at 11:53 am

This reminds me, it would be nice to be able to send custom fields to the setcookie() function like one can with mail() – it would future proof PHP against changes in cookie syntax.

8 Matt September 14, 2006 at 12:04 pm

I’ve updated my original blog post. I’ve had a dig around in the PHP source.

9 dgx December 6, 2006 at 4:14 am

Matt, you should use version_compare(). When PHP_VERSION is ‘5.2.1′, the comparsion PHP_VERSION < 5.2 returns FALSE.

10 dgx December 6, 2006 at 4:24 am

ups… my mistake. PHP_VERSION must be something like ‘5.10.1′ to see difference.

11 Jet April 29, 2007 at 11:41 am

@Matt: thanks for the tips, I just applied your suggestion to my IPB’s setcookie function, as it will take months to see PHP 5.2 coming.

Leave a Comment

Previous post: IPB 2.2 Update

Next post: HTTP Only cookies in Firefox