如何验证urllib2脚本以从Django站点访问HTTPS web服务?

2024-03-29 08:24:14 发布

您现在位置:Python中文网/ 问答频道 /正文

所有人。 我正在一个django/mod_wsgi/apache2网站上工作,该网站使用https为所有请求和响应提供敏感信息。如果用户未通过身份验证,则所有视图都将被编写为重定向。它还有几个视图,它们的功能类似于RESTful web服务。

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

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

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的csrf中间件会让我崩溃,所以我首先从第一个页面/表单加载的cookiejar中获取它。正如我所提到的,这适用于站点的http/开发版本。

特别是,当我试图通过https连接将凭据发布到登录页面/表单时,得到了403。此方法在使用http连接的开发服务器上使用时有效。

没有阻止访问该区域的Apache目录指令(我可以看到)。该脚本在没有post数据的情况下成功地连接到登录页面,因此我认为这将使Apache不存在问题(但我可能错了)。

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

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

据我所知,问题出在cookies和post数据的组合上,但我不清楚从哪里开始。

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


Tags: thedebug脚本nonelogurlifcookie
2条回答

这对我的django设置在https上工作,这是受你的启发。我开始认为问题出在这段代码之外。。。服务器在说什么吗?我很可能正在调查阿帕奇。

我在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.

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

结果我需要在发布登录信息的请求中将HTTP Referer头设置为登录页面url。

req.add_header( 'Referer', login_url )

原因在Django CSRF documentation-特别是步骤4中解释。

由于我们在生产端使用HTTPS并且DEBUG=False的服务器设置有点特殊,我没有看到通常在调试信息中输出的csrf_failure failure原因(在本例中为“Referer checking failed-no Referer”)。我最后把失败的原因打印到Apache错误日志和STFW上。这就引出了code.djangoproject/.../csrf.py和Referer头修复。

相关问题 更多 >