3

I've been getting errors from Django on my webserver (behind Nginx/uWSGI) complaining that it's being accessed with a request where the Host is the IP address of the server. Nginx is using virtual hosts so I'm expecting the Host to always be the server name, so Django's ALLOWED_HOSTS is just that.

Invalid HTTP_HOST header: '###.###.###.###'. You may need to add '###.###.###.###' to ALLOWED_HOSTS.

Request repr():
<WSGIRequest
path:/,
GET:<QueryDict: {}>,
POST:<QueryDict: {}>,
COOKIES:{},
META:{'CONTENT_LENGTH': '',
 'CONTENT_TYPE': '',
 'DOCUMENT_ROOT': '/usr/share/nginx/html',
 'HTTP_HOST': '###.###.###.###',
 'PATH_INFO': '/',
 'QUERY_STRING': '',
 'REMOTE_ADDR': '184.105.139.68',
 'REMOTE_PORT': '45409',
 'REQUEST_METHOD': 'GET',
 'REQUEST_URI': '/',
 'SCRIPT_NAME': '',
 'SERVER_NAME': 'subdomain.example.net',
 'SERVER_PORT': '443',
 'SERVER_PROTOCOL': 'HTTP/1.1',
 'UWSGI_SCHEME': 'https',
 'uwsgi.core': 1,
 'uwsgi.node': b'subdomain.example.net',
 'uwsgi.version': b'2.0.12',
 'wsgi.errors': <_io.TextIOWrapper name=2 mode='w' encoding='ANSI_X3.4-1968'>,
 'wsgi.file_wrapper': <built-in function uwsgi_sendfile>,
 'wsgi.input': <uwsgi._Input object at 0x7f...>,
 'wsgi.multiprocess': True,
 'wsgi.multithread': True,
 'wsgi.run_once': False,
 'wsgi.url_scheme': 'https',
 'wsgi.version': (1, 0)}>

The errors are triggered by 184.105.247.195, 1 or 2 per day, which is owned by the "Shadowserver Foundation", so I'm unsure if these (mock) attacks are being successfully thwarted (but annoying me with an error code) or if something stranger is going on...like how/why is Nginx passing a request with an IP Host to a server block that is of the following format (I have a catch-all server block that rejects Host-less requests):

server {
    listen 443;
    server_name abc.example.net;
    # ...
}

This SO answer tells me how to configure Nginx to reject malformed requests (wrong Host header), but that seems like a belt-and-suspenders approach.

Is this indicative of my belt not being closed properly while someone's trying to pants me?


As an addendum, it's using SSL (and I'm hosting multiple SSL sites on the same port, using SNI to distinguish them, not sure if that matters...). Kinda like this answer (to another question) mentions, does the "attack" consist of negotiating with one of the Nginx server blocks, then in the encrypted request changing the Host header to my server's IP?

Nick T
  • 3,382
  • 4
  • 21
  • 28
  • Can you edit your question to add a sampling of the error messages that you're getting and an approximation of how many of them you are getting per day? That should help us figure out what is going on. – Neil Smithline Mar 24 '16 at 00:21
  • @NeilSmithline done. – Nick T Mar 24 '16 at 07:04
  • Have you tested your configuration to make sure nginx is really blocking? (i.e. by typing your server ip in the address bar) – Azeezah M Jun 24 '16 at 22:59

1 Answers1

3

The issue was being caused by Nginx using my Django host as the default, so an "https://293.7.10.738/" request was being delegated to it. The solution was to add a default server. The default is used when no other server block's server_name doesn't match the request. It is chosen by Nginx to be:

  1. A server listening on the port (and address, if specified) with the default_server property
  2. Otherwise, the first server block in the configuration.

I modified the main /etc/nginx/nginx.conf file to include implicit default server blocks that either

  • redirect HTTP to whatever the "default" host should be for the server, or
  • close HTTPS requests to invalid hostnames.

The latter has no impact on users because it's impossible to get a valid cert for an IP address. It's only strange crawler/clients that go around poking IP hosts, ignoring cert validity for some bizarre reason, that get dumped.

server {
    # Redirect IP request to primary host
    listen 80;
    return 301 https://defaulthost.example.com/;
}
server {
    # Terminate any HTTPS connections made with the IP address as host
    # (or any other unrecognized host).
    listen 443;
    server_name _;

    ssl on;
    # this is just a self-signed cert; there needs to be 
    # a well-formed one for nginx to start
    ssl_certificate /srv/ssl/sham.cer;
    ssl_certificate_key /srv/ssl/sham.key;

    return 444;
}
Nick T
  • 3,382
  • 4
  • 21
  • 28
  • It is _possible_ to get a (public) cert for a public IP address, but more expensive, more risky, and less flexible, so generally not a good idea, and as you say in your case not desirable. (In an intranet or otherwise limited and controllable context with a non-public CA it may be more sensible.) – dave_thompson_085 Nov 16 '16 at 08:35