大家。 我正在开发一个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会让我失望,所以我先从第一页/表单load的cookiejar中取出它。就像我提到的,这适用于网站的http /开发版本。
具体来说,我试图通过https连接将凭据发布到登录页面/表单时得到403。在使用http连接的开发服务器上使用时,此方法有效。
没有Apache目录指令阻止访问该区域(我可以看到)。该脚本成功连接到没有发布数据的登录页面,所以我认为这会让Apache摆脱问题(但我可能是错的)。
我正在使用的python安装都使用SSL编译。
我还读过urllib2不允许通过代理进行https连接。我对代理不是很熟悉,所以我不知道使用远程机器上的脚本实际上是代理连接,是否会出现问题。这会导致访问问题吗?
据我所知,问题在于Cookie和帖子数据的结合,但我不知道从哪里拿到它。
任何帮助将不胜感激。感谢
答案 0 :(得分:5)
请原谅我回答我自己的问题,但是 - 为了记录,这似乎解决了它:
事实证明我需要将HTTP Referer标头设置为我发布登录信息的请求中的登录页面URL。
req.add_header( 'Referer', login_url )
原因在Django CSRF documentation上解释 - 特别是第4步。
由于我们在生产方面使用HTTPS并且DEBUG = False有点奇怪的服务器设置,我没有看到csrf_failure失败的原因(在这种情况下:'Referer check failed - no referer') DEBUG信息中的输出。我最终将这个失败原因打印到Apache error_log和STFW'd上。这导致我code.djangoproject/.../csrf.py和Referer标题修复。
答案 1 :(得分:4)
这适用于我在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.