Skip to main content

A Python package to retrieve user's IP address

Project description

Python IPware (A Python Package)

A python package for server applications to retrieve client's IP address

status-image version-image coverage-image

Overview

Best attempt to get client's IP address while keeping it DRY.

Notice

There is no perfect out-of-the-box solution against fake IP addresses, aka IP Address Spoofing. You are encouraged to read the (Advanced users) section of this page and use proxy_list and/or proxy_count features to match your needs, especially if you are planning to include ipware in any authentication, security or anti-fraud related architecture.

This is an open source project, with the source code visible to all. Therefore, it may be exploited through unimplemented, or improperly implemented features.

Please use ipware ONLY as a complement to your firewall security measures!

How to install

1. easy_install python-ipware
2. pip install python-ipware
3. git clone http:#github.com/un33k/python-ipware
    a. cd python-ipware
    b. run python setup.py install
4. wget https:#github.com/un33k/python-ipware/zipball/master
    a. unzip the downloaded file
    b. cd into python-ipware-* directory
    c. run python setup.py install

How to use

# In a view or a middleware where the `request` object is available
import ipware
ipware = Ipware() # default values
meta = request.META # Django (meta = request.META), Flask (meta = request.environ), etc
ip, trusted_route = ipware.get_client_ip(meta)
if ip: # IPv4Address() or IPv6Address() object
    # ip object has the following properties
    # ip.is_global (is globally routable)
    # ip.is_private (is private ip address)
    # is_loopback (is loopback address)
# trusted_route tells if request came through our proxies (count / trusted)

# do something with the ip address (e.g. pass it down through the request)
# note: ip address doesn't change often, so better cache it for performance,
# try to have distinct session ID for public and anonymous users to cache the ip address

Advanced users:

Params ⇩ ⇩ Description
proxy_count : Total number of expected proxies (pattern: client, proxy1, ..., proxy2)
: if proxy_count = 0 then client
: if proxy_count = 1 then client, proxy1
: if proxy_count = 2 then client, proxy1, proxy2
: if proxy_count = 3 then client, proxy1, proxy2 proxy3
proxy_list : List of trusted proxies (ip header pattern: client, proxy1, ,..., proxyN)
: if proxy_list = ['10.1.'] then client, proxy1
: if proxy_list = ['10.1', '10.2.3'] then client, proxy1 proxy2
: if proxy_list = ['10.1', '10.2.', '10.3.4.4'] then client, proxy1, proxy2, proxy3
leftmost : leftmost = True is default for de-facto standard.
: leftmost = False for rare legacy networks that are configured with the rightmost pattern.
: It converts client, proxy1 proxy2 to proxy2, proxy1, client
Output ⇩ ⇩ Description
ip : Client IP address object of type IPv4Address() or IPv6Address()
trusted_route : If proxy proxy_count and/or proxy_list were provided and matched, True, else False

Precedence Order

The client IP address can be found in one or more request headers attributes. The lookup order is top to bottom and the default attributes are as follow.

# The default meta precedence order - you can be more specific as per your configuration
# It will start looking through the request headers from top to bottom to find the best match
# It will return the first qualified global (public) ip address it finds, else
# It will return the first qualified private ip address it finds, else
# It will return the first qualified loopback up address it finds, else it returns None
# Update as per your network topology, reduce the numbers and/or reorder the list
request_headers_precedence_order = (
    "X_FORWARDED_FOR",  # Load balancers or proxies such as AWS ELB (default client is `left-most` [`<client>, <proxy1>, <proxy2>`])
    "HTTP_X_FORWARDED_FOR",  # Similar to X_FORWARDED_TO
    "HTTP_CLIENT_IP",  # Standard headers used by providers such as Amazon EC2, Heroku etc.
    "HTTP_X_REAL_IP",  # Standard headers used by providers such as Amazon EC2, Heroku etc.
    "HTTP_X_FORWARDED",  # Squid and others
    "HTTP_X_CLUSTER_CLIENT_IP",  # Rackspace LB and Riverbed Stingray
    "HTTP_FORWARDED_FOR",  # RFC 7239
    "HTTP_FORWARDED",  # RFC 7239
    "HTTP_VIA",  # Squid and others
    "X-CLIENT-IP",  # Microsoft Azure
    "X-REAL-IP",  # NGINX
    "X-CLUSTER-CLIENT-IP",  # Rackspace Cloud Load Balancers
    "X_FORWARDED",  # Squid
    "FORWARDED_FOR",  # RFC 7239
    "CF-CONNECTING-IP",  # CloudFlare
    "TRUE-CLIENT-IP",  # CloudFlare Enterprise,
    "FASTLY-CLIENT-IP",  # Firebase, Fastly
    "FORWARDED",  # RFC 7239
    "CLIENT-IP",  # Akamai and Cloudflare: True-Client-IP and Fastly: Fastly-Client-IP
    "REMOTE_ADDR",  # Default
)

You can customize the order by providing your own list during initialization when calling IpWare().

# specific meta key
ipware = IpWare(precedence=("X_FORWARDED_FOR"))

