手绘10张图,把CSRF跨域攻击、JWT跨域认证说得明明白白的( 五 )


  • 那我剩下的三种,我建议是使用 cookie 存储,但不使用 cookie 来鉴权 。服务器鉴权还是通过请求里的 Authorization 字段(通过js写入 Header 的) 。
  • 当然,如果你觉得你通过 Referer Check加验证码等其他手段,已经可以保证不受 CSRF 攻击的威胁,此时你使用 JWT ,就可以选择使用 JWT + cookie HttpOnly,扼杀 XSS 攻击的可能 。
     
    JWT 如何发送?通过上面第七节的描述,其实我也讲到了 JWT 根据不同场景可以选择两种发送方式
    • 第一种:将 JWT 放在 Header 里的 Authorization字段,并使用Bearer标注
     'Authorization': 'Bearer ' + ${token}
    • 第二种:把 JWT 放入 cookie ,发送给服务端,虽然发送 。但是不使用它来鉴权 。

    手绘10张图,把CSRF跨域攻击、JWT跨域认证说得明明白白的

    文章插图
     
    JWT 如何校验?后端收到请求后,从 Header 中取出 Authorization里的 JWT ,使用之前的签名算法对 header 和 payload 再次计算生成新的签名,并与 JWT 里的签名进行对比,如果一样,说明校验通过,是个合法的 Token 。
     HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)
    验证是个合法的 Token 后,还要检查这个 Token 是否过期,在 JWT 里的 payload 中,有 Token 的过期时间,可以通过它来检查 Token 是否可以用?
    payload 里同时还有用户的相关信息,有了这些信息后,后端就可以知道这是哪个用户的请求了,到这里一切都验证通过,就可以执行相关的业务逻辑了 。
    前面我使用了 pyjwt这个来生成 JWT ,事实上,这个库也可以用来验证 token 。
    使用 jwt 的 decode 会先验签再解码取得 payload 的信息 。
     >>> import jwt
    >>> token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQzMjZ9.kkEMhSx732lO6HWWNPNVQDHR9WuCEVxKgNol-LTbCP8"
    >>> jwt.decode(token, 'minggezuishuai', algorithms=['HS256'])
    {'user_id': '888f20d9-07ed-41bd-b329-17c6f058a14e', 'username': 'wangbm', 'exp': 1594434326}
    >>>
    验签同样也可以使用命令行
     $ pyjwt --key="minggezuishuai" decode eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiODg4ZjIwZDktMDdlZC00MWJkLWIzMjktMTdjNmYwNThhMTRlIiwidXNlcm5hbWUiOiJ3YW5nYm0iLCJleHAiOjE1OTQ0MzQ4NTl9.A792th12kY1YnBWyVgbr5l6OQ5emRiETIjsnmIl4Ji8
    {"user_id": "888f20d9-07ed-41bd-b329-17c6f058a14e", "username": "wangbm", "exp": 1594434859}
    如果不想验证签名及有效期,而只是想取下payload,只需加个--no-verify参数即可
     $pyjwt --key="minggezuishuai" decode --no-verify {token}
    更的详细使用方法,可以执行 pyjwt --help学习或者前往官方文档:https://pyjwt.readthedocs.io/en/latest/index.html
     
    JWT 的最佳搭配在真正的业务中,是有可能使用 payload 来存放一些用户的敏感信息的,由于 payload 是采用 Base64URL 转换而成,它是可逆的,因此当你在 payload 存放敏感信息时,需要保证 JWT 的安全性,不能让其暴露在 『阳光』下 。
    为此,JWT 最好与 HTTPS 配合使用,利用 HTTPS 的非对称加密来保证 JWT 的安全 。
    具体是如何保障的呢?
    HTTPS 是基于 SSL/TLS 的非对称加密算法工作的 。
    在非对称加密算法的规则下,服务器会拥有一个叫做『私钥』的东西,它是私有的,除了服务器之外,不能再有第二个人知道它 。
    而相对的,所有的客户端(浏览器)同时也会有一个叫做『公钥』的东西,它是对所有人公开的,任何人都可以拥有它 。它与『私钥』合称为一个密钥对 。
    公钥和私钥的规则是:
    • 公钥加密的东西,只有私钥能解 。因此如果 JWT 的 payload 里有你的敏感信息,那也不要紧,只要把 JWT 用公钥(前提是这个公钥得是正确的,下面会说到)加密一下,那黑客就算拿到了这个密文,也无法解密,因为私钥只有服务器才有 。
    • 私钥加密的东西,所有的公钥也都能解 。因此服务器发给客户端的 JWT 的payload 尽量不要有敏感信息 。

    手绘10张图,把CSRF跨域攻击、JWT跨域认证说得明明白白的

    文章插图
    那么问题又来了,如果客户端拿到的公钥,是黑客伪造的,客户端拿着这个假公钥加密自己的敏感信息,然后发出去,黑客在拿到这个用自己伪造的公钥加密的数据,非常开心,因为这个公钥对应的私钥在自己手里,自己是可以解密得到里面的数据的 。


    推荐阅读