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 被禁止,访问被拒绝

如果 URL 在数据中,POST 返回 403

django框架中form表单Post方法无法提交 Forbidden (403) CSRF verification failed. Request aborted.

HttpSecurity POST 403 Forbidden