2

I have Nginx 1.10 with ssl_verify_client = on. Everything works fine, except that the server finishes TLS handshake and proceeds to parse the headers even though the certificate hasn't been sent by the client.

It can be verified by sending http request without client certificate and extremely big http header, nginx returns "400 Request Header Or Cookie Too Large".

According to our security auditor, the server should fail the TLS handshake, because parsing the headers "increases the possible attack surface.

My nginx config:

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    server_name myserver.com;

    ssl_certificate /etc/letsencrypt/live/myserver.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myserver.com/privkey.pem;

    ssl_verify_client on;
    ssl_client_certificate /etc/nginx/ssl/ca.pem;

    location / {
            proxy_pass       http://127.0.0.1:8001;
            proxy_set_header Host      $host;
            proxy_set_header X-Real-IP $remote_addr;
    } }
2
  • Could you add your nginx config to the question? Apr 25, 2017 at 18:52
  • Sure, good point.
    – jhexp
    Apr 26, 2017 at 7:51

1 Answer 1

1

I think you need to require SNI use to avoid header inspection. Nginx must determine if a server block matches the request and to do that, it needs a hostname. It can get a hostname from SNI during the TLS handshake, but if SNI is not used, it needs to read host information from the http headers.

You can require SNI use by setting up a default server-block handler for non-SNI client requests that refuses these connections. To do that, remove the default_server directive from your main ssl server block and add an explicit default server above it that 1) listens on 443, 2) matches requests with no host name, and 3) always returns 444. Example:

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl default_server;
  server_name   "";

  ssl_ciphers aNULL;
  ssl_certificate /path/to/dummy.crt;
  ssl_certificate_key /path/to/dummy.key;

  return 444;
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name myserver.com;

  ssl_certificate /etc/letsencrypt/live/myserver.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/myserver.com/privkey.pem;

  ssl_verify_client on;
  ssl_client_certificate /etc/nginx/ssl/ca.pem;

  location / {
        proxy_pass       http://127.0.0.1:8001;
        proxy_set_header Host      $host;
        proxy_set_header X-Real-IP $remote_addr;
} }

The default server then handles any SSL requests that do not use SNI. The 444 return is an nginx non-standard code that closes the connection. This structure also closes a potential security hole in which a default-server's certificate can expose resources in unexpected ways. Now nginx will only process requests that use SNI and that have a hostname matching one of your other server blocks.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .