分析flask-wtf.csrf理解CSRF防御机制

2019-04-16  本文已影响0人  flashine

0x01 csrf token 生成

源文件位于flask_wtf/csrf.py

def generate_csrf(secret_key=None, token_key=None):
    secret_key = _get_config(   # 获取配置文件中的secret_key
        secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
        message='A secret key is required to use CSRF.'
    )
    field_name = _get_config(   # 获取csrf_token的自定义名称,默认为csrf_token
        token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token',
        message='A field name is required to use CSRF.'
    )
    if field_name not in g:
        if field_name not in session:  # 判断session中是否存在经过哈希加密的csrf_token,此处的session发现无法修
            session[field_name] = hashlib.sha1(os.urandom(64)).hexdigest() # 生成随机字符串并进行哈希加密
        s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token') # 将哈希字符串加盐和时间戳序列化
        setattr(g, field_name, s.dumps(session[field_name])) # 给g对象设置生成的csrf_token值
    return g.get(field_name)  # g为flask app 的上下文对象,用于与表单内容交互

0x02 校验csrf token

def validate_csrf(data, secret_key=None, time_limit=None, token_key=None):
    secret_key = _get_config(   # 获取secret_key
        secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
        message='A secret key is required to use CSRF.'
    )
    field_name = _get_config(    # 获取csrf_token的自定义名称,默认为csrf_token
        token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token',
        message='A field name is required to use CSRF.'
    )
    time_limit = _get_config(    # 获取csrf_token的过期时间限制,默认是3600秒,即一小时
        time_limit, 'WTF_CSRF_TIME_LIMIT', 3600, required=False
    )

    if not data:  # 判断是否传入了csrf_token
        raise ValidationError('The CSRF token is missing.')

    if field_name not in session: # 判断session中是否存在csrf_token
        raise ValidationError('The CSRF session token is missing.')

    s = URLSafeTimedSerializer(secret_key, salt='wtf-csrf-token') # 获取csrf_token 的序列化解析对象

    try:
        token = s.loads(data, max_age=time_limit) # 使用反解析对象解析用户提交的csrf_token,并验证token中的时间戳是否超出设置的时间
    except SignatureExpired: # 抛出签名过期异常
        raise ValidationError('The CSRF token has expired.')
    except BadData:        # 抛出csrf token无效异常,非正常序列化时会出现该异常
        raise ValidationError('The CSRF token is invalid.')

    if not safe_str_cmp(session[field_name], token):  # 对session中的值和token进行比对
        raise ValidationError('The CSRF tokens do not match.') # 发现不匹配是抛出异常

流程:

client:网站开始运行:用户打开表单,服务器生成csrf_token返回给用户页

server : 生成csrf_token哈希并添加到session中(后面不会再对该session值进行修改),然后将该token序列化处理后返回给上下文对象代理,该代理将token值传递给表单对象,表单对象再渲染到用户页模板中

client:用户完成表单填写,发送提交请求

server:使用form.validate_on_submit() 函数进行验证表单,然后开始调用form.validate_csrf_data校验token,(省略具体调试过程),获取序列化处理对象开始反解析用户提交的token,提取token中的时间戳并计算与当前时间相差的秒数,如果大于设置的过期时间,则返回“token过期”,若未过期则设置g对象属性token_valid=True。

由上述可知:csrf_token在用户请求生成的一小时之内是有效的,即用户如果多次携带该token发送请求该token依然有效。由于token使用了多种加密,致使攻击者无法伪造,那么攻击者在生成payload时必须需要跨域获取到该token,才能够进行csrf。
参考https://www.v2ex.com/t/289364

csrf_token原理
上一篇 下一篇

猜你喜欢

热点阅读