JWT安全(网站渗透3)
之前好像就知道JWT是啥但是好像又懂的不是很透彻刚好有空学习和整理一下。参考博客 https://www.cnblogs.com/Hekeats-L/p/18909366JWT 安全JSON Web Tokens (JWTs)token基础的认证API的兴起API是Application Programming Interface优点是兼容性好用同一个后台服务所有前端包括小程序客户端APP基于令牌的会话管理以前的“Cookie”方式浏览器自动帮忙以前当你登录一个网站时浏览器会自动帮你保管一个叫 Cookie 的小纸条。每次你访问这个网站浏览器都会自动把这个小纸条发给服务器告诉它“是我我登录过了”。整个过程不需要你手动操作很省心。现在的“令牌”方式需要手动干活基于令牌的会话管理是另一种新方法。它不依靠浏览器自动帮忙而是依赖程序员写的代码通常是JavaScript来手动处理。登录后拿令牌你输入用户名密码登录后服务器会返回一个特殊的字符串叫做令牌比如JWT。这个令牌不是被浏览器自动管理而是被 JavaScript 代码放到浏览器的LocalStorage可以理解为浏览器的“小仓库”里存起来。每次请求手动挂令牌当你访问页面或点击按钮需要向服务器发送请求时JavaScript 代码必须主动去那个“小仓库”里把令牌拿出来然后手动把它挂在请求的Header可以理解为快递包裹的贴纸里发给服务器。最常见的令牌类型之一是JSON Web令牌JWT它通过Authorization:Bearer标头传递。API项目记录API的常用方法是创建postman项目或者是swagger文件。实践这边房间给了一个练习的项目用curl来请求网页curl -H Content-Type: application/json -X POST -d { username : user, password : password1 } http://10.48.133.68/api/v1.0/example1解码一下再尝试一下用这个JWT来请求调用这个API的其他功能点curl -H Authorization: Bearer [JWT token] http://10.49.180.36/api/v1.0/example2?usernameY提示说不是admin把之前的jwt复制到jwt.io里面修改一下admin的参数的值把重新生成的JWT用来请求显示请求成功JSON Web TokensJWT是自包含的令牌可用于安全地传输会话信息。自包含令牌就是自身已经包含了所有需要的信息不需要再去数据库查询JWT的结构JWT由三个部分组成中间用点隔开1、Header(头部)指示签名的类型以及使用的签名算法比如说HS2562、Payload(载荷)JWT的“正文内容”包含什么一组组的声明claims注册声明JWT标准预定义的比如iss签发者、exp过期时间、sub主题比如用户ID公共/私有声明开发者自己定义的比如username “user”admin 1flag “THM{…}”3、Signature(签名)验证令牌有没有被篡改过。用Header里指定的算法把HeaderPayload加上一个密钥加密生成的签名算法JWT主要有三种核心算法1、None算法Header 里写着alg: none意思是不加签名JWT只有头部.载荷.(没有第三部分)2、对称签名算法如HS256用同一个密钥既负责签名又负责验证就是服务器里面存了个密钥签名 哈希(头部 载荷 密钥)服务器收到用密钥重新计算一遍3、非对称签名算法RSA用一对密钥私钥签名公钥验证服务器用私钥来签名任何人都可以用公钥来验证签名的有效性签名安全JWT的关键力量是签名。不过JWT也可以被加密简称JWEJWT签名后可以将其发送给客户端客户端可以在需要时使用此JWT。我们可以有一个集中式身份验证服务器它可以创建在多个应用程序上使用的JWT。然后每个应用程序都可以验证JWT的签名如果得到验证JWT中提供的声明可以被信任并采取行动。敏感信息泄露一种常见的基于cookie的会话管理方法是使用服务器端会话存储多个参数。例如在PHP中您可以使用$SESSION[‘var’]data来存储与用户会话相关的值。这些值不会在客户端公开因此只能在服务器端恢复。然而使用令牌当整个JWT发送到客户端时声明就会暴露出来。如果遵循相同的开发实践敏感信息可能会被披露。在实际应用中可以看到一些示例1、凭证泄露密码哈希甚至更糟糕的是明文密码作为声明发送。2、暴露内部网络信息如身份验证服务器的私有IP或主机名。举个例子curl -H Content-Type: application/json -X POST -d { username : user, password : password1 } http://10.49.180.36/api/v1.0/example1JWT中不要加入一些密码或标志来作为声明这些都应该存放在后台的服务器中。签名验证错误JWT的第二个常见错误是没有正确验证签名。如果签名未得到正确验证那么威胁行为者就可能能够伪造有效的JWT令牌来访问其他用户的账户。不认证签名不认证签名就可以把jwt进行任意的修改。不过其实不认证jwt签名的API不常见但是API中可能有某些端点会忽略验证。实践一下先对一个API进行登录请求curl -H Content-Type: application/json -X POST -d { username : user, password : password2 } http://10.49.180.36/api/v1.0/example2可以先测试一下有没有对签名进行验证直接把第二个点后面的内容去掉curl -H Authorization: Bearer [JWT Token] http://10.49.180.36/api/v1.0/example2?usernameuser发现确实没有进行签名验证那就对JWT进行修改成功越权访问虽然在普通API上很少看到这种情况但它经常发生在服务器到服务器的API上。在威胁行为者可以直接访问后端服务器的情况下JWT可以被伪造。代码层面没有对jwt签名进行验证的代码payload jwt.decode(token, options{verify_signature: False})对这个代码进行修改比如说用了对称签名算法payload jwt.decode(token, self.secret, algorithmsHS256)签名算法降级另一个常见问题是签名算法降级。JWT支持无签名算法这实际上意味着JWT不使用签名。这背后的想法是用于服务器到服务器的通信其中JWT的签名是在上游过程中验证的。因此第二服务器将不需要验证签名。然而假设开发人员没有锁定签名算法或者至少否认None算法。在这种情况下您可以简单地将JWT中指定的算法更改为None这将导致用于签名验证的库始终返回true从而允许您再次在令牌中伪造任何声明。实践一下先拿到一个正确的JWTcurl -H Content-Type: application/json -X POST -d { username : user, password : password3 } http://10.49.180.36/api/v1.0/example3对得到JWT进行修改修改alg的值为noneusername和admin的值也进行修改但是我输入进去以后提示错误重新对header部分用base64URL加密了一下注意要写None并且把去掉提示进入成功弱对称密钥如果使用弱对称签名算法则可以执行离线破解密钥。实践一下先请求一个原JWTcurl -H Content-Type: application/json -X POST -d { username : user, password : password4 } http://10.49.133.68/api/v1.0/example4把这个JWT保存到jwt.txt下载jwt的弱密钥字典用hashcat解密先找到Jwt的解码序列hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list解出来密钥是secret修改数据后重新加密签名算法混淆算法混淆攻击类似于降级攻击。如果使用非对称签名算法RS256可以将该算法降级为HS256。改完以后一些库会使用公钥作为对称签名算法由于公钥已知也就可以进行伪造。curl -H Content-Type: application/json -X POST -d { username : user, password : password5 } http://10.49.133.68/api/v1.0/example5由于jwt.io不是很好操作拿到公钥之后可以写个脚本import jwt public_key ADD_KEY_HERE payload { username : admin, admin : 1 } access_token jwt.encode(payload, public_key, algorithmHS256) print (access_token)里面会引用到jwt库由于PyJWT 正常情况下不允许把 RSA 公钥字符串直接当作 HMAC 密钥使用所以这边要注释掉对jwt模块中的algorithms.py文件的143-146行注释掉运行后会得到一个新的jwt然后重新发送curl请求JWT的生命周期在做JWT验证之前得先判断是否过期在JWT中一般有exp参数来设定存活的时间。所以exp的设定要符合常理因为JWT不能直接从服务器上踢除。JWT没有exp值这意味着它将是持久的。如果“exp”声明不存在如果签名得到验证大多数JWT库将接受令牌为有效。curl -H Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPU http://10.48.162.250/api/v1.0/example6?usernameadmin测试一下直接给flag交叉中继攻击听众声明JWT的“听众声明”配置不当引发的安全问题。假如一个公司有多个系统但是共用一个登陆服务器JWT中有个aud声明可以用来表明这个token是给哪个系统用的但是如果某个系统不检查aud字段就很容易造成交叉中继攻击。实践一下先根据题目发送一个登陆请求curl -H Content-Type: application/json -X POST -d { username : user, password : password7, application : appA } http://10.48.162.250/api/v1.0/example7得到一个JWT可以看到aud是appA的尝试一下给appA和appB发信息curl -H Authorization: Bearer [JWT Token] http://10.49.180.36/api/v1.0/example7?usernameadmin当然A和B都不会访问成功因为受众不同并且也不是admin再对appB发送一个登陆请求curl -H Content-Type: application/json -X POST -d { username : user, password : password7, application : appB } http://10.48.162.250/api/v1.0/example7发现这个是admin是1尝试一下用这个appB的JWT去访问appA发现拿到了admin的权限防御payload部分代码可以修改成如下payload jwt.decode(token, self.secret, audience[appA], algorithmsHS256)