EMQX 日志脱敏增强:阻止 JWT HMAC 密钥在 cluster RPC 配置更新日志中泄露
EMQX 日志脱敏增强阻止 JWT HMAC 密钥在 cluster RPC 配置更新日志中泄露【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx本文围绕 EMQX 开源仓库中changes/ee/fix-17791.en.md所记录的修复展开EMQX 改进了日志脱敏log redaction能力使 JWT HMAC 密钥字节不再出现在配置更新期间产生的cluster_rpc_apply_result与cluster_rpc_apply_ok两类 debug 日志行中。读完本文你将理解 EMQX 集群配置同步cluster RPC路径上的日志记录机制、emqx_utils_redact脱敏器的工作原理以及修复中新增的两道防护识别内部 JWK record 形状并整体替换、将jwk字段视为敏感字段。背景集群配置同步与日志泄露风险EMQX 在集群模式下通过emqx_cluster_rpc位于 apps/emqx_conf/src/emqx_cluster_rpc.erl把配置变更以事务transaction方式广播到集群内各节点执行。当管理员通过 Dashboard、HTTP API 或 CLI 修改认证、桥接等配置时配置更新会被封装为 MFA模块-函数-参数提交到集群各节点随后执行apply_mfa/3完成实际更新。配置更新携带的参数可能包含敏感信息密码、Token、私钥等因此apply_mfa/3在构造日志元数据时特别注明%% Do not log args as it might be sensitive information Meta #{kind Kind, tnx_id TnxId, entrypoint format_mfa(M, F, length(A))},即日志中只记录 MFA 的入口签名模块、函数、参数个数绝不记录参数内容本身。真正存在风险的环节是 MFA执行成功后的返回值——对于认证器authenticator这类带运行时状态的配置项返回的 state 中可能内嵌编译后的运行时对象例如 JWT HMAC 认证器会用密钥构造出的jose_jwkrecord其kty_oct字段中存放的正是原始 HMAC 密钥字节。修复前的泄露路径成功结果的值进入 debug 日志修复前cluster RPC 在记录执行结果时存在一条潜在的密钥泄露路径。配置更新成功后节点会把返回值写入 debug 日志行而返回值是一个复合 term——例如 JWT HMAC 认证器的 state 形如#{ jwk {jose_jwk, undefined, {jose_jwk_kty_oct, 原始 HMAC 密钥字节}, #{}}, allowed_algs [...], verify_claims ... }emqx_utils:redact/1的通用脱敏器见 apps/emqx_utils/src/emqx_utils_redact.erl虽然能按敏感键名递归替换值但在修复前它不认识jose_jwk这种内部 record 结构泛化的元组遍历逻辑会继续深入 record 的每个字段{jose_jwk_kty_oct, Secret}这类携带原始密钥的二元组并不命中任何敏感键名于是原始密钥字节会随 debug 日志行原样写出。emqx_cluster_rpc.erl中summarize_ok/1的文档注释也印证了这一点-doc Report the shape of a successful result, never its value. The value can carry compiled runtime state, such as the HTTP authenticator header templates, which holds secrets emqx_utils:redact/1 does not cover. . summarize_ok(ok) - ok; summarize_ok({ok, _}) - {ok, _}.也就是说成功返回的值中可能携带编译后的运行时状态例如 HTTP 认证器的 header 模板而其中包含的秘密无法被emqx_utils:redact/1覆盖。修复机制一成功结果只记录形状不记录值修复首先从源头收紧日志内容。在 apps/emqx_conf/src/emqx_cluster_rpc.erl 中成功路径的日志一律通过summarize_ok/1把返回值折叠成形状摘要log_and_alarm(IsSuccess, Res, #{kind : ?KIND_INITIATE} Meta) - case IsSuccess of true - ?SLOG(debug, Meta#{msg cluster_rpc_apply_result, result summarize_ok(Res)}); false - ?SLOG(warning, Meta#{ msg cluster_rpc_failed_to_init_transaction, result emqx_utils:redact(Res) }) end; log_and_alarm(true, Res, Meta) - Summary summarize_ok(Res), ?SLOG(debug, Meta#{msg cluster_rpc_apply_ok, result Summary}), do_alarm(deactivate, Summary, Meta); log_and_alarm(false, Res, Meta) - ?SLOG(error, Meta#{msg cluster_rpc_apply_failed, result emqx_utils:redact(Res)}), do_alarm(activate, Res, Meta).成功cluster_rpc_apply_result事务发起节点与cluster_rpc_apply_ok各应用节点都只记录{ok, _}这种形状占位真实返回值完全不进入日志失败cluster_rpc_apply_failed仍通过emqx_utils:redact(Res)记录错误项——错误本身是排障必需信息且经过脱敏器处理。修复机制二脱敏器识别内部 JWK record 形状仅靠记录形状并不足够JWT HMAC 密钥还可能以jose_jwkrecord 的形式出现在其它日志路径如 trace、告警元数据、进程状态 dump中。因此修复在脱敏器层面补上了第二道防护——让emqx_utils_redact直接识别内部 JWK record 形状并整体替换。在 apps/emqx_utils/src/emqx_utils_redact.erl 中do_redact/2针对jose_jwkrecord 增加了专门的分支do_redact({jose_jwk, _Keys, _Kty, _Fields}, _Checker) - %% A jose_jwk record carries the raw key material (a kty_oct binary for HMAC, %% kty_rsa fields for RSA, etc.) in its inner tuples. Replace it wholesale so %% the generic tuple walk below never descends into the key bytes. {jose_jwk, ******};关键点在于整体替换replace it wholesale无论jose_jwkrecord 嵌套在什么位置、外层键名是否敏感只要泛化遍历触达该 record就立刻折叠为{jose_jwk, ******}占位符从而保证底层的{jose_jwk_kty_oct, Secret}HMAC 原始密钥、kty_rsa字段RSA 密钥材料等永远不会被继续遍历输出。通用元组遍历逻辑不会再有机会触达密钥字节。修复机制三jwk字段被纳入敏感键名单除了识别 record 形状修复还把jwk加入了is_sensitive_key/1的敏感键名单见 apps/emqx_utils/src/emqx_utils_redact.erlis_sensitive_key(jwk) - true; is_sensitive_key(jwk) - true; is_sensitive_key(jwk) - true;该函数同时接受原子atom、字符串string和二进制binary三种键表示形式。这意味着以jwk为键的 map 条目其值会被redact_v/1直接替换为脱敏占位符******无论值是jose_jwkrecord 还是普通 map例如从 JWKS 端点拉取后缓存的密钥数据这一层防护覆盖非 record 形态的密钥表示与识别 record 形状形成互补共同构成对 JWT 密钥的双重保险。脱敏占位符在源码中统一定义为-define(REDACT_VAL, ******). redacted_value() - ?REDACT_VAL.脱敏器的整体结构从键名匹配到值替换理解本次修复还需要了解emqx_utils_redact的整体工作方式。其对外入口包括redact/1、redact/2可传入自定义敏感判定函数、redact_headers/1、is_sensitive_key/1、is_redacted/2,3与deobfuscate/2用于把脱敏占位符恢复为旧配置中的真实值支撑 API 回显。do_redact/2的递归逻辑按数据结构分派list逐元素递归脱敏且能正确处理 improper list非规范链表map遍历每个键值对do_redact(K, V, Checker)先判断键是否敏感敏感则替换值否则对值递归{headers, Value}特殊形态走do_redact_headers/1对 HTTP 头列表/映射做大小写不敏感的键名匹配Authorization、api-key、cookie、proxy-authorization、x-api-key、x-auth-token等头名会被脱敏tuple泛化遍历而{jose_jwk, _, _, _}record 分支在元组遍历之前被优先匹配直接整体替换其它 term原样保留。对敏感键的值redact_v/1还做了几个精细处理值为${var}形式的占位符模板如${password}不脱敏——因为模板本身不含真实秘密见 apps/emqx_utils/test/emqx_utils_redact_tests.erl 中no_redact_template_var_test值为file://...形式的文件路径不脱敏——路径不是密钥本身被emqx_secret:wrap/1包装的密钥值会被识别并脱敏。测试验证双重防护的落地证据本次修复配套了完善的测试用例可以从源码中直接验证。JWK record 脱敏单元测试在 apps/emqx_utils/src/emqx_utils_redact.erl 内置的jose_jwk_redact_test_()测试中构造了携带原始密钥的 recordSecret abcd1234, JWK {jose_jwk, undefined, {jose_jwk_kty_oct, Secret}, #{}},测试断言redact(JWK)结果为{jose_jwk, ******}——record 被整体替换嵌套在current_jwk等非敏感键下的 record 同样被替换#{current_jwk : {jose_jwk, ******}, other : value}模拟真实配置更新场景的深层嵌套 stateemqx_authn_config #{state #{jwk JWK, ...}}经脱敏后用io_lib:format(~p, ...)渲染出的日志文本中binary:match找不到原始密钥\abcd1234\jwk键的 map 值非 record 形态被替换为******#{jwk ?REDACT_VAL}。cluster RPC 日志不泄露测试在 apps/emqx_conf/test/emqx_cluster_rpc_SUITE.erl 中t_apply_result_not_logged/1专门验证成功的 cluster-RPC apply 只记录结果的形状绝不记录值OkReports emqx_cth_log_capture:capture(debug, fun() - {M, F, A} {?MODULE, echo_compiled, [Marker]}, ?assertMatch({ok, _, {ok, #{compiled : Marker}}}, multicall(M, F, A)) end), ?assertMatch( [#{result : {ok, _}}], [R || #{msg : cluster_rpc_apply_result} R - OkReports] ), AppliedReports [R || #{msg : cluster_rpc_apply_ok} R - OkReports], ?assertMatch([_, _ | _], AppliedReports), lists:foreach(fun(R) - ?assertMatch(#{result : {ok, _}}, R) end, AppliedReports), ?assertEqual( nomatch, binary:match(iolist_to_binary(io_lib:format(~p, [OkReports])), Marker) ),测试通过emqx_cth_log_capture:capture(debug, ...)捕获 debug 日志然后断言cluster_rpc_apply_result与cluster_rpc_apply_ok两类日志行的result字段都是{ok, _}形状占位对整批捕获的日志文本全文检索找不到原始标记值Markerbinary:match返回nomatch同时测试还验证了失败路径仍保留错误信息cluster_rpc_apply_failed日志的result为MFA return not ok之类的错误项排障信息不因脱敏而丢失。JWT 认证路径自身的脱敏在 JWT 认证模块 apps/emqx_auth_jwt/src/emqx_authn_jwt.erl 中HMAC 模式通过jose_jwk:from_oct(Secret)把共享密钥构造为 JWK record 并存入认证器 statejwk JWK这正是修复针对的核心场景之一。该模块也维护着自己的日志脱敏函数redact_jwk_for_log(#jose_jwk{kty {jose_jwk_kty_oct, _}}) - ******; redact_jwk_for_log(JWK) - JWK.凡kty_oct类型的 JWK即 HMAC 密钥一律以******出现在 trace 日志中。与之配套apps/emqx_auth_jwt/src/emqx_authn_jwks_client.erl 中的 JWKS 客户端在内存中缓存jose_jwkrecord 列表这些对象在日志、dump 场景中同样受新脱敏逻辑保护。修复的整体效果与使用建议综合源码实现与测试用例本次修复形成了三条环环相扣的防线记录形状而非值cluster_rpc_apply_result/cluster_rpc_apply_ok只输出{ok, _}摘要成功返回的运行时状态含编译后的认证器、header 模板、JWK根本不进入日志识别 record 形状脱敏器优先匹配{jose_jwk, _, _, _}record 并整体折叠为占位符杜绝泛化元组遍历触及kty_oct/kty_rsa等密钥字段键名兜底jwk字段被纳入is_sensitive_key/1原子/字符串/二进制三种形态非 record 形态的密钥 map 也会被替换。对于使用 EMQX 集群、尤其是启用了 JWT HMAC 认证或基于 JWKS 端点认证的用户本次修复意味着即使开启 debug 级别日志、在配置热更新期间排查cluster_rpc_apply_result与cluster_rpc_apply_ok日志也不会再看到 JWT 共享密钥字节以明文形式出现。如需验证修复效果可直接运行emqx_utils的 EUnit 测试jose_jwk_redact_test_与emqx_cluster_rpc_SUITE的t_apply_result_not_logged用例相关实现与测试均可在当前仓库的 apps/emqx_utils/src/emqx_utils_redact.erl、apps/emqx_conf/src/emqx_cluster_rpc.erl 与 apps/emqx_conf/test/emqx_cluster_rpc_SUITE.erl 中查阅。【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

