Django中csrf-token验证原理

众所周知,django通过CsrfViewMiddleware中间件来下发和校验csrf-token有效性的。

那么,这个中间到底是怎么实现csrf-token校验的呢?

  1. 首先,django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 csrf-token,并把这个 csrf-token 放在 cookie 里。然后每次 POST 请求都会带上这个 csrftoken。(这个csrftoken的有效期非常长(52周))
  2. 在前后端不分离的django项目中,可以通过{%csrf_token%}标签在表单中自动生成csrf-token,当提交这个表单时,csrf-token一并被提交。
  3. 表单提交后,CsrfViewMiddleware中间件会自动校验cookie和表单中的csrftoken是否一致,一致则是合法请求。

 

那么,问题又来了:

  1. 我每次刷新表单页面时,表单中的csrftoken也会更新,而cookie中的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.  --来自django官方文档

官方文档中说到,检验csrftoken时,只比较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添加到请求头中。

 

 

 

贴一个前后端分离项目中如何实现csrf-token校验:

前后端不分离可以使用{% csrf_token%}标签生成token,前后端不分离时可以自定义一个获取csrftoken的函数:


from django.middleware.csrf import get_token


def getToken(request):
    token = get_token(request)
    return HttpResponse(json.dumps({'token': token}), content_type="application/json,charset=utf-8")

前端表单提交前,可以通过ajax获取token并写入header中:'X-CSRFtoken':csrftoken

或者更为简便的方法:https://blog.csdn.net/bocai_xiaodaidai/article/details/97375420

 

 

 

 

你可能感兴趣的:(Django)