微软Titan JWT漏洞:为何upn不该做本地用户名

研究人员伪造JWT的upn字段为admin,利用微软内部服务缺失签名验证与字段误用双重缺陷获取未授权访问。
安全研究人员发现微软Titan内部服务的两处叠加缺陷:其一是服务端未正确校验JWT签名,允许攻击者随意篡改令牌载荷;其二是服务将 `upn`(User Principal Name)字段直接用于本地权限查找,使得将 `upn` 伪造为 `admin` 即可绕过授权逻辑。文章指出,`upn` 是可变且可控的用户标识,不适合作为权限系统主键;应改用 `oid` 等不可变唯一ID进行用户映射。这起事件的核心教训是:安全漏洞往往由多个看似合理的设计决策叠加而成,严格的签名验证与正确的标识符选择缺一不可。
一个被篡改的JWT,如何打开内部服务大门
一位安全研究人员通过将JWT(JSON Web Token)的 upn 声明设置为 admin,成功访问了微软的一个内部服务。这个看似简单的攻击链条背后,藏着两个关键的安全设计缺陷:缺失的签名验证是打开门的钥匙,而把 upn 当作本地查找键(lookup key)的做法,则是真正的漏洞所在。
这类问题在身份认证系统中并不罕见,但微软Titan服务上的这起案例,清晰地展示了当开发者对令牌字段的信任边界理解不足时,会造成怎样的连锁后果。

漏洞拆解:签名缺失 + 字段误用
缺失的签名:门没有锁
JWT的安全性核心在于签名验证。一个标准的JWT由三部分组成——头部、载荷(payload)和签名。载荷中的声明(claims)本质上只是经过Base64编码的明文数据,任何人都可以读取甚至修改。真正保证令牌不被篡改的,是服务端对签名的严格验证。
在这起案例中,内部服务没有正确校验令牌签名,等于给攻击者敞开了大门。研究人员可以随意伪造载荷内容,包括将 upn(User Principal Name,用户主体名称)字段直接改写为 admin。当签名校验这道防线失效时,令牌中的任何声明都不再可信。
JWT的签名算法声明在头部(header)中,这里存在一个经典的攻击变体:将算法声明改为 none,部分实现会直接跳过签名校验。另一种常见攻击是将算法从非对称的 RS256(RSA签名)改为对称的 HS256(HMAC签名),并用服务端的RSA公钥来"签名"——如果服务端实现存在缺陷,便会用同一把公钥去做HMAC验证,从而被攻击者伪造合法令牌。这说明签名验证不仅要检查签名值本身,还必须强制锁定允许的算法列表,而不是信任令牌头部自己声明的算法。
upn作为查找键:错误的信任假设
如果说签名缺失是打开门的钥匙,那么把 upn 当作本地用户名来查找权限,才是这个漏洞真正能造成危害的根源。
upn 本质上是一个用户可控或至少可伪造的标识符。当服务端直接拿 upn 的值去匹配本地的用户账户或权限记录时,一旦这个值能被攻击者操纵(比如伪造成 admin),整个授权逻辑就被绕过了。系统误以为请求来自管理员,从而授予了本不该有的访问权限。
为什么不该用upn做本地用户名
这起事件最有价值的教训,浓缩成一句话:永远不要把 upn 当作本地用户名使用。
原因在于标识符的稳定性和唯一性保证。upn 的设计初衷是用户友好的登录名,它可能随着用户邮箱变更、域名迁移而改变,并不保证在系统生命周期内恒定不变,也不是严格意义上的不可变唯一标识。相比之下,更可靠的做法是使用不可变的、全局唯一的标识符(如 oid / sub 这类对象ID或主体标识)来进行用户映射和权限查找。
当开发者用一个语义上可变、且可能被外部控制的字段作为权限系统的主键时,就埋下了身份混淆与越权的隐患。本案例中这个隐患被签名缺失所激活,但即便签名校验正常,用 upn 做查找键在某些场景下仍可能导致账户混淆风险。
在微软的身份体系中,oid(Object ID)是Azure AD中每个用户在租户内的不可变唯一标识,即便用户修改了邮箱、UPN或显示名称,oid 始终保持不变。sub(Subject)则是OAuth 2.0标准定义的主体标识,在同一应用内对同一用户保持稳定。相比之下,upn 对应用户的 user@domain.com 格式登录名,企业域名变更、账户重命名或跨租户迁移都可能导致其改变。在多租户场景下,不同租户完全可能存在相同 upn 的用户,直接用 upn 做全局匹配还会引发跨租户的身份混淆问题。正确的映射策略是以 oid + tid(Tenant ID)的组合作为用户的稳定主键。
给开发者的实践启示
从这起漏洞中,身份认证系统的开发者可以提炼出几条通用原则:
- 严格验证签名:任何来自客户端的JWT都必须完成完整的签名校验,包括算法、密钥和过期时间,绝不能跳过或弱化。
- 区分可变与不可变标识:权限查找、用户映射应基于不可变的唯一ID,而非
upn、邮箱等可能变动的字段。 - 最小信任原则:令牌中的声明在验证通过前一律视为不可信数据,尤其是用于授权决策的字段。
- 纵深防御:即使单点校验失效,后续的授权逻辑也应有独立的安全假设,避免一处疏漏导致全线崩溃。
这类漏洞往往不是单一错误造成的,而是多个看似合理的设计决策叠加的结果。签名验证的松懈与字段使用的随意,任何一环收紧都可能阻断攻击链。安全设计的价值,正体现在这些细节的严谨之中。
相关推荐

Codex Agent工程实战:测试与可观测性三层验证法
从Codex源码学习Agent工程测试与可观测性:三层验证框架、可控响应流模拟模型、断言位置设计、并发同步屏障、失败用例与遥测日志关联,帮你验证Agent行为是否真正符合预期。

让AI Agent连跑16天:Factory多Agent架构拆解
Factory拆解Missions多Agent架构:验证合约先于代码定义完成标准,串行执行加只读并行降低错误率,结构化交接防止上下文丢失,模型混编让规划、实现、验证各用其长。生产案例中最长连续运行16天,测试占最终代码50%。

让AI智能体自证"已完成":验证闭环决定智能体规模化上限
WorkOS团队在AI Engineer workshop上提出:智能体规模化的核心瓶颈是自我验证能力。本文解析验证CLI、Playwright断言、System 1语义判断三层闭环,以及CTX、Riker等实验工具如何让智能体自证完成、自愈失败。