首页 / 资讯中心 / 文章详情

Authentication / JWT 实战:从登录令牌到敏感信息泄露分析

Authentication / JWT 实战:从登录令牌到敏感信息泄露分析 ★ FEATURED ARTICLE
本文实验均在个人本地部署、合法授权的 OWASP Juice Shop 靶场环境中完成仅用于安全学习与漏洞分析。实际生产文档中不得保留真实 Token、Cookie、邮箱、密码、用户 ID、Basket ID 或其他敏感信息。一、实验目标与环境1.1 实验目标本次实验针对 OWASP Juice Shop 的登录认证流程与 JWT 使用方式进行分析。主要关注登录成功后服务端如何返回认证令牌。客户端如何通过 Bearer Token 表示登录身份。JWT Header / Payload / Signature 的基本结构。JWT Payload 中是否包含不应暴露的敏感字段。Authentication 与 Authorization 的区别。1.2 实验环境项目说明实验目标本地部署的 OWASP Juice Shop 授权靶场目标地址http://127.0.0.1:3000测试工具Burp Suite测试账号test1test.com漏洞类型Authentication / JWT二、正常登录流程2.1 捕获登录请求在登录界面输入测试账号test1test.com 测试密码test1。开启Burp Suite拦截POST /rest/user/login HTTP/1.1 Host: 127.0.0.1:3000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: application/json, text/plain, */* Accept-Language: en-US,en;q0.5 Accept-Encoding: gzip, deflate, br Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7ImlkIjoyNCwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InRlc3QxQHRlc3QuY29tIiwicGFzc3dvcmQiOiI1YTEwNWU4YjlkNDBlMTMyOTc4MGQ2MmVhMjI2NWQ4YSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiL2Fzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdC5zdmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImJpZCI6OSwiaWF0IjoxNzkwMjM1NTc2fQ.v1s4e3XaH-0vvYvpDxgrSDprwzQTdJ1TzMZbhgG6b3-WIB2TTjBK9X77O4tL7D3_fUuUBUPdIkjaY3yRPulQTZLstS7vtUuYEjTpWUOow6-OjIaRfdjXZHLjH-h9AYGgPo2On6xTBKy-Ot7TGCzFZf042OKnRXZeZVWAsKbHoGM Content-Type: application/json Content-Length: 45 Origin: http://127.0.0.1:3000 Connection: keep-alive Referer: http://127.0.0.1:3000/ Cookie: languagezh_CN; welcomebanner_statusdismiss; cookieconsent_statusdismiss; continueCodeP32pqXyQJYZbEekj1dKXIMSnnhXMc83TqXFR8tYRcknGn9B4Ol6VLvrWwM7x; continueCodeFindItJEnej6XmL2MbOGK5q1BNpgyZ0yytrzTEquDP4lJQ7DVr0WwaRdPkvx3oY9z1; continueCodeFixIt7r7BdZOzW026GnqpYQJyvDjxbKKhBXTePuJOAwK3RME5klmgV4b98XLeoNPp Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u0 {email:test1test.com,password:test1}从捕获到的登录请求可以看出客户端在登录时向后端发送了一个 POST /rest/user/login 请求请求体采用 JSON 格式提交用户邮箱和密码{ email: test1test.com, password: test1 }该请求的核心作用是向服务端提交用户凭证由服务端判断账号密码是否正确。请求头中的 Content-Type: application/json 表明请求体数据格式为 JSON后端会按照 JSON 格式解析其中的 email 和 password 字段。需要注意的是本次抓包中登录请求头里已经出现了 Authorization: Bearer JWT_TOKEN 字段。这说明当前浏览器环境中可能已经存在旧的登录状态或者 Burp 捕获的是一次已有登录态下再次发送的登录请求。严格来说普通登录请求本身并不依赖已有的 Bearer Token认证身份主要依赖请求体中的邮箱和密码。此外请求中的 Cookie 主要包含语言、欢迎横幅、挑战进度等状态信息例如 language、welcomebanner_status、continueCode 等。这些字段更多用于前端状态记录或靶场挑战进度维护并不是本次登录认证的核心凭证。综上登录请求阶段的核心数据是请求路径、请求方法、JSON 请求体中的邮箱和密码。服务端会根据这些凭证完成 Authentication即身份认证过程。2.2 分析登录响应将捕获得到得登录请求发送至Repeater模块。发送后查看相应HTTP/1.1 200 OK Access-Control-Allow-Origin: * X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Feature-Policy: payment self X-Recruiting: /#/jobs Content-Type: application/json; charsetutf-8 Content-Length: 787 ETag: W/313-ixEfE1QZFnlZIWG3BLvZA/t8FFk Vary: Accept-Encoding Date: Tue, 29 Sep 2026 08:05:15 GMT Connection: keep-alive Keep-Alive: timeout5 {authentication:{token:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkYXRhIjp7ImlkIjoyNCwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InRlc3QxQHRlc3QuY29tIiwicGFzc3dvcmQiOiI1YTEwNWU4YjlkNDBlMTMyOTc4MGQ2MmVhMjI2NWQ4YSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiL2Fzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdC5zdmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImJpZCI6OSwiaWF0IjoxNzkwNjY5MTE2fQ.ZeNr9GwjClOcy_exp60SOm30oNDaUS5roImseWC9YxdWsoHuuuOdUs78GXAgxvLxgDyShbfH6HaAKCO9xKQh-uicGLBN-PvudsOSf_XAbI-7WVIAU81cw35Yd1Wcpl5RAL31FuNkYpj-Iuc0uScG4kLherv-1s01GsrFFSNJjFg,bid:9,umail:test1test.com}}将登录请求发送至 Repeater 后服务端返回 HTTP/1.1 200 OK说明账号密码验证通过登录请求被成功处理。响应体中最关键的字段是 authentication 对象{ authentication: { token: JWT_TOKEN, bid: 9, umail: test1test.com } }其中token 字段是服务端签发给客户端的 JWT。客户端后续访问需要登录身份的接口时会将该 Token 放入请求头中例如Authorization: Bearer JWT_TOKEN这表示客户端正在使用该 Token 声明自己的登录身份。JWT 通常由三部分组成Header.Payload.SignatureHeader 部分用于说明令牌类型和签名算法Payload 部分用于存放用户相关声明信息Signature 部分用于校验 Token 是否被篡改。在本次实验中将 Token 解码后可以看到 Payload 中包含用户 ID、邮箱、角色、购物车 ID、头像路径、创建时间等信息(解码后文本在后文揭晓。需要特别注意的是Payload 中还出现了 password 字段虽然这里保存的是哈希值而不是明文密码但仍然属于不应暴露给客户端的敏感字段。JWT 的 Payload 只是经过 Base64URL 编码并不是加密。任何拿到 Token 的人都可以解码查看 Payload 内容。因此不应在 JWT Payload 中存放密码哈希、密钥、隐私数据或其他敏感字段。从响应结果可以看出登录成功后服务端不仅返回了 JWT还返回了 bid 和 umail 字段。其中 bid 可以理解为当前用户对应的购物车 IDumail 表示当前登录用户邮箱。这些字段会被前端用于后续业务请求。2.3 初步结论结合登录请求和登录响应可以看出OWASP Juice Shop 的正常登录认证流程大致如下客户端向 /rest/user/login 接口提交邮箱和密码。 服务端校验用户凭证是否正确。 如果认证成功服务端生成并返回 JWT。 客户端保存 JWT并在后续请求中通过 Authorization: Bearer JWT_TOKEN 携带该令牌。 服务端根据 Token 识别当前用户身份并决定是否允许其访问对应资源。因此JWT 在该流程中承担的是“登录成功后的身份凭证”作用。它不是用户密码本身而是服务端签发给客户端的认证令牌。需要区分的是Authentication 和 Authorization 并不是同一个概念。Authentication 解决的是“你是谁”的问题例如用户通过邮箱和密码登录服务端确认该用户身份。Authorization 解决的是“你能访问什么”的问题例如当前用户是否有权限访问某个购物车、订单、后台管理接口或其他用户的数据。本次实验中登录流程主要体现的是 Authentication。用户登录成功后拿到 JWT说明身份认证通过。但这并不意味着该用户可以访问所有资源。后续接口仍然需要在服务端进行权限校验否则就可能产生越权访问问题。另外本次实验也暴露出一个 JWT 设计上的安全问题Token Payload 中包含了过多用户信息甚至包含密码哈希字段。由于 JWT Payload 可以被客户端直接解码查看因此服务端在设计 Token 内容时应遵循最小化原则只放入必要的身份标识信息例如用户 ID、角色、签发时间、过期时间等不应放入密码哈希、隐私字段或其他敏感数据。综上Juice Shop 的登录流程可以概括为账号密码完成身份认证JWT 作为后续请求的身份凭证Bearer Token 负责在请求头中传递该凭证而真正的资源访问安全还依赖服务端后续的权限校验。三、JWT 结构分析3.1 JWT 三段结构JWT 的基本结构由三部分组成Header.Payload.Signature本次登录响应中返回的 Token 同样符合该结构。按照.进行分割后可以得到三段内容部分观察结果HeadereyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9PayloadeyJkYXRhIjp7ImlkIjoyNCwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InRlc3QxQHRlc3QuY29tIiwicGFzc3dvcmQiOiI1YTEwNWU4YjlkNDBlMTMyOTc4MGQ2MmVhMjI2NWQ4YSIsInJvbGUiOiJjdXN0b21lciIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiL2Fzc2V0cy9wdWJsaWMvaW1hZ2VzL3VwbG9hZHMvZGVmYXVsdC5zdmciLCJ0b3RwU2VjcmV0IjoiIiwiaXNBY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsInVwZGF0ZWRBdCI6IjIwMjYtMDktMjQgMDc6Mzg6NTUuMTQyICswMDowMCIsImRlbGV0ZWRBdCI6bnVsbH0sImJpZCI6OSwiaWF0IjoxNzkwNjY5MTE2fQSignatureZeNr9GwjClOcy_exp60SOm30oNDaUS5roImseWC9YxdWsoHuuuOdUs78GXAgxvLxgDyShbfH6HaAKCO9xKQh-uicGLBN-PvudsOSf_XAbI-7WVIAU81cw35Yd1Wcpl5RAL31FuNkYpj-Iuc0uScG4kLherv-1s01GsrFFSNJjFg其中Header 和 Payload 可以通过 Base64URL 解码直接查看内容Signature 是服务端使用私钥对 Header 和 Payload 计算得到的签名值主要用于校验 Token 是否被篡改不能像普通 JSON 一样直接解码成可读字段。3.2 Header 解码对 JWT 第一段 Header 进行 Base64URL 解码后可以得到如下内容{typ:JWT,alg:RS256}其中typ: JWT表示该令牌类型为 JWT。alg: RS256表示该 JWT 使用 RS256 签名算法。RS256 属于非对称签名算法服务端使用私钥生成签名验证方可以使用对应公钥验证签名是否正确。这里需要注意Header 中的算法字段只说明该 Token 声称使用的签名算法真正判断 Token 是否可信还需要服务端在验证阶段正确校验签名。3.3 Payload 解码对 JWT 第二段 Payload 进行 Base64URL 解码后可以得到如下内容{data:{id:24,username:,email:test1test.com,password:5a105e8b9d40e1329780d62ea2265d8a,role:customer,deluxeToken:,lastLoginIp:0.0.0.0,profileImage:/assets/public/images/uploads/default.svg,totpSecret:,isActive:true,createdAt:2026-09-24 07:38:55.142 00:00,updatedAt:2026-09-24 07:38:55.142 00:00,deletedAt:null},bid:9,iat:1790669116}可以看到Payload 中主要包含三类信息第一类是用户身份信息例如id、email、role。这些字段用于表示当前登录用户是谁以及该用户在系统中的角色。第二类是业务相关信息例如bid。结合前文登录响应中的bid: 9可以判断该字段表示当前用户对应的 Basket ID也就是购物车 ID。第三类是用户账户状态与扩展字段例如isActive、createdAt、updatedAt、deletedAt、profileImage、lastLoginIp、totpSecret等。这些字段并非每次接口鉴权都必须使用但仍被放入了 JWT Payload 中。其中iat表示 issued at即 Token 签发时间。这里的1790669116对应的时间约为2026-09-29 08:05:16 UTC与前文响应头中的时间Tue, 29 Sep 2026 08:05:15 GMT基本一致说明该 Token 是本次登录成功后由服务端新签发的。需要重点注意的是Payload 中出现了password字段值为5a105e8b9d40e1329780d62ea2265d8a虽然这里不是明文密码而是密码哈希值但仍然不应该出现在 JWT Payload 中。因为 JWT 的 Payload 只是 Base64URL 编码不是加密。任何拿到 Token 的人都可以直接解码查看其中内容。3.4 敏感字段观察字段是否出现风险说明用户 ID是id: 24用户 ID 属于身份标识信息暴露后可能被用于后续越权测试、用户枚举或接口参数猜测。邮箱是email: test1test.com邮箱属于用户身份信息暴露后可能造成隐私泄露也可能被用于撞库、钓鱼或用户枚举。角色是role: customer角色字段会暴露当前用户权限级别。如果服务端错误信任客户端可控的角色字段可能引发权限绕过。Basket ID是bid: 9Basket ID 属于业务对象标识。若后端接口未校验购物车归属关系可能引发水平越权访问。密码哈希是password: 5a105e8b9d40e1329780d62ea2265d8a即使不是明文密码密码哈希也不应返回给客户端。攻击者拿到哈希后可能尝试离线破解或彩虹表查询。TOTP / 二次验证字段是totpSecret: 当前值为空但字段本身不应暴露。如果真实环境中该字段存在有效值会直接影响二次验证安全性。其他敏感字段是lastLoginIp、deluxeToken、createdAt、updatedAt等这些字段会暴露用户登录状态、账户时间信息或业务扩展信息。虽然部分字段当前为空但仍不建议全部放入 JWT。3.5 本节小结通过对 JWT 的 Header 和 Payload 进行解码可以发现Juice Shop 登录成功后返回的 Token 中包含了较完整的用户对象信息。从认证流程角度看该 JWT 可以作为客户端后续请求的身份凭证。客户端访问需要登录态的接口时只需要在请求头中携带Authorization: Bearer JWT_TOKEN服务端即可根据该 Token 识别当前用户身份。但是从安全设计角度看该 JWT Payload 中包含的信息过多尤其是password密码哈希字段不应返回给客户端。JWT 的 Payload 只是编码不是加密因此不能把它当成安全存储空间。更合理的做法是JWT Payload 中只保留必要的最小身份声明例如{id:24,role:customer,iat:1790669116,exp:1790672716}其中id用于标识用户身份。role用于辅助权限判断。iat表示签发时间。exp表示过期时间。密码哈希、二次验证密钥、邮箱、登录 IP、完整用户对象、购物车 ID 等信息都不应直接放入 JWT Payload 中。因此本节可以得出初步结论JWT 可以用于登录后的身份认证但 JWT Payload 不应存放敏感数据。服务端应遵循最小化原则设计 Token 内容并在后续业务接口中继续进行严格的权限校验。四、Bearer Token 使用验证本节选择当前登录用户的购物车接口作为验证目标GET /rest/basket/9 HTTP/1.1 Host: 127.0.0.1:3000其中9来自前文 JWT Payload 中的bid字段表示当前用户对应的 Basket ID。为了验证服务端到底依赖哪个位置的 Token 识别登录身份本节设计四组对照实验测试组Authorization Bearer TokenCookie 中的 token测试目的第一组有有正常登录状态基线第二组无无验证无 Token 时是否被拒绝第三组有无验证 Authorization 头是否可单独完成认证第四组无有验证 Cookie 中的 token 是否可单独完成认证需要说明的是请求中的其他 Cookie例如language、welcomebanner_status、continueCode等主要与前端状态或靶场挑战进度有关不作为本节认证测试的核心对象。本节重点观察的是Authorization: Bearer JWT和Cookie: tokenJWT。4.1 第一组同时携带 Authorization 与 Cookie Token第一组保留浏览器正常请求中的两处 TokenGET /rest/basket/9 HTTP/1.1 Host: 127.0.0.1:3000 Authorization: Bearer JWT_TOKEN Cookie: ...; tokenJWT_TOKEN服务端响应结果如下HTTP/1.1 200 OK Content-Type: application/json; charsetutf-8响应体中返回了购物车资源{status:success,data:{id:9,UserId:24,Products:[{id:51,name:浆果汁 (1000ml),BasketItem:{ProductId:51,BasketId:9,id:28,quantity:1}}]}}检查项结果HTTP 状态码200 OK是否返回用户资源是返回资源归属Basket ID 为9UserId 为24结论同时携带 Authorization 和 Cookie Token 时请求可以正常访问当前用户购物车资源。该组属于正常登录态下的基线请求用于确认目标接口和 Token 本身有效。4.2 第二组不携带 Authorization也不携带 Cookie Token第二组删除Authorization请求头同时删除 Cookie 中的tokenJWT字段仅保留其他普通 CookieGET /rest/basket/9 HTTP/1.1 Host: 127.0.0.1:3000 Cookie: languagezh_CN; welcomebanner_statusdismiss; cookieconsent_statusdismiss; ...服务端响应结果如下HTTP/1.1 401 Unauthorized Content-Type: application/json; charsetutf-8响应体中返回错误信息{error:{message:No Authorization header was found,name:UnauthorizedError,code:credentials_required,status:401,inner:{message:No Authorization header was found}}}检查项结果HTTP 状态码401 Unauthorized是否返回用户资源否错误信息No Authorization header was found结论不携带有效认证凭证时服务端拒绝访问购物车资源。该结果说明/rest/basket/9不是公开接口访问该接口需要认证凭证。服务端错误信息明确指出缺少Authorization请求头。4.3 第三组只携带 Authorization Bearer Token第三组保留Authorization: Bearer JWT但删除 Cookie 中的tokenJWT字段GET /rest/basket/9 HTTP/1.1 Host: 127.0.0.1:3000 Authorization: Bearer JWT_TOKEN Cookie: languagezh_CN; welcomebanner_statusdismiss; cookieconsent_statusdismiss; ...服务端响应结果如下HTTP/1.1 200 OK Content-Type: application/json; charsetutf-8响应体中同样返回了当前用户购物车资源{status:success,data:{id:9,UserId:24,Products:[{id:51,name:浆果汁 (1000ml),BasketItem:{ProductId:51,BasketId:9,id:28,quantity:1}}]}}检查项结果HTTP 状态码200 OK是否返回用户资源是返回资源归属Basket ID 为9UserId 为24结论仅携带Authorization: Bearer JWT时请求可以通过认证并访问当前用户资源。该结果说明对于该接口而言Authorization请求头中的 Bearer Token 可以单独作为登录身份凭证使用。4.4 第四组只携带 Cookie Token第四组删除Authorization请求头仅保留 Cookie 中的tokenJWT字段GET /rest/basket/9 HTTP/1.1 Host: 127.0.0.1:3000 Cookie: languagezh_CN; ...; tokenJWT_TOKEN服务端响应结果如下HTTP/1.1 401 Unauthorized Content-Type: application/json; charsetutf-8响应体中返回错误信息{error:{message:No Authorization header was found,name:UnauthorizedError,code:credentials_required,status:401,inner:{message:No Authorization header was found}}}检查项结果HTTP 状态码401 Unauthorized是否返回用户资源否Cookie 中是否存在 token是错误信息No Authorization header was found结论仅 Cookie 中存在tokenJWT时服务端仍然拒绝访问说明该接口没有使用 Cookie token 完成认证。该组实验比较关键。虽然请求中确实携带了Cookie: tokenJWT但是服务端仍然返回401 Unauthorized并提示No Authorization header was found。这说明在当前接口的认证逻辑中后端主要检查的是Authorization请求头而不是 Cookie 中的 token 字段。4.5 四组结果汇总测试组Authorization Bearer TokenCookie tokenHTTP 状态码是否返回购物车资源结论第一组有有200 OK是正常登录态请求成功第二组无无401 Unauthorized否无认证凭证请求被拒绝第三组有无200 OK是Authorization Bearer Token 可以单独完成认证第四组无有401 Unauthorized否Cookie token 不能单独完成该接口认证4.6 本节小结通过四组对照实验可以得出以下结论第一/rest/basket/9是需要认证后才能访问的用户资源接口。不携带有效认证凭证时服务端会返回401 Unauthorized。第二Authorization: Bearer JWT可以单独完成该接口的身份认证。当请求头中存在有效 Bearer Token 时服务端能够识别当前用户身份并返回对应的购物车资源。第三Cookie 中的tokenJWT不能单独完成该接口认证。第四组实验中即使 Cookie 中存在 JWT服务端仍然返回No Authorization header was found说明当前接口的后端认证逻辑主要依赖Authorization请求头。第四第一组同时携带 Authorization 和 Cookie Token 能够请求成功但结合第三组和第四组结果可以判断请求成功的关键因素是Authorization: Bearer JWT而不是 Cookie 中的tokenJWT。因此在本实验中Bearer Token 的作用可以概括为客户端在登录成功后将服务端签发的 JWT 放入Authorization请求头中后端通过该 Token 识别当前登录用户。Cookie 中即使保存了 token也不代表后端一定会使用它进行接口认证具体是否生效取决于服务端认证中间件的实现方式。这里也进一步说明了 Authentication 与 Authorization 的区别Authorization请求头是 HTTP 中传递认证凭证的位置。Bearer Token 是客户端提交给服务端的身份凭证。Authentication 解决“你是谁”的问题。Authorization 解决“你是否有权限访问该资源”的问题。在本节实验中服务端首先通过 Bearer Token 完成 Authentication识别当前用户为UserId: 24随后返回该用户对应的Basket ID: 9资源。五、Authentication 与 Authorization 区分在分析 JWT 和 Bearer Token 时需要区分两个概念Authentication 和 Authorization。Authentication身份认证解决“你是谁”的问题。 Authorization权限校验解决“你能访问什么”的问题。虽然请求头名称是Authorization但其中携带的Bearer Token首先用于让服务端识别当前用户身份。服务端识别出用户之后还需要继续判断该用户是否有权限访问具体资源。5.1 AuthenticationAuthentication 指身份认证即服务端确认当前请求来自哪个用户。在本实验中身份认证主要体现在两处第一用户通过登录接口提交邮箱和密码服务端校验成功后返回 JWT{authentication:{token:JWT_TOKEN,bid:9,umail:test1test.com}}第二客户端在访问购物车接口时携带Authorization: Bearer JWT_TOKEN第四章实验中携带有效 Bearer Token 时请求/rest/basket/9返回200 OK并返回UserId: 24、Basket ID: 9等用户相关资源不携带Authorization请求头时服务端返回401 Unauthorized错误信息为{message:No Authorization header was found,code:credentials_required}因此本实验中可以判断服务端通过Authorization: Bearer JWT_TOKEN识别当前登录用户。这个过程属于 Authentication。5.2 AuthorizationAuthorization 指权限校验即服务端判断当前用户是否有权访问某个具体资源。认证成功只说明“当前请求来自哪个用户”并不代表该用户可以访问所有资源。例如当前 JWT Payload 中可以观察到{data:{id:24,email:test1test.com,role:customer},bid:9}这说明当前登录用户是UserId: 24角色为普通用户对应购物车为Basket ID: 9。当用户访问自己的购物车时GET /rest/basket/9 HTTP/1.1 Authorization: Bearer JWT_TOKEN服务端返回对应资源{status:success,data:{id:9,UserId:24}}这说明当前用户不仅完成了身份认证而且访问的是与自己身份匹配的资源。但是如果将请求中的资源 ID 修改为其他用户的 Basket ID例如GET /rest/basket/OTHER_BASKET_ID HTTP/1.1 Authorization: Bearer JWT_TOKEN此时服务端不能只检查 Token 是否有效还必须继续判断当前 Token 对应的 UserId是否有权访问这个 Basket ID如果服务端只验证“用户已登录”却没有校验“资源是否属于该用户”就可能产生越权访问问题。因此Authorization 的核心不是有没有登录而是登录用户是否有权限访问当前请求的具体资源。5.3 二者区别总结对比项AuthenticationAuthorization中文含义身份认证权限校验解决的问题你是谁你能访问什么本实验中的体现Bearer Token 让服务端识别当前用户为UserId: 24服务端应判断UserId: 24是否有权访问Basket ID: 9失败时常见状态码401 Unauthorized403 Forbidden或业务层权限拒绝典型风险Token 泄露、JWT 敏感字段暴露、签名校验错误水平越权、垂直越权、IDOR、角色权限绕过5.4 本节小结本实验中Bearer Token 主要用于身份认证。服务端通过Authorization: Bearer JWT_TOKEN识别当前用户身份。但身份认证成功并不等于拥有所有资源的访问权限。对于购物车、订单、用户信息、后台管理等接口服务端还必须继续进行权限校验确认当前用户是否有权访问目标资源。可以简单概括为Authentication证明“我是 UserId 24”。 Authorization证明“UserId 24 可以访问 Basket ID 9”。如果系统只做 Authentication而忽略 Authorization就可能导致越权访问漏洞。六、实验总结本次实验围绕 OWASP Juice Shop 的登录认证流程和 JWT 使用方式展开主要分析了登录成功后服务端如何返回 Token、客户端如何通过 Bearer Token 表示登录身份以及 JWT Payload 中是否存在敏感字段。通过实验可以确认用户登录成功后服务端会返回 JWT。客户端后续访问需要登录身份的接口时需要在请求头中携带Authorization: Bearer JWT_TOKEN在/rest/basket/9接口的四组对照实验中只有携带Authorization: Bearer JWT_TOKEN的请求可以正常返回购物车资源不携带 Authorization 请求头时即使 Cookie 中存在tokenJWT服务端仍然返回401 Unauthorized并提示{message:No Authorization header was found,code:credentials_required}因此可以判断在该接口中请求成功的关键因素是Authorization请求头中的 Bearer Token而不是 Cookie 中的 token 字段。同时通过 JWT 解码可以发现JWT Payload 中包含了用户 ID、邮箱、角色、Basket ID、账户状态字段以及密码哈希字段。由于 JWT Payload 只是 Base64URL 编码并不是加密任何获得 Token 的人都可以解码查看其中内容。因此JWT 中不应存放密码哈希、TOTP Secret、完整用户对象等敏感信息。本实验还进一步说明了 Authentication 和 Authorization 的区别。Bearer Token 主要用于身份认证即让服务端识别“当前请求来自哪个用户”但用户完成认证并不代表可以访问所有资源。对于购物车、订单、用户信息、后台管理等接口服务端仍然需要继续进行权限校验判断当前用户是否有权访问目标资源。综上本次实验可以得出以下结论1. JWT 可以作为登录后的身份凭证。 2. Bearer Token 通过 Authorization 请求头传递。 3. JWT Payload 可以被解码查看不应存放敏感信息。 4. Cookie 中存在 token 不代表接口一定会使用它完成认证。 5. Authentication 只解决“你是谁”的问题。 6. Authorization 还需要判断“你能访问什么”。 7. 只做身份认证、不做资源级权限校验可能导致越权访问漏洞。针对上述问题实际开发中应遵循以下安全原则1. JWT Payload 只保留必要字段例如用户 ID、角色、签发时间和过期时间。 2. 不在 JWT 中存放密码哈希、二次验证密钥、完整用户对象等敏感信息。 3. 服务端必须校验 JWT 签名不能只解码 Payload 后直接信任其中内容。 4. Token 应设置合理过期时间降低泄露后的风险。 5. 每个敏感业务接口都应进行资源归属校验和权限校验。本次实验的核心收获是JWT 和 Bearer Token 解决的是登录后的身份识别问题但接口安全不能只依赖“用户已登录”。真正安全的后端接口还需要在认证之后继续进行严格的权限校验。
阅读完成 · 觉得有帮助?
咨询建站