PiKVM KVMD 2.65 更新指南:为 Ezcoo USB 3.0 多端口 KVM 交换机启用 `protocol: 2` 管理协议

PiKVM KVMD 2.65 更新指南:为 Ezcoo USB 3.0 多端口 KVM 交换机启用 `protocol: 2` 管理协议

文档教程 【免费下载链接】pikvm Open and inexpensive DIY IP-KVM based on Raspberry Pi 项目地址: https://gitcode.com/gh_mirrors/pi/pikvm 点击查看 免费下载 如果你购买了 Ezcoo 出品的 USB 3.0 版多端口 KVM 交换机并希望配合 PiKVM 使用,那么必…

2026/9/24 16:19:25 阅读更多 →
Prisma 数据建模完全指南:用 GraphQL SDL 编写 Data Model 并生成数据库 Schema

Prisma 数据建模完全指南:用 GraphQL SDL 编写 Data Model 并生成数据库 Schema

后端数据库GraphQL 【免费下载链接】prisma1 💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated] 项目地址: https://gitcode.com/gh_mirrors/pr/prisma1 点击查看 免费下载 导读 Prisma 使用 Graph…

2026/9/24 16:18:24 阅读更多 →
F´ 框架中的 Svc::BufferManager:基于固定分箱(Bin)池的内存缓冲区管理器组件深度解析

