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
网友评论