CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式python
django 经过中间件 django.middleware.csrf.CsrfViewMiddleware 来实现,咱们查看这个类 CsrfViewMiddleware,假设咱们用django跑一个项目,第一次打开页面,process_response 会设置一个cookie(csrf不用session的状况下) ,cookie的名称为 csrftoken ,能够在settings中设置 CSRF_COOKIE_NAME,咱们须要提交一个表单,process_view 中检测到 请求是用post方法,会将cookie中的 csrftoken和表单中提交的 csrfmiddlewaretoken值进行校验,校验经过则继续下去,失败返回403,请求失败。git
csrf怎么生成的django
CSRF_SECRET_LENGTH = 32 CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits def _get_new_csrf_string(): # 返回64位的随机随机字符串(字母加数字) return get_random_string(CSRF_SECRET_LENGTH, allowed_chars=CSRF_ALLOWED_CHARS) def get_token(request): if "CSRF_COOKIE" not in request.META: csrf_secret = _get_new_csrf_string() request.META["CSRF_COOKIE"] = _salt_cipher_secret(csrf_secret) else: csrf_secret = _unsalt_cipher_token(request.META["CSRF_COOKIE"]) request.META["CSRF_COOKIE_USED"] = True return _salt_cipher_secret(csrf_secret) 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() # 随机生成salt chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in salt)) # 将salt和secret的索引组合到一块儿 cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs) # 索引相加再除以chars的长度的余数做为索引查找到chars中对应的值拼接到一块儿 return salt + cipher # 将salt和cipher拼接一块儿返回
csrf 怎么校验的cookie
def constant_time_compare(val1, val2): """Return True if the two strings are equal, False otherwise.""" return hmac.compare_digest(force_bytes(val1), force_bytes(val2)) # force_bytes 转换为字节的函数 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), )
cookie中的 csrftoken:8JDrBEx0K2ia0KFGaYGCg9mYmzYANFMtqcrZsq3KMPrZJAwKXgjrnxH6T17TJoL3网络
form中的 csrfmiddlewaretoken:dGZNEIV1rwUbMJA4EDRHaJGHHydZLVEFv9NlvurLtj30vzr8rVuwh71Pe0miHEDfsession
用上述方法进行校验:dom
import hmac import string import datetime from decimal import Decimal _PROTECTED_TYPES = ( type(None), int, float, Decimal, datetime.datetime, datetime.date, datetime.time, ) CSRF_SECRET_LENGTH = 32 CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits def is_protected_type(obj): """Determine if the object instance is of a protected type. Objects of protected types are preserved as-is when passed to force_text(strings_only=True). """ return isinstance(obj, _PROTECTED_TYPES) def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Similar to smart_bytes, except that lazy instances are resolved to strings, rather than kept as lazy objects. If strings_only is True, don't convert (some) non-string-like objects. """ # Handle the common case first for performance reasons. if isinstance(s, bytes): if encoding == 'utf-8': return s else: return s.decode('utf-8', errors).encode(encoding, errors) if strings_only and is_protected_type(s): return s if isinstance(s, memoryview): return bytes(s) return str(s).encode(encoding, errors) def constant_time_compare(val1, val2): """Return True if the two strings are equal, False otherwise.""" return hmac.compare_digest(force_bytes(val1), force_bytes(val2)) 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 # force_bytes 转换为字节的函数 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), ) csrfmiddlewaretoken = "dGZNEIV1rwUbMJA4EDRHaJGHHydZLVEFv9NlvurLtj30vzr8rVuwh71Pe0miHEDf" csrf_token = "8JDrBEx0K2ia0KFGaYGCg9mYmzYANFMtqcrZsq3KMPrZJAwKXgjrnxH6T17TJoL3" print(_compare_salted_tokens(csrfmiddlewaretoken, csrf_token)) # 返回True