登录流程及防止跨域伪造攻击
第 1 步
浏览器使用 POST 方法发送登录请求。
服务器收到浏览器发来的请求,Flask 框架在处理请求的时候,利用 Flask-WTF 插件生成一个随机的字符串,下图所示:
这个就是 csrf_token
字段,它会经过 A 令牌生成器生成一个令牌塞到响应 body 的表单的隐藏域里。
第 2 步
每个请求都由一个单独的线程处理,这个线程会创建一个请求上下文对象,它有个 session 属性,属性值是类字典对象。
捎带脚,这个「 csrf_token
字段及其值」会作为「请求上下文对象的 session 属性值」的键值对,这个字典的键值对可能很多,例如 _id
(它是根据 IP 地址和 USER_AGENT 生成的 128 位字符串)、_user_id
是用户存在数据库里的主键等等。
先瞎编一个字典:
然后有一个 B 令牌生成器将「请求对象的 session 属性值」作为参数调用令牌生成器的 dumps 方法,就生成了一个字符串,它就是 session
:
这个 session 字符串会被扔到响应对象的头部字典的 Set-Cookie 键中,粗略代码如下:
response.headers.add('Set-Cookie', b'session={}'.format(session))
响应对象传回浏览器,浏览器设置 Cookies 后,Cookies 键值对中就有了 session 字段。
额外说明:浏览器首次访问网站时,Flask Web 应用也会在响应对象中提供一个 session ,如果响应 body 有表单,也会在隐藏域中提供 csrf_token 。
第 3 步
浏览器再次发送了一个请求,假设是带表单的 POST 请求,修改用户信息之类的操作。
这个请求对象里就携带了 cookies 和表单,表单里有 csrf_token 的加密令牌。
服务器收到请求后,把表单隐藏域中的值拿出来使用 A 令牌生成器解密并赋值给一个变量,我们假设它是 c1 。
把 cookies 里的 session 字段的值拿出来,使用 B 令牌生成器解密得到字典:
然后从中拿出 csrf_token 字段的值跟 c1 比较,就可以判断这个请求了。
如果坏人使用「跨域伪造攻击」,可以得到用户浏览器上的 cookies ,也就得到了 session ,但它得不到表单隐藏域 csrf_token ,这个 csrf_token 跟 session 是一套。
防止 CSRF 跨域伪造攻击
用户访问「某某银行」的网站,登录后,浏览器保存带有 session 的 Cookies
而 csrf_token 呢,它作为一个 40 位的字符串,会被加密成一个更复杂的字符串
这个字符串被放到响应对象的 body 中的表单的隐藏域里头,返回给浏览器
也就是说只有包含表单的页面才有这个 csrf_token ,下图所示:
- 现在攻击者诱导用户访问钓鱼网站,钓鱼网站获取用户的 Cookie
- 然后自动使用 POST 方法访问地址,这个地址就是转账请求,这个请求会带上偷来的 Cookie
- 虽然有偷来的 Cookie ,但没有表单的隐藏字段 csrf_token ,所以请求会失败