Comment puis-je obtenir en toute sécurité l'adresse IP réelle de l'utilisateur dans Flask (en utilisant mod wsgi)?

j'ai une application flask setup sur mod_wsgi/Apache et je dois enregistrer l'adresse IP de l'utilisateur. demande.remote_addr renvoie "127.0.0.1" et fix tente de corriger cela mais J'ai trouvé que Django a enlevé le code similaire pour des raisons de sécurité.

Est-il un meilleur moyen pour amener l'utilisateur de l'adresse IP réelle?

EDIT: peut-être que je manque quelque chose d'évident. J'ai appliqué werkzeug/Flacon est corrigé mais cela ne semble pas faire de différence quand Je tente une demande avec des en-têtes modifiés:

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"

ce même résultat se produit si J'ai activé ou non le ProxyFix. J'ai l'impression que je suis absent quelque chose de tout à fait évident

10
demandé sur Marissa Levy 2014-04-04 21:08:03

3 réponses

Vous pouvez utiliser le request.access_route l'attribut uniquement si vous définissez une liste de confiance proxy.

access_route l'attribut utilise le X-Forwarded-For en-tête, pour retomber à l' REMOTE_ADDR variable WSGI; cette dernière est très bien car votre serveur le détermine; la variable X-Forwarded-For aurait pu être défini par n'importe qui, mais si vous faites confiance à un proxy pour définir la valeur correctement, alors utilisez le premier (à partir de la fin) qui est confiance:

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 Cette façon, même si quelqu'un usurpe la X-Forwarded-For en-tête avec fake_ip1,fake_ip2, le serveur proxy ajoutera ,spoof_machine_ip à la fin, et le code ci-dessus va définir le remote_addrspoof_machine_ip, peu importe le nombre de mandataires de confiance qu'il y a en plus de votre mandataire externe.

C'est l'approche de la liste blanche dont parle votre article lié (brièvement, dans le fait que Rail l'utilise), et ce que Zope mis en œuvre plus de 11 ans.

Votre L'approche ProxyFix fonctionne très bien, mais vous avez mal compris ce qu'elle fait. configure request.remote_addr;request.access_route l'attribut est inchangé (X-Forwarded-For en-tête ajustée par le middleware). cependant, je me méfierais de compter aveuglément les procurations.

appliquer la même approche de whitelist à l'middleware ressemblerait à:

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)

Pour être explicite: cette middleware trop, configure request.remote_addr; request.access_route reste inchangée.

23
répondu Martijn Pieters 2016-03-04 19:16:17

D'après cet article, on dirait que la question de la sécurité est de faire confiance aux premier valeur X-Forwarding-For en-tête. Vous pouvez mettre en œuvre ce que l'article suggère dans la section "j'essaie de trouver une meilleure solution" pour surmonter ce problème de sécurité potentiel. Une bibliothèque que j'ai utilisée à django avec succès est django-ipware. Trace à travers sa mise en œuvre pour rouler votre propre pour flasque (vous pouvez voir que c'est similaire à ce que cet article suggère):

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

1
répondu Sohan Jain 2014-04-04 17:57:54

vous ne pouvez pas faire cela en toute sécurité, parce que vous ne pouvez pas faire confiance à toutes les parties d'une connexion http (ou tcp)

-3
répondu pbacterio 2014-04-08 12:54:07