This follows up from my previous entry where I found a very easy way to set HttpOnly cookies for Internet Explorer 6 (SP1+) without requiring PHP 5.2 or hand-rolling set-cookie routines.
Due to reasons too long to explain here Firefox doesn’t have support for these cookies yet. However, there is a Firefox (well, Gecko) only solution.
The solution requires you to add this simple line of javascript as near to the top of your document as you can.
HTMLDocument.prototype.__defineGetter__("cookie",function (){return null;});
This overwrites the default cookie “getter” function and simply returns “null” when document.cookie is requested.
Unfortunately, it suffers from two major problems.
Firstly, it prevents any cookies from being read by javascript. This is a problem if you have javascript set and get cookies on the fly for dynamic web applications (read Web 2.0). and more fatally it can be undone simply by using:
delete HTMLDocument.prototype.cookie.
For example, if you managed to craft a link like so:
<a href='javascript:alert(document.cookie)'>Click me!</a>
Then you’d get an alert box with “null” thanks to the overridden cookie getter - however, crafting this link:
<a href='javascript:delete HTMLDocument.prototype.cookie; alert(document.cookie)'>Click me!</a>
Then you’d get access to the document.cookie as we’ve just deleted our custom getter.
The first problem is quite easy to overcome. You can set up an array of cookies that you want to ‘hide’ then parse up document.cookie storing the cookie values into an array and simply return the value of the array key when you would otherwise request a cookie.
The second problem is a little more difficult. After toying with it for a little while, I took a rather crude approach. I simply add an onload function that examines the document’s innerHTML and just replace “HTMLDocument.prototype” with a safer version (note, you can’t convert to hex or ascii entities as Firefox runs it just the same). There are two cavaets however.
Firstly, this will only stop “passive” attacks - that is scripting in URLs and images. It won’t stop a <script>..</script> block of code as the script code is executed before the onload function is called. Secondly, as the document’s innerHTML is replaced, all subsequent javascript on the page will stop working. I don’t consider that a major stumbling block as there aren’t many legitimate cases where you’d want someone to post “HTMLDocument.prototype”. If it is a problem, then I suggest you add a line of code in your PHP script to convert this string into something safer before javascript has a chance to see it.
Of course, this isn’t an excuse to stop checking for javascript in URLs and images - but it does provide another barrier a determined hacker must overcome before accessing your cookie information. It’s also not fool proof. There are hundreds of ways to obfuscate javascript code and it’s almost impossible to catch them all. Hopefully when Firefox 2 is out of BETA it’ll support HttpOnly cookies and deprecate this rather crude solution.
Here’s the complete code.
var COOKIES = new Array();
var uagent = navigator.userAgent.toLowerCase();
var is_moz = ( navigator.product == ‘Gecko’ );
var _tmp = document.cookie.split(’;');
if ( _tmp.length )
{
for( i = 0 ; i < _tmp.length ; i++ )
{
var _data = _tmp[i].split('=');
var _key = php_trim( _data[0] );
var _value = unescape( php_trim( _data[1] ) );
if ( _key && ( ! php_in_array( _key, ignore_cookies ) ) )
{
COOKIES[ _key ] = _value;
}
}
}
_tmp = null;
if ( is_moz )
{
HTMLDocument.prototype.__defineGetter__( "cookie", function () { return null; } );
window.addEventListener( 'load', function() {
var _a = document.body;
var _x = _a.innerHTML;
var _y = new RegExp( "HTMLDocument\.prototype", 'ig' );
if ( _x.match( _y ) )
{
_x = _x.replace( _y, 'HTMLDocument_prototype' );
_a.innerHTML = _x;
}
}, false );
}
/**
* Emulate PHPs trim function
*/
function php_trim( text )
{
text = text.replace(/^\s+/, '');
return text.replace(/\s+$/, '');
};
/**
* Emulate PHPs in_array function
*/
function php_in_array( needle, haystack )
{
if ( ! haystack.length )
{
return false;
}
for ( var i = 0 ; i < haystack.length ; i++)
{
if ( haystack[i] === needle )
{
return true;
}
}
return false;
};
function get_cookie( name )
{
return COOKIES[ name ];
}




Recent Comments