From 923ad720a19e8983e6124bb459ad9e0453c66e15 Mon Sep 17 00:00:00 2001 From: "Stephen J. Fuhry" Date: Sun, 28 Dec 2014 08:25:57 -0500 Subject: [PATCH] X-Forwarded-For can contain multiple IP addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the nginx docs: http://nginx.org/en/docs/http/ngx_http_proxy_module.html > $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. Use the last IP address in X-Forwarded-For. For this to work properly behind a trusted proxy, you must be using ProxyFix as described in the flask & werkzeug documentation. --- .gitignore | 8 ++++++++ docs/configuration.rst | 3 ++- flask_security/utils.py | 6 +++--- tests/test_trackable.py | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 4d3ab55..a9701f2 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,11 @@ env/ *.db *cache* + +# vim +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ diff --git a/docs/configuration.rst b/docs/configuration.rst index 018d4ae..b6b994f 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -159,7 +159,8 @@ Feature Flags option. Defaults to ``False``. ``SECURITY_TRACKABLE`` Specifies if Flask-Security should track basic user login statistics. If set to ``True``, ensure your - models have the required fields/attribues. Defaults to + models have the required fields/attribues. Be sure to + use `ProxyFix ` if you are using a proxy. Defaults to ``False`` ``SECURITY_PASSWORDLESS`` Specifies if Flask-Security should enable the passwordless login feature. If set to ``True``, users diff --git a/flask_security/utils.py b/flask_security/utils.py index 86bb24a..0419ba0 100644 --- a/flask_security/utils.py +++ b/flask_security/utils.py @@ -62,10 +62,10 @@ def login_user(user, remember=None): return False if _security.trackable: - if 'X-Forwarded-For' not in request.headers: - remote_addr = request.remote_addr or 'untrackable' + if 'X-Forwarded-For' in request.headers: + remote_addr = request.headers.getlist("X-Forwarded-For")[0].rpartition(' ')[-1] else: - remote_addr = request.headers.getlist("X-Forwarded-For")[0] + remote_addr = request.remote_addr or 'untrackable' old_current_login, new_current_login = user.current_login_at, datetime.utcnow() old_current_ip, new_current_ip = user.current_login_ip, remote_addr diff --git a/tests/test_trackable.py b/tests/test_trackable.py index 1e08662..7017754 100644 --- a/tests/test_trackable.py +++ b/tests/test_trackable.py @@ -26,3 +26,19 @@ def test_trackable_flag(app, client): assert user.last_login_ip == 'untrackable' assert user.current_login_ip == '127.0.0.1' assert user.login_count == 2 + + +def test_trackable_with_multiple_ips_in_headers(app, client): + e = 'matt@lp.com' + authenticate(client, email=e) + logout(client) + authenticate(client, email=e, headers={ + 'X-Forwarded-For': '99.99.99.99, 88.88.88.88'}) + + with app.app_context(): + user = app.security.datastore.find_user(email=e) + assert user.last_login_at is not None + assert user.current_login_at is not None + assert user.last_login_ip == 'untrackable' + assert user.current_login_ip == '88.88.88.88' + assert user.login_count == 2