2014-04-04 14 views
9

Ich habe eine Flask-App-Setup auf mod_wsgi/Apache und muss die IP-Adresse des Benutzers protokollieren. request.remote_addr gibt "127.0.0.1" und this fix versucht, das zu korrigieren, aber ich habe festgestellt, dass Django aus Sicherheitsgründen ähnlichen Code entfernt hat.Wie bekomme ich die reale IP-Adresse des Benutzers sicher in Flask (mit mod_wsgi)?

Gibt es eine bessere Möglichkeit, die echte IP-Adresse des Benutzers sicher zu erhalten?

EDIT: Vielleicht fehlt mir etwas offensichtlich. Ich bewarb mich werkzeug's/Flask's fix aber es scheint nicht, einen Unterschied zu machen, wenn ich eine Anfrage mit veränderten Header versuchen:

run.py:

from werkzeug.contrib.fixers import ProxyFix 
    app.wsgi_app = ProxyFix(app.wsgi_app) 
    app.run() 

view.py:

for ip in request.access_route: 
     print ip # prints "1.2.3.4" and "my.ip.address" 

Dieselbe Ergebnis passiert, wenn ich den ProxyFix aktiviert habe oder nicht. Ich fühle mich wie ich bin etwas ganz offensichtlich

Antwort

21

Sie die request.access_route attribute nur verwenden, wenn Sie eine Liste der vertraute Proxies definieren.

Das Attribut access_route verwendet die X-Forwarded-For header, die auf die REMOTE_ADDR WSGI-Variable zurückfällt; Letzteres ist in Ordnung, da Ihr Server dies feststellt; die X-Forwarded-For von nur etwa jeder gesetzt worden sein könnte, aber wenn Sie einen Proxy vertrauen korrekt den Wert einzustellen, verwenden Sie dann die erste (von hinten), die nicht vertraut ist:

trusted_proxies = {'127.0.0.1'} # define your own set 
route = request.access_route + [request.remote_addr] 

remote_addr = next((addr for addr in reversed(route) 
        if addr not in trusted_proxies), request.remote_addr) 

Auf diese Weise, Selbst wenn jemand den X-Forwarded-For Header mit fake_ip1,fake_ip2 vortäuscht, fügt der Proxy-Server ,spoof_machine_ip zum Ende hinzu, und der obige Code setzt remote_addr auf spoof_machine_ip, egal wie viele vertrauenswürdige Proxies es zusätzlich zu Ihrem äußersten Proxy gibt.

Dies ist die Whitelist-Herangehensweise, über die Ihr verknüpfter Artikel spricht (kurz, in dem Rails es verwendet) und was Zope implemented over 11 years ago.

Ihr ProxyFix Ansatz funktioniert gut, aber Sie missverstanden, was es tut. Es nur Sätze request.remote_addr; Das request.access_route Attribut ist unverändert (der X-Forwarded-For Header ist nicht angepasst von der Middleware). Aber, würde ich sehr vorsichtig blind von Proxys abzählen.

Anwendung des gleichen Whitelist-Ansatz an die Middleware würde wie folgt aussehen:

class WhitelistRemoteAddrFix(object): 
    """This middleware can be applied to add HTTP proxy support to an 
    application that was not designed with HTTP proxies in mind. It 
    only sets `REMOTE_ADDR` from `X-Forwarded` headers. 

    Tests proxies against a set of trusted proxies. 

    The original value of `REMOTE_ADDR` is stored in the WSGI environment 
    as `werkzeug.whitelist_remoteaddr_fix.orig_remote_addr`. 

    :param app: the WSGI application 
    :param trusted_proxies: a set or sequence of proxy ip addresses that can be trusted. 
    """ 

    def __init__(self, app, trusted_proxies=()): 
     self.app = app 
     self.trusted_proxies = frozenset(trusted_proxies) 

    def get_remote_addr(self, remote_addr, forwarded_for): 
     """Selects the new remote addr from the given list of ips in 
     X-Forwarded-For. Picks first non-trusted ip address. 
     """ 

     if remote_addr in self.trusted_proxies: 
      return next((ip for ip in reversed(forwarded_for) 
         if ip not in self.trusted_proxies), 
         remote_addr) 

    def __call__(self, environ, start_response): 
     getter = environ.get 
     remote_addr = getter('REMOTE_ADDR') 
     forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',') 
     environ.update({ 
      'werkzeug.whitelist_remoteaddr_fix.orig_remote_addr': remote_addr, 
     }) 
     forwarded_for = [x for x in [x.strip() for x in forwarded_for] if x] 
     remote_addr = self.get_remote_addr(remote_addr, forwarded_for) 
     if remote_addr is not None: 
      environ['REMOTE_ADDR'] = remote_addr 
     return self.app(environ, start_response) 

explizit zu sein: diese Middleware auch nur Sätze request.remote_addr; request.access_route bleibt davon unberührt.

+1

Ich denke, ich bin nur verwirrt, warum der sogenannte "ProxyFix" das Problem nicht behoben hat. Soll ich mich mit dem ProxyFix beschäftigen? –

+0

thx wieder Martijn! –

1

Von diesem Artikel fehlt, es klingt wie das Sicherheitsproblem ist die ersten Wert des X-Forwarding-For Header zu vertrauen. Sie können implementieren, was der Artikel im Abschnitt "Mein Versuch zu einer besseren Lösung" vorschlägt, um dieses potenzielle Sicherheitsproblem zu beheben. Eine Bibliothek, die ich erfolgreich mit Django benutzt habe, ist django-ipware. Trace durch seine Umsetzung Ihrer eigenen für Kolben zu rollen (man kann sehen, dass es ähnlich ist, was dieser Artikel schon sagt):

https://github.com/un33k/django-ipware/blob/master/ipware/ip.py#L7

+0

ich das Update von Werkzeug angewendet vertrauen kann, aber es scheint keine Wirkung zu haben. Bitte beachten Sie meine Bearbeitung # 2 –

+0

Dies verwendet auch eine Whitelist-Ansatz, wenn auch eine, die nur private Netzwerk-IP-Adressen erlaubt. –

-3

Sie können nicht sicher tun, denn die Sie nicht alle Teile in einem http (oder TCP) -Verbindung

+1

Sie können Ihrem eigenen Server vertrauen, die IP-Adresse der Remote-Verbindung korrekt festzulegen. Sie können bestimmten Fernverbindungen vertrauen, um die Wahrheit zu sagen. In Kombination mit einer Whitelist können Sie eine Vertrauenskette aufbauen. –

+0

Und was ist mit IP-Spoofing oder bösartigen Proxies? – pbacterio

+0

IP-Spoofing in einer TCP/IP-Verbindung erfordert, dass Sie die Pakete schnüffeln können; z.B. Sie können auch abfangen, was bereits hoch und runter geht. Bösartige Proxies sollten nicht zu Ihrer vertrauenswürdigen Liste hinzugefügt werden, * Duh *. –

Verwandte Themen