F´ 框架中的 Svc::BufferManager:基于固定分箱(Bin)池的内存缓冲区管理器组件深度解析

F 框架中的 Svc::BufferManager:基于固定分箱(Bin)池的内存缓冲区管理器组件深度解析 【免费下载链接】fprime F - A flight software and embedded systems framework 项目地址: https://gitcode.com/gh_mirrors/fp/fprime 导读 Svc…

2026/9/24 16:18:24 阅读更多 →

最新新闻

gsd-core 配置读取修复解析:`config-get --default true` 如何消除 Nyquist 校验开关的 stderr 噪音与空变量回退

gsd-core 配置读取修复解析:`config-get --default true` 如何消除 Nyquist 校验开关的 stderr 噪音与空变量回退

gsd-core 配置读取修复解析:config-get --default true 如何消除 Nyquist 校验开关的 stderr 噪音与空变量回退 【免费下载链接】gsd-core Git. Ship. Done - Core 项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core 本文以 gsd-core 仓库中归档变更集 .…

2026/9/24 16:59:10 阅读更多 →
Hermes Rust 工作区公共工具 crate:hermes_utils 设计规范与 PointerAddress 实现解析

Hermes Rust 工作区公共工具 crate:hermes_utils 设计规范与 PointerAddress 实现解析

