【问题标题】:How do I safely get the user's real IP address in Flask (using mod_wsgi)?如何在 Flask 中安全地获取用户的真实 IP 地址(使用 mod_wsgi)?
【发布时间】:2014-05-17 03:03:18
【问题描述】:

我在 mod_wsgi/Apache 上有一个烧瓶应用程序设置,需要记录用户的 IP 地址。 request.remote_addr 返回“127.0.0.1”,this fix 试图纠正这个问题,但我发现 Django 出于安全原因删除了类似的代码。

有没有更好的方法来安全获取用户的真实 IP 地址?

编辑:也许我遗漏了一些明显的东西。我申请了werkzeug's/Flask's fix,但当我尝试使用更改的标头的请求时似乎没有什么不同:

运行.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"

无论我是否启用了 ProxyFix,都会出现同样的结果。我觉得我错过了一些非常明显的东西

【问题讨论】:

    标签: python flask werkzeug


    【解决方案1】:

    只有在您定义了受信任的代理列表时,您才能使用request.access_route attribute

    access_route 属性使用X-Forwarded-For header,回退到REMOTE_ADDR WSGI 变量;后者很好,因为您的服务器确定了这一点; X-Forwarded-For 几乎可以由任何人设置,但如果您信任代理来正确设置值,则使用第一个(从最后开始)受信任的代理:

    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)
    

    这样,即使有人用fake_ip1,fake_ip2欺骗了X-Forwarded-For标头,代理服务器也会在末尾添加,spoof_machine_ip,上面的代码无论如何都会将remote_addr设置为spoof_machine_ip除了最外层的代理之外,还有许多受信任的代理。

    这是您的链接文章谈到的白名单方法(简而言之,Rails 使用它),以及 Zope implemented over 11 years ago 的内容。

    您的 ProxyFix 方法运行良好,但您误解了它的作用。它设置request.remote_addrrequest.access_route 属性不变(X-Forwarded-For 标头没有由中间件调整)。 但是,我会非常警惕盲目地计算代理。

    对中间件应用相同的白名单方法如下所示:

    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)
    

    明确地说:这个中间件也是,只有设置request.remote_addrrequest.access_route 不受影响。

    【讨论】:

      【解决方案2】:

      您不能安全地这样做,因为您不能信任 http(或 tcp)连接中的所有部分

      【讨论】:

      • 您可以信任自己的服务器正确设置远程连接的ip地址。您可以选择信任特定的远程连接来说出真相。结合白名单,可以建立信任链。
      • 那么 IP 欺骗或恶意代理呢?
      • TCP/IP 连接中的 IP 欺骗要求您可以嗅探数据包;例如你也可以拦截已经发生的任何事情。不应将恶意代理添加到您的信任列表中,duh
      猜你喜欢
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 2015-03-23
      • 2011-09-02
      • 1970-01-01
      相关资源
      最近更新 更多