# multiple meta keys
ipware = IpWare(precedence=("X_FORWARDED_FOR", "HTTP_X_FORWARDED_FOR"))

# usage is just to pass in the http request headers

# Django (request.META)
ip, proxy_verified = ipware.get_client_ip(meta=request.META)

# Flask (request.environ)
ip, proxy_verified = ipware.get_client_ip(meta=request.environ)

# ... etc.

Trusted Proxies

If your node server is behind one or more known proxy server(s), you can filter out unwanted requests by providing a trusted proxy list, or a known proxy count.

You can customize the proxy IP prefixes by providing your own list during initialization when calling IpWare(proxy_list). You can pass your custom list on every call, when calling the proxy-aware api to fetch the ip.

# In the above scenario, use your load balancer IP address as a way to filter out unwanted requests.
ipware = IpWare(proxy_list=["198.84.193.157"])


# If you have multiple proxies, simply add them to the list
ipware = IpWare(proxy_list=["198.84.193.157", "198.84.193.158"])

# For proxy servers with fixed sub-domain and dynamic IP, use the following pattern.
ipware = IpWare(proxy_list=["177.139.", "177.140"])

# usage: non-strict mode (X-Forwarded-For: <fake>, <client>, <proxy1>, <proxy2>)
# The request went through our <proxy1> and <proxy2>, then our server
# We choose the <client> ip address to the left our <proxy1> and ignore other ips
ip, trusted_route = self.ipware.get_client_ip(meta=request.META)


# usage: strict mode (X-Forwarded-For: <client>, <proxy1>, <proxy2>)
# The request went through our <proxy1> and <proxy2>, then our server
# Total ip address are total trusted proxies + client ip
# We don't allow far-end proxies, or fake addresses (exact or None)
ip, trusted_route = self.ipware.get_client_ip(meta=request.META, strict=True)

In the following example, your public load balancer (LB) can be seen as a trusted proxy.

`Real` Client <public> <-> <public> LB (Server) <private> <-----> <private> Django Server
                                                             ^
                                                             |
`Fake` Client <private> <-> <private> LB (Server) <private> -+

Proxy Count

If your python server is behind a known number of proxies, but you deploy on multiple providers and don't want to track proxy IPs, you still can filter out unwanted requests by providing proxy count.

You can customize the proxy count by providing your proxy_count during initialization when calling IpWare(proxy_count=2).

# In the above scenario, the total number of proxies can be used as a way to filter out unwanted requests.
import ipware

# enforce proxy count
ipware = IpWare(proxy_count=1)

# enforce proxy count and trusted proxies
ipware = IpWare(proxy_count=1, proxy_list=["198.84.193.157"])


# usage: non-strict mode (X-Forwarded-For: <fake>, <client>, <proxy1>, <proxy2>)
# total number of ip addresses are greater than the total count
ip, trusted_route = self.ipware.get_client_ip(meta=request.META)


# usage: strict mode (X-Forwarded-For: <client>, <proxy1>, <proxy2>)
# total number of ip addresses are exactly equal to client ip + proxy_count
ip, trusted_route = self.ipware.get_client_ip(meta=request.META, strict=True)

In the following example, your public load balancer (LB) can be seen as the only proxy.

`Real` Client <public> <-> <public> LB (Server) <private> <---> <private> Node Server
                                                            ^
                                                            |
                                `Fake` Client  <private> ---+

Support for Public IP Address (routable on the internet), Private and Loopback

# We make best attempt to return the first public IP address based on header precedence
# Then we fall back on private, followed by loopback
import ipware

# no proxy enforce in this example
ipware = IpWare()

ip, _ = self.ipware.get_client_ip(meta=request.META)

if ip.is_global:
    print('Public IP')
else if ip.is_private:
    print('Private IP')
else if ip.loopback:
    print('Loopback IP')

Support for IPv4, Ipv6, and IP:Port patterns

- Ports will be automatically stripped off, and the IP addresses will be processed and returned.
- IPv4s that are wrapped in IPv6 containers will be also unwrapped, processed and returned

Originating Request

Please note that the [de-facto](https:#developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) standard
for the originating client IP address is the `leftmost`as per`client, proxy1, proxy2`, and the `rightmost` proxy is the most
trusted proxy.

However, in rare cases your network has a `custom` configuration where the `rightmost` IP address is that of the originating client. If that is the case, then indicate it when creating `IpWare(leftmost=False)`.

Running the tests

To run the tests against the current environment:

./test.sh

License

Released under a (MIT) license.

Version

X.Y.Z Version

`MAJOR` version -- making incompatible API changes
`MINOR` version -- adding functionality in a backwards-compatible manner
`PATCH` version -- making backwards-compatible bug fixes

Sponsors

Neekware Inc.

Need Support?

Neekware Inc. (reach out at info@neekware.com)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python-ipware-1.0.3.tar.gz (14.8 kB view hashes)

Uploaded Source

Built Distribution

python_ipware-1.0.3-py3-none-any.whl (9.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page