【问题标题】:How do I authenticate a urllib2 script in order to access HTTPS web services from a Django site?如何验证 urllib2 脚本以便从 Django 站点访问 HTTPS Web 服务?
【发布时间】:2011-02-22 18:17:08
【问题描述】:

大家。 我正在开发一个 django/mod_wsgi/apache2 网站,该网站使用 https 为所有请求和响应提供敏感信息。如果用户未通过身份验证,所有视图都将被写入重定向。它还有几个视图,旨在像 RESTful Web 服务一样运行。

我现在正在编写一个脚本,它使用 urllib/urllib2 来联系其中的几个服务,以便下载一系列非常大的文件。我在尝试登录时遇到了 403: FORBIDDEN 错误。

我用于身份验证和登录的(草稿)方法是:

def login( base_address, username=None, password=None ):

    # prompt for the username (if needed), password
    if username == None:
        username = raw_input( 'Username: ' )
    if password == None:
        password = getpass.getpass( 'Password: ' )
    log.info( 'Logging in %s' % username )

    # fetch the login page in order to get the csrf token
    cookieHandler = urllib2.HTTPCookieProcessor()
    opener = urllib2.build_opener( urllib2.HTTPSHandler(), cookieHandler )
    urllib2.install_opener( opener )

    login_url = base_address + PATH_TO_LOGIN
    log.debug( "login_url: " + login_url )
    login_page = opener.open( login_url )

    # attempt to get the csrf token from the cookie jar
    csrf_cookie = None
    for cookie in cookieHandler.cookiejar:
        if cookie.name == 'csrftoken':
             csrf_cookie = cookie
             break
    if not cookie:
        raise IOError( "No csrf cookie found" )
    log.debug(  "found csrf cookie: " + str( csrf_cookie ) )
    log.debug(  "csrf_token = %s" % csrf_cookie.value )

    # login using the usr, pwd, and csrf token
    login_data = urllib.urlencode( dict(
        username=username, password=password,
        csrfmiddlewaretoken=csrf_cookie.value ) )
    log.debug( "login_data: %s" % login_data )

    req = urllib2.Request( login_url, login_data )
    response = urllib2.urlopen( req )
    # <--- 403: FORBIDDEN here

    log.debug( 'response url:\n' + str( response.geturl() ) + '\n' )
    log.debug( 'response info:\n' + str( response.info() ) + '\n' )

    # should redirect to the welcome page here, if back at log in - refused
    if response.geturl() == login_url:
        raise IOError( 'Authentication refused' )

    log.info( '\t%s is logged in' % username )
    # save the cookies/opener for further actions
    return opener 

我正在使用 HTTPCookieHandler 在脚本端存储 Django 的身份验证 cookie,以便我可以访问 Web 服务并通过我的重定向。

我知道如果我不将 csrf 令牌与登录信息一起传递,那么用于 Django 的 CSRFmiddleware 将会把我赶出去,所以我首先从第一页/表单加载的 cookiejar 中提取它。就像我提到的,这适用于网站的 http/development 版本。

具体来说,我在尝试通过 https 连接将凭据发布到登录页面/表单时收到 403。此方法在使用 http 连接的开发服务器上使用时有效。

没有阻止访问该区域的 Apache 目录指令(我可以看到)。该脚本在没有发布数据的情况下成功连接到登录页面,所以我认为这会让 Apache 没有问题(但我可能是错的)。

我使用的 python 安装都是用 SSL 编译的。

我还了解到 urllib2 不允许通过代理进行 https 连接。我对代理不是很有经验,所以我不知道使用远程机器上的脚本是否实际上是代理连接,以及这是否会成为问题。这会导致访问问题吗?

据我所知,问题出在 cookie 和发布数据的组合中,但我不清楚从哪里获取它。

任何帮助将不胜感激。谢谢

【问题讨论】:

    标签: python django web-services https urllib2


    【解决方案1】:

    请原谅我回答我自己的问题,但是——据记录,这似乎已经解决了:

    事实证明,我需要在我发布登录信息的请求中将 HTTP Referer 标头设置为登录页面 url。

    req.add_header( 'Referer', login_url )
    

    原因在Django CSRF documentation 上进行了解释——具体来说,第 4 步。

    由于我们在生产端使用 HTTPS 并且 DEBUG=False 的服务器设置有些特殊,我没有看到通常的 csrf_failure 失败原因(在这种情况下:'Referer checks failed - no referer')在调试信息中输出。我最终将失败原因打印到 Apache error_log 和 STFW 上。这将我引向code.djangoproject/.../csrf.py 和Referer 标头修复。

    【讨论】:

      【解决方案2】:

      这适用于我在 https 上的 django 设置,这是受您的启发。我开始认为问题出在这段代码之外......服务器在说什么吗?我很可能正在研究 apache。

      我在 nginx 上使用 ssl 从我的本地机器到我的服务器使用以下代码,因此 apache 可能是查看的地方。我想缩小范围的一种方法是在我的登录页面上尝试你的脚本:) 给我发一封电子邮件!

      import urllib
      import urllib2
      import contextlib
      
      
      def login(login_url, username, password):
          """
          Login to site
          """
          cookies = urllib2.HTTPCookieProcessor()
          opener = urllib2.build_opener(cookies)
          urllib2.install_opener(opener)
      
          opener.open(login_url)
      
          try:
              token = [x.value for x in cookies.cookiejar if x.name == 'csrftoken'][0]
          except IndexError:
              return False, "no csrftoken"
      
          params = dict(username=username, password=password, \
              this_is_the_login_form=True,
              csrfmiddlewaretoken=token,
               )
          encoded_params = urllib.urlencode(params)
      
          with contextlib.closing(opener.open(login_url, encoded_params)) as f:
              html = f.read()
      
              print html
              # we're in.
      

      【讨论】:

      • 非常感谢您对此进行调查和回复!我在 Apache 端做了一些调试,没有发现其他任何东西。在尝试了一些事情时,我最终找到了问题并回答了我自己的问题。谢谢!
      • 没有问题。恭喜你弄明白了!奇怪的是我的没有失败。我认为这可能与 Nginx 正在处理 SSL 而 apache 不知道 SSL 的事实有关——但我认为我将中间件设置为在我自己的参数上设置 is_secure()。 PS:检查您自己的答案是否已接受,以便每个人都知道它已经解决了!
      猜你喜欢
      • 1970-01-01
      • 2014-07-06
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多