NodeJS中修改AWS Cognito Token一个字符仍验证通过?
为什么修改AWS Cognito JWT的最后一个字符后,node-jose验证仍能通过?
这种情况完全不符合预期——正常来说,修改JWT签名部分的任意字符都应该导致验证失败。出现这个问题大概率是验证逻辑的容错性过强,或者代码中缺少了关键的校验约束,下面是具体分析和解决方案:
问题原因分析
node-jose默认验证逻辑的宽松性
默认情况下,jose.JWS.createVerify(keystore)会尝试用密钥库中的所有密钥去验证JWT,并且对算法的校验不够严格。如果你的密钥库中有多个密钥,极端情况下可能出现误匹配(虽然概率极低),或者旧版本node-jose存在签名校验的bug。Base64URL解码的容错机制
部分Base64URL解码实现会忽略末尾的无效字符,或者对填充字符=的处理过于宽松。但RS256签名是基于哈希的,哪怕一个字节的变化都应该导致验证失败,这个因素的影响相对较小,但值得排查。误修改了非签名部分
如果你误以为修改的是签名段(JWT第三个.之后的内容),但实际改的是payload段,那验证失败才是正常的——但你描述的是修改最后一个字符,大概率是签名段,所以这个可能性较低。
解决方案
1. 明确指定验证的算法和kid
在创建验证器时,强制指定JWT头中声明的算法和kid,避免node-jose尝试无关密钥,同时严格校验算法匹配:
const jose = require('node-jose'); const jwks = { "keys": [ { "alg": "RS256", "e": "AQAB", "kid": "GswyT6a+OAuL5kCrRCmT5xmfCL6uFuo1MWaD+otmfd4=", "kty": "RSA", "n": "hrYesjnwXvwfh8Pr87Jun5uAb3YKF9XNYFbxjQwlvd__8JQw3l0iGJVLjmHKoOHQztVUdeWe9jfkShEA2FjFukb21mPH1UCOYGWg7HR_lJz1IAaYf0ysQXmG1fWkhYDisHGrdEFURbWNhmSzJcqCM9Faa6LBGzUMMJzwDMyrxjOP8nZYbVhjorpFnHOfxUYPJ_RhZfUEXY1F6RbmHdtZ7rpFeaOzptKFfEqOcCU0avkPrpPcrKNaZ4kMN3YOHl-gPzoy-HgjAYYOGE1XMW316t1keEoYJhk8MY9NjriPx7RNcsbPpeRpb7bS2Vki1p8NYhBqywoGv0QYZK_b1JLlAw", "use": "sig" }, { "alg": "RS256", "e": "AQAB", "kid": "IQCV14AYWuTjxjwqbtxW9uh2psd2iyjHox6TQZihdwo=", "kty": "RSA", "n": "ojo3CemQghYpj_Vic-a28BNj69cce76DmVKBr0U7Uj2q0rgfrE0aamUdya21-NJAOq2D5PwqZyxQJOXPhngpOKS9bTaBfeqdTzKrz2b98Q0Q-ADEYNoWRtVMYFDnpeq1Xkjl3X-Y1nB76SD5Xjtv-g3z7VHWD-p91hGZTWotaid1yvGWcjL5VHAFI6Qko0s_-RPlj93BUnIVboWfWjmsC6qcC9lIa4V_562xS7bOMNJDt6UGum0bWUhexhytWyplZnTUfO2Pb4ML2e52zhe3f87jFny9pra9N_ULQwrU8DpslvK-AdmcLYaHNOt1J8g5gS_Es45HDMkkNjwinikiMQ", "use": "sig" } ] }; const jwt = 'eyJraWQiOiJJUUNWMTRBWVd1VGp4andxYnR4Vzl1aDJwc2QyaXlqSG94NlRRWmloZHdvPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI0MGMwNTc4My1jMWI2LTRiZWQtYWUzZS1hNWQ2ZWE4NzM2MmIiLCJldmVudF9pZCI6ImRiMWEzMzI2LTY0YjctMTFlOC04MmFjLTU3OWMxZTQ2M2U2MiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Mjc3NjAxNDUsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMS5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMV9jOVZENWdqN3AiLCJleHAiOjE1Mjc3NjM3NDUsImlhdCI6MTUyNzc2MDE0NSwianRpIjoiNGNjZGI4YjAtNjg3MS00NWRiLTkzYjktZmYwODczNTIyMDJhIiwiY2xpZW50X2lkIjoiNm45aDRhdjJibGR2OTM1M21hZWFuaTZuaWoiLCJ1c2VybmFtZSI6ImppZXdtZW5nLmxpbUAyMzU5bWVkaWEuY29tIn0.Kp5ZVTJo-zTb3Tj26UHcjmufVfxtDKKHFgQNnEQOaMUJ8KtPXQPy_gL0ZzUBS4-rn0BsphAD_vT80iLjOxLKa9C-qNm2vq_28zlySnbqh1aN_pOTFC5YkQFrspU6_B_AIC_1GtcFuTlCksBe5AJLaYZhzYaxqy3ubcG2z3Eyzf13OlwHoUqNeJgtKk7Ff48nUF2pAUkCQbkFEhLTcoxh1xjJYcex4yEM2LeW1ZLdlFp2R5YGDB6al-MyMAh03r_SIGAuD-lEnz3rmeR5XUsdIj4Yg5UPAWIGufmb1fPa740iTf-7OF2FF7YLPQW60ng6w_cWGe1e36K3B5RVbduf5A'; // 解析JWT头,获取声明的kid和算法 const getJwtHeader = (token) => { const [headerBase64] = token.split('.'); try { const headerString = Buffer.from(headerBase64, 'base64url').toString('utf8'); return JSON.parse(headerString); } catch (err) { throw new Error('Invalid JWT header'); } }; const jwtHeader = getJwtHeader(jwt); jose.JWK.asKeyStore(jwks) .then(keystore => { // 强制指定算法和kid,严格校验 return jose.JWS.createVerify({ algorithms: [jwtHeader.alg], kid: jwtHeader.kid }, keystore).verify(jwt); }) .then(result => { console.log('OK!', result.payload.toString('utf8')); }) .catch(err => { console.error('Verification failed:', err.message); });
2. 升级node-jose到最新版本
旧版本的node-jose可能存在签名校验的bug,执行以下命令升级到最新稳定版:
npm install node-jose@latest
3. 用第三方工具交叉验证
可以用JWT在线验证工具(比如jwt.io)分别验证原始Token和修改后的Token,确认修改后的Token确实应该验证失败,排除代码逻辑外的问题。
内容的提问来源于stack exchange,提问作者Jiew Meng
相关产品推荐
相关产品推荐

