Updated: Comment #N

Problem/Motivation

Moving this into a public issue form the security team tracker, since the attack is a general one on web servers, and not specific to Drupal, and the information is already public.

Django made a security announcement regarding the BREACH attack:
https://www.djangoproject.com/weblog/2013/aug/06/breach-and-django/
Their approach of suggesting you turn off gzip/deflate seems infeasible for most people.

There's more information here: http://breachattack.com/

Drupal is less vulnerable than many web applications, since the CSRF token is usually different per form.

However, if a sensitive or administrative form is on the same page with a form without CSRF protection (e.g. a search form that takes values from the query string), it's possible to be vulnerable to this attack. You could also be vulnerable if there is other sensitive data on a page that includes such a form.

Also, this attack is more effective against a stream cipher like rc4, which many people witched to using due to the BEAST attack.

Proposed resolution

Possible suggested BREACH mitigations:

  1. Disabling HTTP compression
  2. Separating secrets from user input
  3. Randomizing secrets per request
  4. Masking secrets (effectively randomizing by XORing with a random secret per request)
  5. Protecting vulnerable pages with CSRF
  6. Length hiding (by adding random number of bytes to the responses)
  7. Rate-limiting the requests

#1, #2, and #3 don't seem very feasible.

#4 could be pretty trivially implemented by essentially splitting the form token into 2 parts - a secret that's constant (essentially what we send as the token today), and a XOR mask that varies on every request. The the valid token check would XOR the 2 parts and compare to the expected secret.

#5 We already protect most forms and actions against CSRF, so Drupal start out from a relatively strong position against this attack already.

#6 should be easy to implement as well:
At least for BREACH and possibly for the vulnerabilities described at http://www.isg.rhul.ac.uk/tls/ for rc4, a possible counter-measure would be injecting a random number of random bytes as a header, and also injecting a random number of random bytes into the HTML content (e.g. in the HEAD section)

#7 seems more suitable to a WAF or load balancer outside Drupal.

Remaining tasks

pick 1 or more aproaches and implement

User interface changes

N/A

API changes

Possible changes to token generation/checking, but these could be transparent at the API level

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pwolanin’s picture

Priority: Normal » Critical
Status: Active » Needs work
FileSize
1.59 KB

Here's totally untested code, to show what I have in mind re: #4 above.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
2.04 KB

A few tweak - this seems to work to allow me to submit a form with proper token checking. Let's see if it breaks any core tests.

pwolanin’s picture

Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 2: 2234243-2.patch, failed testing.

catch’s picture

http://drupal.og/project/cacheable_csrf does #2 to an extent. The token isn't included in the HTML markup, but is injected via JavaScript based on the value of a cookie. So there's no reflection which a quick read of that doc suggests is what this attack relies on. Obviously the cookie is there, and the server still has to set it when it's not available.

pwolanin’s picture

I'm not sure if we are worried about the cost of potentially multiple random bytes calls per page? I'm not sure we've profiled that with the openssl default.

If it is a worry we could probably make do with something much less random but that still changed every page, like a hash of mt_rand() and microtime().

grendzy’s picture

Here are the possible resolutions as I see it. Since I somehow managed to reach opposite conclusions from the current issue summary, I'll leave this chart in a comment until we are closer to consensus.

countermeasure feasibility effectiveness notes
Disabling HTTP compression high high Easy, and effective. It's unfortunate to give up the bandwidth savings, but it's been known since at least 2002 that encryption and compression don't mix well. Drupal core does not gzip authenticated pages anyway, so we could almost mark this "works as designed". We may be able to prevent downstream intermediaries from gzipping by using the Cache-control: no-transform header. Honoring no-transform is a MUST in the HTTP RFC, but it's real-world effectiveness needs more research. Sadly, the first program checked (Apache httpd/mod_deflate) fails.
Separating secrets from user input no n/a Core cannot know, in advance, what data is secret. There are a few universal secrets like e-mail address and CSRF tokens, but in principle any part of the HTML document may contain confidential information. As a result, the solution most protect the entire HTTP body.
Randomizing secrets per request no n/a same as "separating secrets"
Masking secrets (effectively randomizing by XORing with a random secret per request) no n/a same as "separating secrets"
Protecting vulnerable pages with CSRF no n/a Even simple GET requests would require CSRF protection. This suggestion from breachattack.com is not the same as protecting forms and actions from CSRF. The requirement for this solution to be viable is to prevent all forged requests that reflect user input. Simple example:
HTTP GET https://victim.com/user/1/edit?chosen-plaintext=aaaa@gmail.com
Repeated requests to this URL (forged via <img> tag on 3rd-party site) would result in the attacker stealing the admin's e-mail address. Clearly we cannot require CSRF tokens on simple GET requests as this would break basic hyperlinking on a the web.
Length hiding (by adding random number of bytes to the responses) high low Can delay, but not prevent an attack, because even random padding will reveal a statistical bias.
Rate-limiting the requests low low hard to choose a limit high "enough for anybody", yet low enough to be effective. Proxies and CDNs can cause false positives. Fundamentally a task better suited for WAFs.

 

