为防止CSRF攻击,我们需要在html页面加入{% csrf_token %}这样的代码。
但是,当我们打开多个页面的时候,你会发现,每个页面的这个csrfmiddlewaretoken还不一样,如下代码所示:
//页面1的部分代码
<input type="hidden" name="csrfmiddlewaretoken" value="aPhlhBx4ellSgZbxDiBYwLqGd8pC3Pt0bkSD7wedsiKAuDIDwIYPh1XdklDmmGcI">
//页面2的部分代码
<input type="hidden" name="csrfmiddlewaretoken" value="RgE7JUyRySSAh28ONcirBD1slwrnz2fhSLfpzPf0MPhivGFUGCFimTyZsJF7STYZ">
如果你修改csrfmiddlewaretoken的值,验证失败。所以,简单了解一下django如何验证csrfmiddlewaretoken。
从settings.py文件的MIDDLEWARE的配置项中有csrf的配置。
如果用的是pycharm的话,按住ctrl+鼠标左键,直接跳转到框架源码文件。
其实关于csrfmiddlewaretoken的加解密主要就两个函数。
def _salt_cipher_secret(secret):
"""
Given a secret (assumed to be a string of CSRF_ALLOWED_CHARS), generate a
token by adding a salt and using it to encrypt the secret.
"""
salt = _get_new_csrf_string()
chars = CSRF_ALLOWED_CHARS
pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in salt))
cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs)
return salt + cipher
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
下面写出简单的测试代码,来说明为什么每个页面的csrfmiddlewaretoken不一样,表单提交依然可以互不干扰验证通过。
其实csrfmiddlewaretoken字符串 = 随机salt字符串 拼接上 随机token字符串,其中,“随机token字符串”是根据“随机salt字符串”与“密钥字符串”计算得出的。
import django
import string
def get_secret(token):
salt = token[:32] # 前32位为加盐字符串
token = token[32:] # 后32位为token字符串
chars = string.ascii_letters + string.digits # 字符串为a-zA-Z0-9
print('chars: ', 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)
print('secret: ', secret) # 计算出密钥
if __name__ == '__main__':
print('django version: ', django.VERSION)
text = 'aPhlhBx4ellSgZbxDiBYwLqGd8pC3Pt0bkSD7wedsiKAuDIDwIYPh1XdklDmmGcI' # 表单中csrf_token的字符串,长度为64
text2 = 'RstkB5GMKSzlbnJksqy2vOWobk00SEgASX4Cr0nVYPY3p1gqlQVTg4tVixeKbvZi' # cookie中csrf_token的字符串,长度为64
get_secret(text)
get_secret(text2)
最后,我们再回去看csrfmiddlewaretoken的加解密函数,就不难理解了。