解决Django提交表单报错:CSRF token missing or incorrect的问题

Miles
Miles
发布时间:2026-10-13 | 3人浏览过
原创

本文详细解析Django中常见的CSRF token missing or incorrect报错原因及解决方案。涵盖检查settings.py中的中间件配置、在模板中添加{% csrf_token %}标签,以及深入剖析Django CSRF验证机制中Secret与Token的关系,帮助开发者快速定位并修复跨站请求伪造保护相关的错误。

Django CSRF报错现象与原因分析

在Django框架中提交表单时,可能会遇到如下报错信息:

CSRF token missing or incorrect

具体的错误页面展示如下:

通过翻译软件查看,该错误的含义如下:

此错误通常出现在两种情况:一是确实发生了跨站请求伪造攻击,二是Django的CSRF机制未被正确配置或使用。对于表单提交场景,需要确保以下几点:

用户的浏览器已启用并接收Cookie。

视图函数将请求对象传递给了模板的渲染方法。

在模板中,所有POST请求的表单内部必须包含{% csrf_token %}模板标签,且目标地址为内部URL。

若未使用CsrfViewMiddleware中间件,则在使用csrf_token模板标签的视图以及接收POST数据的视图上,必须应用csrf_protect装饰器。

表单需携带有效的CSRF令牌。如果在登录其他浏览器标签页或登录后点击返回按钮,可能需要重新加载页面,因为登录成功后令牌会重新生成(旋转)。

当Django设置文件中的DEBUG值为True时,会显示此页面的帮助部分。若将DEBUG改为False,则仅显示初始错误消息。

可通过CSRF_FAILURE_VIEW设置来自定义此错误页面。

解决CSRF报错的具体步骤

3、解决办法:

(1)、检查项目根目录下的settings.py文件,确认是否已包含django.middleware.csrf.CsrfViewMiddleware:


MIDDLEWARE = [
  'django.middleware.security.SecurityMiddleware',
  'django.contrib.sessions.middleware.SessionMiddleware',
  'django.middleware.common.CommonMiddleware',
  'django.middleware.csrf.CsrfViewMiddleware',
  'django.contrib.auth.middleware.AuthenticationMiddleware',
  'django.contrib.messages.middleware.MessageMiddleware',
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

(2)、若确认settings.py中已正确配置该中间件但仍报错,请在HTML的form表单内部添加以下代码:

{% csrf_token %}

执行上述操作后,通常可解决前述错误。以上内容仅供参考学习,感谢阅读!

Django CSRF Token验证原理深入解析

补充知识:Django中csrf token验证原理

在我多年未维护的博客园中,有一篇初学Django时的笔记,记录了关于django-csrftoken的使用心得。当时内容几乎照搬官网示例,此后工作主要使用Flask,博客也未再维护。直到我的博客收到如下评论,这些问题确实让我感到困惑,随后我对此进行了深入研究。

1. Django是如何验证csrfmiddlewaretoken合法性的?

2. 每次刷新页面时,<input>标签中的csrf value都会更新,每次重新登录时cookie中的csrf令牌也会刷新,那么这两个csrf-token有什么区别?

image.png CSRF(Cross Site Request Forgery protection),中文简称跨站请求伪造保护。

Django在第一次响应来自某个客户端的请求时,会在服务器端随机生成一个token,并将其放置在Cookie中。随后,每次POST请求都会携带此token, 从而避免遭受CSRF攻击。

乍看之下似乎没有问题,但评论中的第三个问题指出:每次刷新页面,form表单中的token都会刷新,而Cookie中的token仅在每次登录时刷新。这引发了我对csrftoken验证方式的疑问,随后查阅了官方文档的解释。

When validating the ‘csrfmiddlewaretoken' field value, only the secret, not the full token, is compared with the secret in the cookie value. This allows the use of ever-changing tokens. While each request may use its own token, the secret remains common to all. This check is done by CsrfViewMiddleware.

官方文档指出,在验证token时,仅比较secret是否与Cookie中的secret值一致,而非比较整个token。

这又引出一个疑问:在同一次登录过程中,form表单中的token每次都会变化,而Cookie中的token保持不变,Django将那个salt存储在哪里才能保证验证通过呢?

直到查看源码后我才明白。


def _compare_salted_tokens(request_csrf_token, csrf_token):
  # Assume both arguments are sanitized -- that is, strings of
  # length CSRF_TOKEN_LENGTH, all CSRF_ALLOWED_CHARS.
  return constant_time_compare(
    _unsalt_cipher_token(request_csrf_token),
    _unsalt_cipher_token(csrf_token),
  )

def _unsalt_cipher_token(token):
  """
  Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length
  CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt
  the second half to produce the original secret.
  """
  salt = token[:CSRF_SECRET_LENGTH]
  token = token[CSRF_SECRET_LENGTH:]
  chars = CSRF_ALLOWED_CHARS
  pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt))
  secret = ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok
  return secret

token字符串的前32位是salt,后面是加密后的token,通过salt能解密出唯一的secret。

Django会验证表单中的token和Cookie中token是否能解出相同的secret,若secret一致,则本次请求合法。

这也解释了为什么在AJAX请求时,需要从Cookie中获取token并添加到请求头中。

网上不少关于Django CSRF token验证原理的文章存在错误,原因在于他们不了解csrf-token的结构组成。我也是卡在第三条评论处,随后查阅官方文档及CsrfViewMiddleware中间件源码后才恍然大悟。

以上这篇解决Django提交表单报错:CSRF token missing or incorrect的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表