run oficial homepage example development python flask werkzeug

python - oficial - uwsgi flask



¿Cómo puedo obtener de forma segura la dirección IP real del usuario en Flask(usando mod_wsgi)? (3)

Tengo una configuración de aplicación de matraz en mod_wsgi / Apache y necesito registrar la dirección IP del usuario. request.remote_addr devuelve "127.0.0.1" y esta solución intenta corregir eso, pero descubrí que Django eliminó un código similar por razones de seguridad.

¿Hay una mejor manera de obtener de forma segura la dirección IP real del usuario?

EDITAR: Tal vez me falta algo obvio. Apliqué la corrección de werkzeug / Flask, pero no parece marcar la diferencia cuando intento una solicitud con encabezados alterados:

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"

Este mismo resultado ocurre si tengo ProxyFix habilitado o no. Siento que me falta algo completamente obvio


A partir de ese artículo, parece que el problema de seguridad es confiar en el primer valor del encabezado X-Forwarding-For . Puede implementar lo que sugiere el artículo en la sección "Mi intento en una mejor solución" para superar este posible problema de seguridad. Una biblioteca que he utilizado en django con éxito es django-ipware . Haga un seguimiento a lo largo de su implementación para rodar su propio frasco (puede ver que es similar a lo que sugiere el artículo):

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


No puede hacer eso de forma segura, porque no puede confiar en todas las partes en una conexión http (o tcp)


Puede usar el atributo request.access_route solo si define una lista de proxies confiables .

El atributo access_route usa el encabezado X-Forwarded-For , volviendo a la variable REMOTE_ADDR WSGI; lo último está bien ya que su servidor lo determina; el X-Forwarded-For podría haber sido establecido por casi cualquier persona, pero si confía en un proxy para establecer el valor correctamente, entonces use el primero (del final) que no sea de confianza:

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)

De esta forma, incluso si alguien parodia el encabezado X-Forwarded-For con fake_ip1,fake_ip2 , el servidor proxy agregará ,spoof_machine_ip al final, y el código anterior establecerá el remote_addr en spoof_machine_ip , sin importar cuántos apoderados de confianza haya en Además de su proxy externo.

Este es el enfoque de la lista blanca del que habla su artículo vinculado (brevemente, en el que Rails lo usa), y lo que Zope implementó hace más de 11 años .

Su enfoque ProxyFix funciona bien, pero usted no entendió lo que hace. Solo establece request.remote_addr ; el atributo request.access_route no se modifica (el middleware no ajusta el encabezado X-Forwarded-For ). Sin embargo , sería muy cuidadoso de contar ciegamente los poderes.

Aplicando el mismo enfoque de lista blanca al middleware se vería así:

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)

Para ser explícito: este middleware también, solo establece request.remote_addr ; request.access_route no se ve afectado.