完全指南:algorithms 与 crit 的深度解析与实战用法)
网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载导读在 jose 库中VerifyOptions是所有 JWSJSON Web Signature验签操作共用的配置接口负责约束验签时允许哪些签名算法以及如何对待 JWS 头部中标记为 Critical 的扩展参数。本文将围绕 VerifyOptions 接口文档 展开结合 src/types.d.ts 的类型定义与 src/lib/options.ts 的底层校验实现逐一拆解algorithms与crit两个选项的含义、默认行为、错误语义与安全边界并通过 Compact、Flattened、General 三种序列化以及 JWT 验签场景给出可复制、可运行的实战示例。读完本文你将能精准配置 jose 的验签白名单、正确处理crit扩展头并理解这些选项在源码层面的真实执行流程。一、VerifyOptions 是什么一处定义、处处生效1.1 接口定义与继承关系VerifyOptions并不是一个孤立的接口它在类型体系中是JWS 验签选项与JWT 验签选项的共同基座// src/types.d.ts /** JWS Verification options. */ export interface VerifyOptions extends CritOption { algorithms?: JWSAlgorithm[] }algorithms属性直接声明在本接口上crit属性来自被继承的CritOption接口见 src/types.d.ts#L601-L619该接口被 Sign、Verify、Encrypt、Decrypt 全部操作共享。由此形成两条重要的应用链路JWS 验签链路compactVerify、flattenedVerify、generalVerify三个函数都以options?: VerifyOptions为第三个参数详见 src/jws/compact/verify.ts#L51-L54、src/jws/flattened/verify.ts 与 src/jws/general/verify.tsJWT 验签链路jwtVerify使用扩展接口JWTVerifyOptions extends types.VerifyOptions, types.JWTClaimVerificationOptions见 src/jwt/verify.ts#L13-L14因此algorithms、crit同样可直接传给jwtVerify。此外VerifyOptions还通过 src/index.ts#L125 从主入口导出是 jose 公共 API 的一部分。1.2 在验签流程中的位置从源码看验签函数的第一步就是通过prepareVerify(options)将这两个选项编译成验签共享状态见 src/lib/jws_verify.ts#L75-L77export function prepareVerify(options?: types.VerifyOptions): VerifyShared { return [options validateAlgorithms(algorithms, options.algorithms), options?.crit] }返回的三元组VerifyShared算法集合、crit 映射、可选的非 b64 载荷缓存会贯穿整个签名校验过程。也就是说VerifyOptions的生效点在任何密钥解析、任何签名验证之前属于验签流程的第一道闸门。二、algorithms验签算法白名单2.1 语义与默认行为按 VerifyOptions.md 的定义algorithms?: string[]是被接受的 JWSalgAlgorithm头部参数取值列表默认行为不传该选项时凡是适用于当前所用密钥/秘密的算法一律放行重要限制未受保护的 JWT即{ alg: none }永远不会被本 API 接受——即使algorithms里写了none也不会生效因为none根本不在可校验的算法体系内。2.2 底层实现与参数校验algorithms的解析发生在 src/lib/options.ts#L16-L29export function validateAlgorithms(option: string, algorithms?: string[]): Setstring | undefined { if ( algorithms ! undefined (!Array.isArray(algorithms) || algorithms.some((s) typeof s ! string)) ) { throw new TypeError(${option} option must be an array of strings) } if (!algorithms) { return undefined } return new Set(algorithms) }可以提炼出三个实现事实类型约束严格必须是字符串数组。混入非字符串元素如[null]、[42]会直接抛出TypeError: algorithms option must be an array of strings这一点被 test/jwt/verify.test.ts#L115-L123 的用例锁定转为 Set 提速合法输入会被转换为Set使后续alg 是否在白名单内的判断成为 O(1) 查找不传则放行undefined时返回undefined语义就是使用默认白名单。2.3 白名单判定与报错路径算法判定发生在validateJwsHeaders见 src/lib/jws_verify.ts#L88-L105const alg joseHeader.alg if (typeof alg ! string || !alg) { throw new JWSInvalid(JWS alg (Algorithm) Header Parameter missing or invalid) } if (shared[0] !shared[0].has(alg)) { throw new JOSEAlgNotAllowed(alg (Algorithm) Header Parameter value not allowed) }顺序很关键先校验头部缺省/非法再做白名单过滤。若 token 头部的alg不在algorithms白名单内会抛出错误码为ERR_JOSE_ALG_NOT_ALLOWED的JOSEAlgNotAllowed见 src/util/errors.ts。该错误在 test/jwt/verify.test.ts#L101-L114 中有直接验证对HS256签发的 JWT 传algorithms: [PS256]验签即抛alg (Algorithm) Header Parameter value not allowed。2.4 实战示例Compact JWS 验签收紧算法白名单import { compactVerify } from jose const jws eyJhbGciOiJFUzI1NiJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IHlvdXIgZG9vci4.kkAs_gPPxWMI3rHuVlxHaTPfDWDoqdI8jSvuSmqV-8IHIWXg9mcAeC9ggV-45ZHRbiRJ3obUIFo1rHphPA5URg // 只接受 ES256其余算法一律拒绝 const { payload, protectedHeader } await compactVerify(jws, publicKey, { algorithms: [ES256], }) console.log(protectedHeader) // { alg: ES256 } console.log(new TextDecoder().decode(payload))适用场景与建议多密钥/多算法环境下用algorithms固定白名单可显著缩小攻击面防止算法混淆/降级攻击如把 RSA 签名的 JWT 硬解释为 HMAC在jwtVerify中同样适用且可与其 JWT Claims 校验选项issuer、audience、clockTolerance等叠加使用与动态密钥解析函数getKey配合时同样生效因为prepareVerify在密钥解析之前就已执行。三、critCritical 扩展头参数的处理策略3.1 语义一个声明 检查映射表按 VerifyOptions.md 的定义crit?: { [propName: string]: boolean }是一个对象键是被识别的crit头参数名称值为true表示该参数必须被完整性保护即必须出现在 Protected Header 中值为false表示该参数是否受保护无关紧要可以出现在 Unprotected Header内置特例JWS 扩展头参数b64永远被识别并正确处理不需要在crit中声明除此之外目前没有任何注册的头参数享受这种内置待遇。⚠️ 重要警告文档原文强调crit选项只检查头参数在提供时是否语法正确、以及可选地是否被完整性保护。它不会处理该头参数本身也不会在参数缺失时拒绝操作。你必须在验签成功之后自行验证该参数确实存在并按照协议规范完成后续的校验步骤。3.2 底层校验逻辑全流程crit的完整校验实现在 src/lib/options.ts#L46-L96 的validateCrit中按序执行未受保护的 crit 即拒绝若 JOSE Header 中出现crit但 Protected Header 中没有抛JWSInvalid错误码ERR_JWS_INVALID消息为crit (Critical) Header Parameter MUST be integrity protected对应 test/jws/crit.test.ts#L8-L17结构合法性检查crit必须是非空字符串数组否则抛crit (Critical) Header Parameter MUST be an array of non-empty strings when present见 test/jws/crit.test.ts#L18-L27识别性检查crit中列出的每个参数必须出现在识别集合里该集合 选项中的crit映射合并内置的JWS_RECOGNIZED内置集合目前只有{ b64: true }见 src/lib/options.ts#L10-L14否则抛JOSENotSupportedERR_JOSE_NOT_SUPPORTEDExtension Header Parameter ... is not recognized存在性检查每个列出的参数必须实际存在于 JOSE Header 中用Object.hasOwn做自有属性判断见 test/jws/crit.test.ts#L112-L142constructor、toString、__proto__等继承属性都不算数完整性保护检查若该参数在crit映射中标记为true则它必须同时出现在 Protected Header 中否则抛Extension Header Parameter ... MUST be integrity protected。此外还有两个值得注意的实现细节识别集合的合并顺序{ __proto__: null, ...recognizedOption, ...recognizedDefault }——用户选项在前、内置默认在后因此内置的b64: true永远生效用户无法用crit: { b64: false }覆盖生产端与消费端的差异见 src/lib/options.ts#L31-L44crit数组中出现重复值时生产端签名会抛crit (Critical) Header Parameter MUST NOT contain duplicate values而消费端验签容忍重复——因为 RFC 7515 只禁止生产方列重复名接收方仅可以认为其无效。这一差异被 test/jws/crit.test.ts#L93-L158 的两组用例分别覆盖。3.3 实战示例声明并校验自定义扩展头假设你的协议在 JWS 中加入自定义扩展头urn:example:foo并要求它必须被完整性保护import { FlattenedSign, flattenedVerify } from jose const key new Uint8Array(32) // 对称密钥仅用于演示 // 生产端把扩展头放进 Protected Header并在 crit 中声明 const jws await new FlattenedSign(new TextEncoder().encode(payload)) .setProtectedHeader({ alg: HS256, crit: [urn:example:foo], urn:example:foo: bar }) .sign(key) // 消费端声明识别该参数并要求它受完整性保护 const { payload } await flattenedVerify(jws, key, { crit: { urn:example:foo: true }, }) // ⚠️ 验签成功后仍需自行确认该头参数存在并按协议处理 console.log(new TextDecoder().decode(payload))对应的失败场景可在 test/jws/crit.test.ts 中找到等价断言把crit放进 Unprotected Header →ERR_JWS_INVALIDcrit列出的参数名未在选项中声明 →ERR_JOSE_NOT_SUPPORTED参数声明为true但只出现在 Unprotected Header →ERR_JWS_INVALID参数声明为false时放在 Unprotected Header 则可正常通过见 test/jws/crit.test.ts#L70-L75。3.4 内置 b64 扩展头一个开箱即用的特例b64base64url 编码载荷开关是 jose 唯一内置识别的 JWS 扩展头。即使crit选项完全为空只要 JWS 头中声明crit: [b64]验签端也会自动完成通过validateB64src/lib/options.ts#L98-L113强制b64必须是布尔值否则抛The b64 (base64url-encode payload) Header Parameter must be a boolean依据b64: false切换到非编码载荷验证路径Compact 序列化要求载荷必须是纯 ASCII见 src/lib/jws_verify.ts#L147-L153Flattened 序列化则接受字符串或Uint8Arraysrc/lib/jws_verify.ts#L211-L217。值得提醒jwtVerify会直接拒绝b64: false的 token见 src/jwt/verify.ts#L179-L181JWTs MUST NOT use unencoded payload因为 JWT 规范强制要求载荷采用 base64url 编码。普通 JWS 验签则无此限制。四、VerifyOptions 在三种 JWS 序列化与 JWT 验签中的统一语义VerifyOptions不区分序列化形态Compact、Flattened、General 三种验签函数共享同一套选项语义只是底层执行路径略有差异验签函数序列化选项参数共享校验内核compactVerifyCompact三段式字符串options?: VerifyOptionsverifyCompactflattenedVerifyFlattened JSON单签名options?: VerifyOptionsverifySignaturegeneralVerifyGeneral JSON多签名options?: VerifyOptionsverifySignature逐签名jwtVerifyCompact JWToptions?: JWTVerifyOptionsverifyCompact Claims 校验关键实现事实见 src/lib/jws_verify.ts#L226-L249Compact 路径先按.拆分为三段解析出 Protected Header 后立刻执行validateJwsHeaders——也就是说algorithms白名单在密钥解析之前就已生效。而 Flattened/General 路径src/lib/jws_verify.ts#L196-L223则先合并 Protected 与 Unprotected 头要求两者名称互不相交否则抛JWS Protected and JWS Unprotected Header Parameter names must be disjoint再统一执行算法白名单与 crit 校验。另一个值得注意的细节Compact 验签的verifyCompact会在密钥解析前对 token 做快照test/jws/compact.verify.test.ts#L79-L94防止getKey回调中篡改 token 组件影响校验输入——这保证了你传入的VerifyOptions所校验的头与最终验签的头是同一份。五、错误速查表VerifyOptions相关校验可能触发的错误均定义于 src/util/errors.ts实际报错消息以 src/lib/options.ts 与 src/lib/jws_verify.ts 为准错误类型错误码触发条件对应用例TypeError—algorithms不是纯字符串数组test/jwt/verify.test.ts#L115-L123JOSEAlgNotAllowedERR_JOSE_ALG_NOT_ALLOWEDtoken 的alg不在algorithms白名单test/jwt/verify.test.ts#L106-L114JWSInvalidERR_JWS_INVALIDcrit未受完整性保护 / 结构非法 / 所列参数缺失或未受保护test/jws/crit.test.ts#L7-L37JOSENotSupportedERR_JOSE_NOT_SUPPORTEDcrit列出未在选项中声明的扩展参数test/jws/crit.test.ts#L28-L36六、实践建议与安全边界总结生产环境务必显式传入algorithms白名单。默认按密钥放行虽方便但在多算法互通场景下可能意外接受你并不想支持的算法固定白名单是防算法混淆的第一道防线。crit只负责语法与完整性检查不负责业务语义。文档与源码反复强调验签成功后仍需自行确认扩展头存在并按协议处理。将crit视为协议合规性钩子而非业务校验器。不要把b64写进自定义crit映射。它是内置特例写与不写效果相同且用户映射无法覆盖内置的b64: true。在 JWT 场景叠加使用。jwtVerify接受VerifyOptions与 Claims 校验选项的组合可在一次调用内同时完成算法白名单 crit 合规 iss/aud/exp 校验。阅读源码加深理解选项解析见 src/lib/options.ts验签主流程见 src/lib/jws_verify.ts完整类型定义见 src/types.d.ts#L710-L720行为测试见 test/jws/crit.test.ts 与 test/jwt/verify.test.ts。赞分享网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载相关推荐jose 通用 JSON 序列化 JWS 验签实战generalVerify 的用法、选项与底层实现jose 通用 JSON 序列化 JWS 验签实战generalVerify 的用法、选项与底层实现 jose 的 generalVerify 用于验证 Ge网络安全认证鉴权后端jose 库 compactVerify() 完全指南Compact JWS 签名验证与动态密钥解析jose 库 compactVerify 完全指南Compact JWS 签名验证与动态密钥解析 导读 compactVerify 是 jose https:网络安全认证鉴权后端jose 紧凑序列化 JWS 签名验证实战compactVerify 全解析jose 紧凑序列化 JWS 签名验证实战compactVerify 全解析 compactVerify 是 jose 库中用于验证 Compact Seri网络安全认证鉴权后端上一篇秒懂Flink深入理解Flink四大基石之时间和水位线下一篇告别记事本3步搞定Notepads文件关联让编辑效率提升300%创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考