Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask request.remote_addr is wrong on webfaction and not showing real user IP

I just deployed a Flask app on Webfaction and I've noticed that request.remote_addr is always 127.0.0.1. which is of course isn't of much use.

How can I get the real IP address of the user in Flask on Webfaction?

Thanks!

like image 225
Ignas Butėnas Avatar asked Oct 07 '12 17:10

Ignas Butėnas


People also ask

How do I find my visitors IP in flask?

How to Find the IP Address Of a User in Flask? Whenever a client requests a page - they send an HTTP GET request, with a certain payload. Amongst other information - their distinctive IP address is included. In Flask - every route has access to the request variable, which represents the incoming request from the user.

What is HTTP_X_FORWARDED_FOR?

HTTP_X_FORWARDED_FOR is often used to detect the client IP address, but without any additional checks, this can lead to security issues, especially when this IP is later used for authentication or in SQL queries without sanitization.

What is remote address header?

REMOTE_HOST pertains to the hostname of the client (i.e. the computer making the request). REMOTE_ADDR refers to the IP address of the client. There would be times when the hostname is unresolvable so the REMOTE_HOST will return the REMOTE_ADDR or the IP address instead. Follow this answer to receive notifications.


4 Answers

If there is a proxy in front of Flask, then something like this will get the real IP in Flask:

if request.headers.getlist("X-Forwarded-For"):
   ip = request.headers.getlist("X-Forwarded-For")[0]
else:
   ip = request.remote_addr

Update: Very good point mentioned by Eli in his comment. There could be some security issues if you just simply use this. Read Eli's post to get more details.

like image 196
Ignas Butėnas Avatar answered Oct 22 '22 22:10

Ignas Butėnas


Werkzeug middleware

Flask's documentation is pretty specific about recommended reverse proxy server setup:

If you deploy your application using one of these [WSGI] servers behind an HTTP [reverse] proxy you will need to rewrite a few headers in order for the application to work [properly]. The two problematic values in the WSGI environment usually are REMOTE_ADDR and HTTP_HOST... Werkzeug ships a fixer that will solve some common setups, but you might want to write your own WSGI middleware for specific setups.

And also about security consideration:

Please keep in mind that it is a security issue to use such a middleware in a non-proxy setup because it will blindly trust the incoming headers which might be forged by malicious clients.

The suggested code (that installs the middleware) that will make request.remote_addr return client IP address is:

from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1)

Note num_proxies which is 1 by default. It's the number of proxy servers in front of the app.

The actual code is as follows (lastest werkzeug==0.14.1 at the time of writing):

def get_remote_addr(self, forwarded_for):
    if len(forwarded_for) >= self.num_proxies:
        return forwarded_for[-self.num_proxies]

Webfaction

Webfaction's documentation about Accessing REMOTE_ADDR says:

...the IP address is available as the first IP address in the comma separated list in the HTTP_X_FORWARDED_FOR header.

They don't say what they do when a client request already contains X-Forwarded-For header, but following common sense I would assume they replace it. Thus for Webfaction num_proxies should be set to 0.

Nginx

Nginx is more explicit about it's $proxy_add_x_forwarded_for:

the “X-Forwarded-For” client request header field with the $remote_addr variable appended to it, separated by a comma. If the “X-Forwarded-For” field is not present in the client request header, the $proxy_add_x_forwarded_for variable is equal to the $remote_addr variable.

For Nginx in front of the app num_proxies should be left at default 1.

like image 10
saaj Avatar answered Oct 22 '22 22:10

saaj


Rewriting the Ignas's answer:

headers_list = request.headers.getlist("X-Forwarded-For")
user_ip = headers_list[0] if headers_list else request.remote_addr

Remember to read Eli's post about spoofing considerations.

like image 7
Shankar Cabus Avatar answered Oct 22 '22 21:10

Shankar Cabus


You can use request.access_route to access list of ip :

if len(request.access_route) > 1:
    return request.access_route[-1]
else:
    return request.access_route[0]

Update:

You can just write this:

    return request.access_route[-1]
like image 6
rezakamalifard Avatar answered Oct 22 '22 21:10

rezakamalifard