Attached is a patch for the first option with no-transform.

grendzy’s picture

Status: Needs work » Needs review

The last submitted patch, 1: 2234243-1.patch, failed testing.

pwolanin’s picture

@grendzy - so the original document discusses CRSF tokens as the sort of common that this attack would target.

Do you think the approach in the patch for masking the token is infeasible or wrong, or you just don't think it's that useful?

grendzy’s picture

Both I guess - not useful because CSRF tokens are only a small fraction of the secrets that need protecting. UID 1's e-mail is one example above, another example would be private user profile fields. On top of that prior to #1798296: Integrate CSRF link token directly into routing system we don't even have a central way to protect CSRF tokens. Discussing tokens is a good way to illustrate the BREACH vulnerability (they are nearly universal to all web apps, and secondly it's a very high-value secret).

Also wrong, in the sense that if the transport security is broken, then a fix must address it at that layer, rather than accepting broken transport encryption and masking a single high-value secret.

I admit the no-transform header idea may turn out to be weak, if it's not well-supported by CDNs and load balancers in the wild (this still needs research). Another layer we could add would be a status report check in hook_requirements(), or https://drupal.org/project/security_review (the check would generate a warning if an intermediary was detected gzipping SSL pages).

pwolanin’s picture

I had thought of the no-transform header as applying to encoding rather than compression.

If you don't think it's worth specifically masking CSRF tokens, then i think length hiding by adding random byes to both the header and body are worth doing.

They are low cost, and making any such attack 100x harder (or some significant factor) is useful.

grendzy’s picture

Compression is a a kind of encoding, yes? (Compressed responses are labeled with Content-Encoding: gzip) And I think the RFC is pretty clear that no-transform prohibits changes to the body, and the Content-Encoding header:

Therefore, if a message includes the no-transform directive, an
intermediate cache or proxy MUST NOT change those headers that are
listed in section 13.5.2 as being subject to the no-transform
directive. This implies that the cache or proxy MUST NOT change
any aspect of the entity-body that is specified by these headers,
including the value of the entity-body itself.

For length-hiding, I'm inclined to agree that the cost / benefit ratio is pretty good. My stats are terribly rusty, so take this with a grain of salt, but here's a estimate of sample size needed given 0-128 of padding bytes, and a 10-byte secret:

n = 2 ((sigma erf^(-1)(c))/M)^2 | sigma = (standard deviation UniformDistribution[{0, 128}]), c = 0.5**(1/10), M = 0.5

18,327 samples are predicted per length observation, assuming 32 observations per byte, and 10 bytes in the secret means almost 6 million observations needed to "breach" the secret. Depending on how fast your Drupal backend is, this could take days or weeks (and proportionally more for longer secrets). This isn't a crypto-scale amount of time, but better than nothing, especially considering the victim needs to have a malicious site open for the duration.

Can padding be confined to the header? Padding an HTML body is pretty safe, but not so with all media types (XML, JSON, etc). I would expect an attacker can only see the overall message size, and not the boundary between header and body, but I'm not sure.

Last, we can document on https://drupal.org/https-information that site operators who want the strongest protection from BREACH should refrain from enabling gzip on SSL pages.

catch’s picture

This looks like something we could add to 8.0.1 - is there a particular reason it should block the release?

grendzy’s picture

Priority: Critical » Major

I agree it should not be a release blocker.

pwolanin’s picture

Not a release blocker, but nice to have.

alansaviolobo’s picture

pwolanin’s picture

Status: Needs review » Needs work

Given that there are pushes to move all traffic to https, I don't think disabling compression for https is a good move.

pwolanin’s picture

Since this attack is on the body, I'm not sure variable content in the header would be effective - it might? not clear.

Given that the attack requires getting user input into the response, it seems like JSON and XML responses are a lower priority.

I suggest random padding of the header and random padding of html bodies when the request is SSL would be easy to implement, and would probably mitigate a range of yet-undiscovered attacks in addition.

grendzy’s picture

pwolanin: "pushes to move all traffic to https" - to me, this is an argument in favor of disabling compression; as TLS becomes more widely used, it becomes even more important to have strong assurances of it's safety.
Interestingly EFF mentions CRIME, but not BREACH: https://www.eff.org/https-everywhere/deploying-https

Anyway, I would be happy to review a length-hiding patch. To the best of my knowledge, TLS operates 3 layers below HTTP, so it's not even aware of the header / body boundary, which is why I think header padding would have the intended effect, with a lower risk of side-effects.

pwolanin’s picture

The only reason to suggest adding it to the body also is that the content of the padding (as well as its size) would potentially affect the gzip routine (if used) and further thwart this attack.

Also - we should probably always pad (not try to check for SSL) in case Drupal is being served from a back-end behind a SSL reverse proxy?

grendzy’s picture

I think it's OK to rely on \Drupal::request()->isSecure(), proxy users still have to configure this accurately (via X-Forwarded-Proto or otherwise). If this isn't set, the session handler won't function correctly anyway.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

pwolanin’s picture

Version: 8.1.x-dev » 8.2.x-dev
pwolanin’s picture

I wasn't sure if Drupal meets the requirement of reflecting user input in the response, but it seems every FORM tag does via the action attribute like:
<form action="/node/1/edit?canary=1"

pwolanin’s picture

Ah, however - see: http://www.breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030...

Because Drupal FAPI CSRF tokens are in value attributes as shown in 2.4.7 those at least are not really vulnerable.

So the the risk is more to CSRF tokens in GET requests rendered as links.

grendzy’s picture

FileSize
328.28 KB

We do have modules like https://www.drupal.org/project/prepopulate that allow filling the value attribute. Even without a way to force a value attribute, at that point you're relying on a single quote mark, and the attacker's inability to guess the first three characters of whatever data is being attacked, which seems pretty flimsy. (Lots of private data have predictable prefixes, for example: Social Security Number Prefixes, Area codes, Health insurance account numbers, etc.

I also think focusing on the form tokens is a way to narrow view. In principle any private data on the page could be attacked. (For another example, see my comment in #7 -- the admin's email address could be leaked with e.g. https://victim.com/user/1/edit?chosen-plaintext=aaaa@gmail.com).

Below is a screenshot from a real Drupal site, showing how a little OSINT could be combined with BREACH to steal health insurance information.

screenshot

grendzy’s picture

Because Drupal core cannot anticipate the nature of the data present on a page, the only effective solution I can see is to disable compression for non-cached/non-public pages over HTTPS. (Which was already the case in core last I checked, though we might be able to take steps to prevent proxies & CDNs from adding compression, or possibly a system status warning if compression is detected).

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

rreiss’s picture

Hi,

Any thoughts about the same BREACH vulnerability with theme_token on D7?
Disabling HTTP compression will be a performance hit, so this isn't the best solution IMHO.

We ended up removing (unset) theme_token from our default theme, since we have no AJAX requests
with Drupal AJAX APIs.
That token seems a little bit odd to me, why not just checking a permission to the administrative theme / add per theme permission and check that permission instead? Weird solution, isn't it?

John Morahan’s picture

Ivan Ristić's book suggests another possible mitigation that I don't think has been considered: conditionally disable compression when the request doesn't have a same-origin Referer. It's similar to the CSRF token idea mentioned in #7, but without the tokens - and the only penalty for not sending a Referer at all is that you get an uncompressed response.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

semiaddict’s picture

The solution indicated in #33 is also suggested and detailed on https://wiki.crashtest-security.com/prevent-ssl-breach

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

prudloff’s picture

Status: Needs work » Needs review
FileSize
1.62 KB

The attached patch returns a Cache-Control: no-transform header if the request uses HTTPS and does not have a trusted Referer header.

We are using Varnish and this feels like the best compromise between security and performances.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
FileSize
151 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.