语言运行时编译器移动开发 【免费下载链接】hermes A JavaScript engine optimized for running React Native. 项目地址: https://gitcode.com/gh_mirrors/hermes/hermes 点击查看 免费下载 hermes_utils 是 Hermes 项目 Rust 工作区中一个"兜底型"公共…

2026/9/24 16:59:10 阅读更多 →
The Concise TypeScript Book 精读:函数返回类型推断(Type from Func Return)实战指南

The Concise TypeScript Book 精读:函数返回类型推断(Type from Func Return)实战指南

文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 本指南聚焦《The C…

2026/9/24 16:59:10 阅读更多 →
Opus 5.5 对 GPT-6 Sol,谁更强?

Opus 5.5 对 GPT-6 Sol,谁更强?

美国时间 9 月 22 日,Anthropic 发布了 Claude Opus 5.5。 大约 90 分钟后,OpenAI 发布了 GPT-6 Sol 和 GPT-6 Luna。 两家打的是同一张牌:能力不降,价格往下砍。Opus 5.5 比上一代 Opus 5 便宜约 40%,Sol 和 Luna 的 …

2026/9/24 16:59:10 阅读更多 →
RuboCop 0.35.0:团队配置终于能发 gem 了,升级前必看的 3 个边界

RuboCop 0.35.0:团队配置终于能发 gem 了,升级前必看的 3 个边界

RuboCop 0.35.0:团队配置终于能发 gem 了,升级前必看的 3 个边界 【免费下载链接】rubocop A Ruby static code analyzer and formatter, based on the community Ruby style guide. 项目地址: https://gitcode.com/GitHub_Trending/rub/rubocop …

2026/9/24 16:59:10 阅读更多 →
基于 Java Spring Boot 的中学课外兴趣小组管理系统设计与实现

基于 Java Spring Boot 的中学课外兴趣小组管理系统设计与实现

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 1. 项目背景与意义 随着素质教育的深入推进,中学课外兴趣小组活动日益丰富,涵盖科技、艺术、体育、文学等多个方向。然而,传统的人工…

2026/9/24 16:58:09 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →