What is BREACH? How can we protect Django websites from it?

Wed 07 August 2013 by Matthew Scott

Summary

BREACH is a side-channel attack that can retrieve secrets, such as CSRF tokens, that the attacker can later use to compromise a site’s security. The setup required for a successful breach typically involves physical access to the victim’s network.

All Django projects that make use of GZip-compressed HTTPS, and make available certain kinds of CSRF-protected views (such as the admin login view), are vulnerable to the attack.

Several mitigation techniques against the attack are known, some working more effectively than others.

How the attack works

This section offers an overview. See the original BREACH paper for more detail.

BREACH is a side-channel attack on web apps served via GZip-compressed HTTP served via HTTPS. The attacker gains access to a secret such as a CSRF token by comparing responses for hundreds or thousands of artificially-generated requests.

The required ingredients for the attack itself are HTTP-level compression (e.g. GZip), web app reflecting user input in HTTP response bodies, and web app reflecting a secret such as a CSRF token in those same HTTP response bodies. Fewer artificial requests are needed when the length of the response is based on user input.

The attacker must be able to capture HTTPS traffic from the victim, while also being able to control the victim’s browser to make several requests to the target server. The easiest way for this to occur is for the attacker to have access to the victim’s network router, both to capture traffic and to modify port 80 traffic to include a hidden iframe to create requests.

BREACH is similar to the CRIME attack. CRIME relies on inspection of request headers when using TLS/SSL-level compression, which was already little-used and disabled in most browsers. BREACH, on the other hand, is the application of the same technique but on GZip-encoded responses.

Vulnerability in Django projects

An example of a vulnerable view would be one that accepted user input in a CSRF-protected form, and presented an “incomplete form” message with the user’s input pre-populated in an input field. Each artificial request would present a guess, or “canary”, as input. When there exists a partial or complete match between the canary and the CSRF token, compression artifacts can be used to infer such a match without having to decipher the traffic.

Many views in a Django project are susceptible to this attack, and some are not as easily controlled as others. For instance, the default Django admin login view, when given a nonexistent username, contains the CSRF middleware token and the username that was entered.

Mitigation

The original paper mentions several techniques, some of which I’ll list here with reference to where they appear in the original paper. These techniques purportedly prevent the attack:

  • 3.3 Disable GZip compression: This has a performance impact on the amount of time it takes to render a web page completely.
  • 3.4 Masking secrets: Instead of embedding same CSRF token each time, mask it with a one-time pad to avoid compressing it with the user input.
  • 3.2 Transfer secrets and user input in separate compression contexts: Most easily done via two separate requests. This is not feasible until Django has a systematic approach to delivering all CSRF tokens and other secrets in a different manner.

These techniques serve to slow attacks but may not prevent a persistent attacker:

  • 3.1 Length-hiding: Add random amount of garbage data to all CSRF-protected responses reflecting user input.
  • 3.5 Per-user rate limiting: Rate-limit all CSRF-protected responses that reflect user input.

Django

The Django team published a security advisory recommending disablement of GZip compression as the foremost immediate mitigation technique.

You can disable Django’s GZip compression by making sure GZipMiddleware is not installed.

@lpomfrey introduced django-debreach which provides an implementation of mitigation techniques 3.4 (masking secrets) and 3.1 (length-hiding).

django-brake lets you rate-limit certain requests.

Apache

To disable GZip compression, disable mod_deflate.

There are no built-in Apache modules for request rate limiting. The third-party modules mod_evasive and mod_cband can be used to limit request rates from scripted attacks. (via How can I implement rate limiting with Apache? (requests per second))

Nginx

To disable GZip compression, disable the gzip module,

To enable request limiting, configure the limit_req module.

Further notes

A conversation with @tobych brought up an important distinction: BREACH relies in the vulnerability of the victim’s network devices, not the network where your app is hosted.


Comments

Do you like podcasts? Check out Fanscribed. It helps podcasts use crowdsourcing to create high-quality transcripts.

Fanscribed is created and maintained by Elevencraft Inc.


I'm Matthew Scott, and my company Elevencraft Inc. exists to help you wisely use tech to solve problems:

  • "Full stack" development
  • Pair-programming
  • Python and Django support
  • Modern dev tools, like git

Ready to find out how we can work together? Email me at matthew@11craft.com


Elevencraft Inc.
Springfield, MO, USA
matthew@11craft.com
+1 360 389-2512