AWS Cognito自定义邮件触发器解密密码出现HTML实体转义异常
问题描述
为AWS Cognito配置自定义邮件发送触发器(custom email sender trigger)时,业务子系统通过Cognito的admin-create-user命令创建用户,操作触发系统向用户发送包含初始一次性密码的邮件,90%场景下功能运行正常。
但部分场景下,解密得到的密码中特殊符号被表示为HTML实体,Cognito预期用户输入原始符号,二者不匹配导致用户无法登录:
- Lambda邮件发送器内解密得到(待发送给用户)的密码值:
iPi1Hz>H(包含>片段) - Cognito侧实际校验的正确密码值:
iPi1Hz>H
使用的Lambda解密逻辑代码如下:
const getPlainTextCode = async (event: CustomEmailSenderTriggerEvent) => { if (!event.request.code) { throw Error("Could not find code"); } if (!process.env.KEY_ID) { throw Error("Cannot decrypt code"); } const client = buildClient(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT); const generatorKeyId = process.env.KEY_ALIAS; const keyIds = [process.env.KEY_ID]; const keyring = new KmsKeyringNode({ generatorKeyId, keyIds }); let plainTextCode: string | undefined = undefined; const decryptOutput = await client.decrypt(keyring, Buffer.from(event.request.code, "base64")); if (event.userPoolId !== decryptOutput.messageHeader.encryptionContext["userpool-id"]) { throw new Error("Encryption context does not match expected values!"); } plainTextCode = decryptOutput.plaintext.toString(); // 输出为iPi1Hz>H return plainTextCode; };
根本原因
这个问题和KMS解密逻辑无关,是Cognito自定义邮件触发器的内置转义规则导致的:
当Cognito生成的临时密码包含&、<、>、单引号、双引号这类HTML特殊字符时,会先对密码做HTML实体转义,再用配置的KMS密钥加密后传递给Lambda触发器。
- 原始密码为
iPi1Hz>H时,转义后的值为iPi1Hz>H,加密后传入Lambda - 解密后拿到的实际字符串就是转义后的
iPi1Hz>H,日志中显示为iPi1Hz&gt;H是日志平台对&字符做了二次转义的展示效果,并非解密结果本身包含两层转义 - 因为转义规则仅在密码命中特定特殊字符时触发,所以90%不含特殊字符的密码场景下功能完全正常,只有密码命中特殊字符时才会出现密码不匹配问题。
修复方法
在KMS解密完成后,对得到的明文字符串做一次HTML实体解码,再将解码后的密码放入邮件发送给用户即可,不需要修改KMS解密、加密上下文校验的原有逻辑。
Node.js环境下可以直接用以下无依赖方法完成解码,覆盖Cognito会转义的所有特殊字符:
const decodeHtmlEntities = (str) => { const entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'" }; return str.replace(/&(amp|lt|gt|quot|#39);/g, (match) => entityMap[match]); };
修改原有解密逻辑的返回部分:
// 原有解密逻辑 plainTextCode = decryptOutput.plaintext.toString(); // 新增解码步骤 plainTextCode = decodeHtmlEntities(plainTextCode); return plainTextCode;
如果项目中已经引入了HTML实体处理库(比如he),也可以直接调用库的解码方法,效果完全一致。
修复后可以专门构造包含&、<、>字符的临时密码测试,解密解码后的密码会和Cognito侧存储的原始密码完全匹配,用户收到邮件输入密码即可正常登录。
内容的提问来源于stack exchange,提问作者florian norbert bepunkt
相关产品推荐
相关产品推荐

