First of all, thanks for for getting this out there.
The Length Hiding piece protects all content, but only really slows the attack (as you mention!).
The problem is this implementation breaks Rails' Conditional GET (Actually Rack's) -- which returns 304 Not Modified if the content is the same. Now that the content varies, normal usage will get a 200 and the full response every time.
It depends on the Application, but in most cases this will be a reasonable increase in overall bandwidth (for normal use, not the attack). Probably more of an increase than you'd encounter by turning off gzip - which completely defeats the attack rather than just making it more difficult, as Length Hiding does.
Edit: Actually another workaround might be to put your Middleware after the relevant middleware Rack::ConditionalGet, Rack::ETag. Right now it's at the top of the Middleware chain.
Actually, it's a harder workaround than I was suggesting. When the ETag isn't explictly set, the full body of the middleware chain is generated every single time, irrespective of the Conditional Get.
You want the ETag generated without the Length Hiding, which isn't the way Rack::ETag works. It digests the whole content - it's implementation calls upstream first, so it effectively ensures it's always at the end of the middleware chain.
You'd need to extend/replace/otherwise hook into Rack::ETag, which is a bit more surgical.
Wonder how easy it would be to turn off gzip on just potential attacks. It takes a lot of requests to execute the attack after all. So an example where each user only got their response gzipped the first time for any URL would be safe. But then the site would slow down for them later. So you'd have to pick some sort of reset metric that prevented efficient attacks, but still gave users compressed data most of the time.
Another piece of low hanging fruit would be to change the session cookie every request. It isn't a big deal for most servers to update the session ID and send a cookie down each request. Meanwhile it would hurt attacks going after the session ID by methods like this that need a lot of requests with the same value a lot.
" Setting the X-Frame-Options header can make it harder for an attacker to carry out this attack (by making it impossible to put your site in an iframe). "
Even with X-Frame-Options, the browser still makes a request (else, how can it see the X-Frame-Options header ?). X-Frame-Options only prevents display and execution, not requests.
The random length padding could perhaps be added to the compressed payload rather than the source document. Sensible decoders should ignore the padding, but it may depend on the decompression code and requires thourough testing.
Another option is to append a random length HTTP trailer header, sending the response in chunked mode. However, the spec says that you can only use chunked mode when the request specifies that the client supports it, and I don't have any idea of the browser support for said mode (you could refuse to serve content to clients that don't support chunked mode).
The second option can also be used to slow down CRIME if the payload is not compressed (beside TLS or HTTP 2.0 compression), or can't be padded for some reason.
The main drawback of chunked mode is that the client doesn't get to know the file length in advance.
These methods could be implemented at the reverse proxy level rather than the app level. That way, the Rails conditional GET would still work.
Example of a chunked transfer response, with a trailer:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Trailer: My-Test-Trailer
D\r\n
All your base\r\n
B\r\n
are belong\r\n
6\r\n
to us\r\n
0\r\n
My-Test-Trailer: something\r\n
\r\n
I'm not sure - but if you're concerned* you can turn off gzip compression for HTML. At least until a longer term fix is available. Turning off gzip/compression negates the attack irrespective of the framework.
* it's probably right to be concerned, but worth keeping in mind that this attack still requires a fair bit of access (to your traffic), planning (working out the vulnerable data) and intent.
I'm a bit confused.... In the mitigation section, the paper states: "However, we remark that requiring a valid CSRF token for all requests that reflect user input would defeat the attack."
Isn't that the point of CSRF? That's the advice Django provides anyway...
It seems like a pretty impractical mitigation to me. For one thing, it would make it very difficult to share links; a link containing my CSRF token wouldn't work for you. It would also make it much easier to give up your CSRF token. We would need to retrain people to think of the URLs they're viewing as personal and secret.
There is a way that the CSRF attack could be prevented. And that is if only every other byte of the CSRF token was part of the token, and the others were random every time.
In that case it would be impossible to predict common text in the CSRF based on past compression rounds.
Nobody did, but that is how life goes. An attack is found. A defense is found. And you can't build a secure system without knowing all of the attacks and corresponding defenses.
i wouldn't call it a proper defense, rather a work around. besides CSRF tokens there are lots of secret strings, and it's not website's problem to avoid wiretapping
Yep. I'm sending a patch for the CSRF token masking change shortly. I'm less sure about the length hiding change; it feels like an ugly hack, but we put it in because it's the least awful solution that the paper presents. I'm curious to find out exactly how effective the length hiding is one the PoC code is released.
Does anyone know, or seen info about whether enforcing client certificates mitigates/makes easier/harder the use of BEAST/CRIME/BREACH against servers?
I don't think they'd be much help. Even with client certificates, information will still leak via compression, and so an attacker will still be able to get CSRF tokens and then use them then forge requests via the user's own browser.
The Length Hiding piece protects all content, but only really slows the attack (as you mention!).
The problem is this implementation breaks Rails' Conditional GET (Actually Rack's) -- which returns 304 Not Modified if the content is the same. Now that the content varies, normal usage will get a 200 and the full response every time.
It depends on the Application, but in most cases this will be a reasonable increase in overall bandwidth (for normal use, not the attack). Probably more of an increase than you'd encounter by turning off gzip - which completely defeats the attack rather than just making it more difficult, as Length Hiding does.
The workaround is to use ETags (See: http://api.rubyonrails.org/classes/ActionController/Conditio...), which will use the tag rather than the content to determine 304s. I'd suggest this should be recommended if you're using Length Hiding.
Edit: Actually another workaround might be to put your Middleware after the relevant middleware Rack::ConditionalGet, Rack::ETag. Right now it's at the top of the Middleware chain.