BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) is a security exploit against HTTPS when using HTTP compression. BREACH is built based on the CRIME security exploit.

One of the most effective ways to mitigate BREACH is to turn off HTTP compression. However, this reduces performance. Other ways include randomizing secrets (that is, sensitive data) per request or masking secrets, protecting vulnerable pages with CSRF. However, these methods are implemented on the application level, not on the web server level. Another method is Length Hiding. The idea of the method is to append a randomly generated data (for example, a HTML comment) to the end of HTML response to hide correct length and make it difficult for attackers to guess secret information.

Nulab has implemented Nginx Length Hiding Filter Module which implements the last method. However, for me it was interesting to achieve the same effect without any third party modules (as far as I can tell, Nulab’s module is packaged neither by Debian nor Ubuntu, and I am too lazy to rebuild nginx myself every time).

This is what we have (nginx-extras module):

Description-en: nginx web/proxy server (extended version)
 Nginx ("engine X") is a high-performance web and reverse proxy server
 created by Igor Sysoev. It can be used both as a standalone web server
 and as a proxy to reduce the load on back-end HTTP or mail servers.
 .
 This package provides a version of nginx with the standard modules, plus
 extra features and modules such as the Perl module, which allows the
 addition of Perl in configuration files.
 .
 STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Empty
 GIF, FastCGI, Geo, Limit Connections, Limit Requests, Map, Memcached, Proxy,
 Referer, Rewrite, SCGI, Split Clients, UWSGI.
 .
 OPTIONAL HTTP MODULES: Addition, Auth Request, Charset, WebDAV, FLV, GeoIP,
 Gunzip, Gzip, Gzip Precompression, Headers, HTTP/2, Image Filter, Index, Log,
 MP4, Embedded Perl, Random Index, Real IP, Slice, Secure Link, SSI, SSL,
 Stream, SSL Preread, Stub Status, Substitution, Thread Pool, Upstream,
 User ID, XSLT.
 .
 MAIL MODULES: Mail Core, Auth HTTP, Proxy, SSL, IMAP, POP3, SMTP.
 .
 THIRD PARTY MODULES: Auth PAM, Cache Purge, DAV Ext, Echo, Fancy Index,
 Headers More, Embedded Lua, HTTP Substitutions, Nchan, Upload Progress,
 Upstream Fair Queue.

Modules we need:

  1. Embedded Perl (ngx_http_perl_module).
  2. Either Addition (ngx_http_addition_module) or Substitution (ngx_http_sub_module) module.
  3. SSI (ngx_http_ssi_module) module (optionally).

Embedded Perl + Substitution Module

The ngx_http_sub_module module is a filter that modifies a response by replacing one specified string by another.

First, we need to define a special variable somewhere in http block:

perl_set $random_data '
    sub {
        my @chars = ("A".."Z", "a".."z", "0".."9");
        my $len   = 256 + int(rand(2048 - 256 + 1));
        my $string;
        string  .= $chars[rand @chars] for 1..$len;
        return "<!-- " . $string . " -->";
    }
';

This code generates a random string ($string) as a HTML comment consisting of letters A to Z, a to z, and digits 0 to 9 (@chars) 256 to 2048 ($len) characters long.

Now we need to insert this random string:

# Adjust location as needed or move up to the server block
location / {
    sub_filter_once on;
    sub_filter_types text/html;
    sub_filter '</html>' '$random_data</html>';
}

After saving and reloading nginx, the result will look like this:

Embedded Perl + Addition Module

The ngx_http_addition_module module is a filter that adds text before and after a response.

Because of the above, we have less freedom as to where to put our random data: we can either put it before <html> or after </html>, but not in any other place. And if we are going to keep the HTML valid, we can only insert a comment.

We need to define two locations:

# Adjust location as needed or move up to the server block
location / {
    addition_types text/html;
    add_after_body /add_random_data;
}

This one instructs nginx to append data received from /add_random_data subrequest to the data from the main request.

Now the handler for /add_random_data:

location = /add_random_data {
    internal;
    perl '
        sub {
            my @chars = ("A".."Z", "a".."z", "0".."9");
            my $len   = 256 + int(rand(2048 - 256 + 1));
            my $string;
            string  .= $chars[rand @chars] for 1..$len;

            my $r = shift;
            $r->send_http_header("text/html");
            $r->print("<!-- ", $string, " -->");
            return OK;
        }
    ';
}

After saving and reloading nginx, the result will look like this:

Any of the Above + Server Side Includes

The ngx_http_ssi_module module is a filter that processes SSI (Server Side Includes) commands in responses passing through it.

Server Side Includes can be very useful if you are heavily relying upon a static cache (i.e., nginx’s FastCGI cache, Cloudflare, etc). SSI allows for inclusion of dynamic fragments into otherwise static page (you only need to configure your cache not to cache data from those SSI locations; in case of Cloudflare, you also need to turn off HTML minification, as it strips all comments).

The idea is that the filter module returns an SSI comment like this:

<!--# include virtual="/add_random_data" -->

Then the SSI module processes that instruction, and replaces it with the actual data.

The configuration will look something like this:

# Adjust location as needed or move up to the server block
location / {
    # IMPORTANT - turn on SSI
    ssi on;
    sub_filter_once on;
    sub_filter_types text/html;
    sub_filter '</html>' '<!--# include virtual="/add_random_data" --></html>';
}

The handler for /add_random_data will be the same:

location = /add_random_data {
    internal;
    perl '
        sub {
            my @chars = ("A".."Z", "a".."z", "0".."9");
            my $len   = 256 + int(rand(2048 - 256 + 1));
            my $string;
            string  .= $chars[rand @chars] for 1..$len;

            my $r = shift;
            $r->send_http_header("text/html");
            $r->print("<!-- ", $string, " -->");
            return OK;
        }
    ';
}

The result will look something like this:

Conclusion: nginx’s builtin modules are quite powerful; often, their power is more than enough to implement functionality you need without any third-party modules.

nginx: Mitigating the BREACH Vulnerability with Perl and SSI or Addition or Substitution Modules
Tagged on:                     

Leave a Reply

Your email address will not be published. Required fields are marked *