2014-04-04 8 views
9

Ho un'installazione di app per flask su mod_wsgi/Apache e devo registrare l'indirizzo IP dell'utente. request.remote_addr restituisce "127.0.0.1" e this fix tentativi di correggerlo, ma ho scoperto che Django ha rimosso un codice simile per motivi di sicurezza.Come posso ottenere in modo sicuro l'indirizzo IP reale dell'utente in Flask (usando mod_wsgi)?

C'è un modo migliore per ottenere in modo sicuro l'indirizzo IP reale dell'utente?

EDIT: Forse mi manca qualcosa di ovvio. Ho applicato werkzeug's/Flask's fix ma non sembra fare la differenza quando provo una richiesta con le intestazioni modificate:

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" 

Questo stesso il risultato si verifica se ho il ProxyFix abilitato o meno. Mi sento come se mi mancasse qualcosa di completamente ovvio

risposta

21

È possibile utilizzare l'request.access_route attribute solo se si definisce un elenco di fiducia proxy.

L'attributo access_route utilizza lo X-Forwarded-For header, ritornando alla variabile WSGI REMOTE_ADDR; il secondo va bene come il tuo server determina questo; il X-Forwarded-For avrebbe potuto essere fissato dalla quasi chiunque, ma se vi fidate un proxy per impostare correttamente il valore, quindi utilizzare il primo (dalla fine) che è non attendibile:

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) 

In questo modo, anche se qualcuno contraffa l'intestazione X-Forwarded-For con fake_ip1,fake_ip2, il server proxy aggiungerà ,spoof_machine_ip alla fine e il codice precedente imposterà lo remote_addr in spoof_machine_ip, indipendentemente dal numero di proxy fidati che ci sono in aggiunta al proxy più esterno.

Questo è l'approccio di whitelist di cui parla il tuo articolo collegato (brevemente, nel fatto che Rails lo utilizza), e cosa Zope implemented over 11 years ago.

L'approccio ProxyFix funziona correttamente, ma hai frainteso ciò che fa. È solo set request.remote_addr; l'attributo request.access_route non è stato modificato (l'intestazione X-Forwarded-For è non regolata dal middleware). Tuttavia,, sarei molto cauto nel contare ciecamente i proxy.

Applicando lo stesso approccio whitelist al middleware sarà simile:

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) 

essere espliciti: questo middleware anche solo set request.remote_addr; request.access_route rimane inalterato.

+1

Immagino di essere solo confuso perché il cosiddetto "ProxyFix" non ha risolto il problema. Dovrei preoccuparmi di ProxyFix? –

+0

grazie ancora Martijn! –

1

Da questo articolo, sembra che il problema di sicurezza si basi sul valore prima dell'intestazione X-Forwarding-For. È possibile implementare ciò che l'articolo suggerisce nella sezione "Il mio tentativo in una soluzione migliore" per superare questo potenziale problema di sicurezza. Una libreria che ho usato in django con successo è django-ipware. Trace attraverso la sua implementazione a rotolare il proprio per pallone (si può vedere che è simile a quello che suggerisce che l'articolo):

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

+0

ho applicato la correzione dal Werkzeug, ma non sembra avere alcun effetto. Si prega di vedere il mio edit n. 2 –

+0

Anche questo utilizza un approccio whitelist, anche se consente solo gli indirizzi IP della rete privata. –

-3

Non si può tranquillamente farlo, perché non ci si può fidare di tutte le parti in una connessione http (o TCP)

+1

Ci si può fidare del proprio server per impostare correttamente l'indirizzo IP della connessione remota. Puoi scegliere di affidarti a connessioni remote specifiche per dire la verità. Combinato con una lista bianca, puoi costruire una catena di fiducia. –

+0

E che dire di ip-spoofing o proxy dannosi? – pbacterio

+0

Lo spoofing IP in una connessione TCP/IP richiede la possibilità di sniffare i pacchetti; per esempio. puoi anche intercettare tutto ciò che sale e scende già. I proxy dannosi non dovrebbero essere aggiunti al tuo elenco di fiducia, * duh *. –

Problemi correlati