Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

get_host() vs META['REMOTE_ADDR'] for security reasons

I am working on a Django project where users will have custom info given to them depending on their location. In order to do this, I use their IP address to identify their country. In order to keep data in the database consistent, I need to make sure I have an accurate IP.

I understand that using META usually uses headers sent by the client's browser, but I don't know if that applies to the REMOTE_ADDR attribute.

TLDR: what is the difference between HttpRequest.get_host() and HttpRequest.META['REMOTE_ADDR']?

like image 728
dirtshell Avatar asked Jul 14 '13 00:07

dirtshell


1 Answers

The difference between HttpRequest.get_host() and HttpRequest.META['REMOTE_ADDR'] is that the first one checks IP in the following headers in order of decreasing preference:

  1. HTTP_X_FORWARDED_HOST
  2. HTTP_HOST
  3. SERVER_NAME combined with SERVER_PORT

whereas the second one check the IP in the header REMOTE_ADDR.

There is a huge difference in the type of information returned: get_host() will give you the name of the server hosting your application, not the IP of the client.

More in detail, here is the implementation of get_host():

def get_host(self):
    """Returns the HTTP host using the environment or request headers."""
    # We try three options, in order of decreasing preference.
    if settings.USE_X_FORWARDED_HOST and (
        'HTTP_X_FORWARDED_HOST' in self.META):
        host = self.META['HTTP_X_FORWARDED_HOST']
    elif 'HTTP_HOST' in self.META:
        host = self.META['HTTP_HOST']
    else:
        # Reconstruct the host using the algorithm from PEP 333.
        host = self.META['SERVER_NAME']
        server_port = str(self.META['SERVER_PORT'])
        if server_port != ('443' if self.is_secure() else '80'):
            host = '%s:%s' % (host, server_port)

    allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
    domain, port = split_domain_port(host)
    if domain and validate_host(domain, allowed_hosts):
        return host
    else:
        msg = "Invalid HTTP_HOST header: %r." % host
        if domain:
            msg += "You may need to add %r to ALLOWED_HOSTS." % domain
        raise DisallowedHost(msg)

If you want to check for client IP address, here are some headers that could be worth checking (see Getting the client IP address: REMOTE_ADDR, HTTP_X_FORWARDED_FOR, what else could be useful?):

  • REMOTE_ADDR
  • HTTP_X_FORWARDED_FOR
  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR can be comma delimited list of IPs
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED

If you don't know which one to pick (if not all), you could log those headers and pragmatically add new checkings over time.

like image 99
Benjamin Toueg Avatar answered Nov 20 '22 01:11

Benjamin Toueg