ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

djang-csrf源码分析

2019-11-08 16:02:47  阅读:230  来源: 互联网

标签:return csrf djang token secret 源码 CSRF salt


前言

CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式

流程分析

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,请求失败。

代码解析

csrf怎么生成的

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 怎么校验的

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:dGZNEIV1rwUbMJA4EDRHaJGHHydZLVEFv9NlvurLtj30vzr8rVuwh71Pe0miHEDf

用上述方法进行校验:

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

标签:return,csrf,djang,token,secret,源码,CSRF,salt
来源: https://www.cnblogs.com/wbc827/p/11820808.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有