2013-11-27 203 views
4

我有一个django服务器来上传文件,当我使用浏览器时,我可以毫无问题地上传文件。python-requests和django - CSRF验证失败。请求中止

但是,如果我使用python-requests命令,它会告诉我CSRF验证失败。请求中止。蟒蛇,请求代码如下:

#upload via HTTP 
    file = {"docfile": open(fullfilename, "rb")} 
    s = requests.Session() 
    r = s.get(dhost) 
    r = s.post(dhost, files=file) 

如果我执行我的代码,我得到的代码403和错误CSRF验证失败。请求中止。给出失败的原因:CSRF令牌丢失或不正确。

但如果我看在我发送的头,我有Cookie集:

CaseInsensitiveDict({'Content-Length': u'84169', 
'Accept-Encoding': 'gzip, deflate, compress', 
'Accept': '*/*', 
'User-Agent': 'python-requests/2.0.1 CPython/2.7.3 Linux/3.6.11+', 
'Cookie': 'csrftoken=GOOIsG89i5oMCJO6594algTXooxoUeoL', 
'Content-Type': 'multipart/form-data; boundary=86ada00b4f6c41d5997293cce7a53b6b'}) 

你能告诉我,我应该为有这样的工作吗?

谢谢,

约翰。

+0

这段代码被作为一个POST执行?如果是这样,你使用表单吗?您可能需要将“{%csrf_token%}”添加到表单中。 – karthikr

回答

0

这取决于你想要做什么。

如果您不需要CSRF验证,则可以使用csrf_exempt修饰符。

或者您可以创建一个新的csrf_exempt视图,以便仅通过python请求进行访问。

+0

我明白了,但是为什么当我通过浏览器(如Chrome浏览器)上传文件时,它会工作? – user2040597

+0

https://docs.djangoproject.com/zh/dev/ref/contrib/csrf/ – jpwagner

11

它实际上一切正常,你只需要了解csrf的工作原理。当您在浏览器中加载页面时,您会在{% csrf_token %}中获得一个csrf令牌,因此,当您将数据发送到服务器时,还会沿着csrf令牌发送消息。

当您使用请求时,您会在'get'部分获取cookie,但您并未将它与“post”一起发送。没有它,你只是发送一个没有令牌的帖子请求,这意味着一个CSRF验证错误。为了解决这个问题,试试这个代码:

file = {"docfile": open(fullfilename, "rb")} 
s = requests.Session() 
r1 = s.get(dhost) 
csrf_token = r1.cookies['csrftoken'] 
r2 = s.post(dhost, files=file, data={'csrfmiddlewaretoken': csrf_token}, headers=dict(Referer=dhost)) 

如果这仅仅是为自己的使用情况,您可以使用csrf_exampt该视图上禁用CSRF:

@csrf_exempt 
def my_view(request): 
    ...whateva... 

但是请注意,这不是推荐解决方案,如果您打算启动你的服务器,它向公众开放

+0

我想稍后再公开发布。问题是,当我使用chrome浏览器时,它有效,我有选择文件按钮,然后是上传按钮,如果我点击了最后一个,该文件将上传到我的django web服务器上,并且可以在我的服务器上的文件夹中找到它。那么,为什么它不与python-requests命令一起工作,或者我应该使用哪些其他命令? – user2040597

+1

正如我所解释的,当使用浏览器时,您将发送csrf令牌以及发布请求。在python-requests代码中,没有'get'部分,你发送一个无符号请求,返回一个错误 – yuvi

+0

我已经更新了我的问题,因为即使我得到错误之前我有一个错误403 ...所以我不't不介意做一个得到之前,但我的问题是真的,我不能张贴:(。 – user2040597

2

如果我使用Python,请求命令,它告诉我CSRF验证 失败。但是,如果我看在我发送的头,我有Cookie集:

'Content-Length': u'84169', 
    'Accept-Encoding': 'gzip, deflate, compress', 
    'Accept': '/', 
    'User-Agent': 'python-requests/2.0.1 CPython/2.7.3 Linux/3.6.11+', 
    'Cookie': 'csrftoken=GOOIsG89i5oMCJO6594algTXooxoUeoL', 
    'Content-Type': 'multipart/form-data; boundary=86ada00b4f6c41d5997293cce7a53b6b 

你有两件事发送回服务器:

1)csrftoken饼干。

2)以下表单名称/值对:

"csrfmiddlewaretoken" = "csrf token here" 

一个requests会议将返回该cookie为你的照顾,但你必须添加表格名称/值对:

sess = requests.Session() 
r = sess.get(get_url) 
my_csrf_token = r.cookies['csrftoken'] 

with open('myfile.txt') as f: 
    r = sess.post(
     post_url, 
     data = { 
      "csrfmiddlewaretoken": my_csrf_token, 
     }, 
     files = {"myfile": f} 

) 

print r.status_code 
print r.text 

当你的Django HTML模板包含一个跨站请求伪造标签:

<form name="myMessage" 
     method="post" 
     class="signin" 
     action="/myapp/process_form/" 
     enctype="multipart/form-data"> 

{% csrf_token %} 

csrf tag被替换为隐藏的表单字段:

<input type="hidden" 
     value="RTpun6OhlRehRRa2nAIcTtFJk5WuWsLg" 
     name="csrfmiddlewaretoken"> 

的隐藏的表单字段发送一个附加的名称/值对到服务器与所有形式产生其他名称/值对沿。名称是"csrfmiddlewaretoken",值是csrf标记。 django检查cookie以及表单数据中的名称/值对。

顺便说一句,为了获得用于测试的CSRF令牌,你可以做一个GET请求,看起来像这样一个观点:

def myview(request): 
    from django.middleware.csrf import get_token 
    get_token(request) #This causes django to set the csrftoken cookie in the response 

    return HttpResponse('server received GET request') 
相关问题