POST 方法总是返回 403 Forbidden
Posted
技术标签:
【中文标题】POST 方法总是返回 403 Forbidden【英文标题】:POST method always return 403 Forbidden 【发布时间】:2012-05-26 15:59:42 【问题描述】:我已阅读 Django - CSRF verification failed 以及与 django 和 POST 方法相关的几个问题(和答案)。对我来说最好但不工作的答案之一是https://***.com/a/4707639/755319
所有获得批准的答案都表明至少 3 件事:
-
使用RequestContext作为render_to_response_call的第三个参数
使用 POST 方法在每个表单中添加 % csrf_token %
检查 settings.py 中的 MIDDLEWARE_CLASSES
我完全按照建议做了,但错误仍然出现。我使用 django 1.3.1(来自 ubuntu 12.04 存储库)和 python 2.7(默认来自 ubuntu)
这是我的观点:
# Create your views here.
from django.template import RequestContext
from django.http import HttpResponse
from django.shortcuts import render_to_response
from models import BookModel
def index(request):
return HttpResponse('Welcome to the library')
def search_form(request):
return render_to_response('library/search_form.html')
def search(request):
if request.method=='POST':
if 'q' in request.POST:
q=request.POST['q']
bookModel = BookModel.objects.filter(title__icontains=q)
result = 'books' : bookModel,
return render_to_response('library/search.html', result, context_instance=RequestContext(request))
else:
return search_form(request)
else:
return search_form(request)
这是我的模板(search_form.html):
% extends "base.html" %
% block content %
<form action="/library/search/" method="post">
% csrf_token %
<input type="text" name="q">
<input type="submit" value="Search">
</form>
% endblock %
我已经重启了服务器,但是403禁止错误仍然存在,提示CSRF验证失败。
我有 2 个问题:
-
如何解决此错误?
为什么在 django 中做一个“POST”这么难,我的意思是有什么具体的原因让它如此冗长(我来自 php,之前从未发现过这样的问题)?
【问题讨论】:
【参考方案1】:我可能错了,但是我发现上述解决方案相当复杂。
对我有用的只是将我的 csrf 令牌包含到我的发布请求中。
$.ajax(
type: "POST",
url: "/reports/",
data: csrfmiddlewaretoken: " csrf_token ", // < here
state:"inactive"
,
success: function()
alert("pocohuntus")
console.log("prototype")
)
【讨论】:
【参考方案2】:此答案适用于将来可能遇到同样问题的人。
Django 中表单所需的 CSRF csrf_token
模板标签可防止跨站点请求伪造。 CSRF 使客户端浏览器访问过的恶意站点可以向您自己的服务器发出请求。因此,django 提供的 csrf_token 使您的 django 服务器和站点可以很容易地被保护免受这种类型的恶意攻击。如果您的表单不受 csrf_token 保护,django 会返回 403 禁止页面。这是对您网站的一种保护形式,尤其是在没有故意遗漏令牌的情况下。
但在某些情况下,django 站点不想使用 csrf_token 保护其表单。例如,我开发了一个 USSD 应用程序,需要一个视图函数来接收来自 USSD API 的 POST 请求。我们应该注意到 POST 请求不是来自客户端上的表单,因此不可能存在 CSRF 风险,因为恶意站点无法提交请求。 POST 请求是在用户拨打 USSD 代码时收到的,而不是在提交表单时收到的。
换句话说,在某些情况下,函数需要获取 POST 请求而不需要 csrf_token。
Django 为我们提供了一个装饰器@csrf_exempt
。此装饰器将视图标记为不受中间件确保的保护。
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
@csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
Django 还提供了另一个装饰器,它与csrf_token
执行相同的功能,但它不会拒绝传入的请求。这个装饰器是@requires_csrf_token
。例如:
@requires_csrf_token
def my_view(request):
c =
# ...
return render(request, "a_template.html", c)
本文将提到的最后一个装饰器与 csrf_token 完全相同,称为@csrf_protect
。但是,单独使用这个装饰器并不是最佳实践,因为您可能会忘记将它添加到您的视图中。例如:
@csrf_protect
def my_view(request):
c =
# ...
return render(request, "a_template.html", c)
以下是一些可以更好地指导和解释的链接。
https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#module-django.views.decorators.csrf
https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/
http://www.squarefree.com/securitytips/web-developers.html#CSRF
【讨论】:
【参考方案3】:避免此类问题的最简单方法是使用render
快捷方式。
from django.shortcuts import render
# .. your other imports
def search_form(request):
return render(request, 'library/search_form.html')
def search(request):
q = request.GET.get('q')
results = BookModel.objects.all()
if q:
results = results.filter(title__icontains=q)
return render(request, 'library/search.html', 'result': results)
【讨论】:
必须尽快尝试一下,看起来很清楚,谢谢您的回答。使用这种方法有什么缺点吗?我想知道如果有如此清晰的语法,为什么文档和 djangobook 会提供如此冗长的语法 我知道这是一个旧线程,但我更新了它以进一步阐明搜索方法。希望这现在(更多)有用。 另外,搜索方法可能是唯一应该使用GET
而不是 POST
的方法,因为它们不会改变任何东西。【参考方案4】:
尝试将 RequestContext 放入 search_form 视图的 render_to_response:
context_instance=RequestContext(request)
【讨论】:
可以,谢谢你的回答。但是如何以及为什么?你能解释一下吗? docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-to-use-it - 阅读第 3 点 因为csrf_token
必须在您的视图中创建,以便 django 可以将其传递给模板。在您的情况下,由于您的搜索视图没有创建令牌,因此模板中的 % csrf_token %
是 empty string (None)
并且结果页面在验证时失败【参考方案5】:
响应是 403 bcoz, django 在您发出的每个 POST 请求中都需要一个 csrf 令牌(包含在发布数据中)。
有多种方法可以做到这一点,例如:
从cookie中获取token及方法在enter link description here文章中有说明
或
您可以使用模板中的 csrf_token 从 DOM 访问它
所以现在使用第二种方法:
var post_data =
...
'csrfmiddlewaretoken':" csrf_token "
...
$.ajax(
url:'url',
type:'POST'
data:post_data,
success:function(data)
console.log(data);
,
error:function(error)
console.log(error);
);
【讨论】:
【参考方案6】:您需要在回复中使用RequestContext
例如
在view.py
文件中
from django.template import RequestContext
def home(request):
return render_to_response('home.html',RequestContext(request, ))
【讨论】:
【参考方案7】:你也可以使用
direct_to_template(request, 'library/search.html', result)
而不是
render_to_response('library/search.html', result, context_instance=RequestContext(request))
因为direct_to_template
会自动添加RequestContext
。但请注意,direct_to_template
将被弃用,django 提议使用 CBV TemplateView
代替。
RequestContext
允许您使用上下文处理器。这是你的错误:% csrf_token %
输出空字符串,你得到 403。
【讨论】:
嗨,谢谢你的评论,看来我在熟悉 django 之前应该考虑很多事情:D【参考方案8】:这为我解决了这个问题。正如 Django 文档所说,在能够直接将 POST 方法发送到您的 RESTAPI 之前,需要完成一些处理。这是代码:
function getCookie(name)
let cookieValue = null;
if (document.cookie && document.cookie !== '')
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++)
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '='))
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
return cookieValue;
const csrftoken = getCookie('csrftoken');
console.log(csrftoken)
$.ajax(
method:'POST',
url:"//127.0.0.1:8000/app/homepage/", /*change it to your api endpoint*/
headers: 'X-CSRFToken': csrftoken,
data : "data" : "3"
)
我无法用这个解决方案保证的一件事是安全方面。
【讨论】:
以上是关于POST 方法总是返回 403 Forbidden的主要内容,如果未能解决你的问题,请参考以下文章
Node.js:POST - 请求方法:OPTIONS 状态代码:403 Forbidden
从 iCloud 获取 CalDAV 事件返回“403 Forbidden”
Spring Security 总是返回 403 被禁止,访问被拒绝
django框架中form表单Post方法无法提交 Forbidden (403) CSRF verification failed. Request aborted.