- 后端
- 物联网
- 消息队列
- 通信
【免费下载链接】emqx
The most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles
本篇技术指南围绕 EMQX 开源仓库中的一个安全修复项展开:LwM2M 网关在把设备的 CoAP REGISTER/UPDATE 请求翻译为 MQTT 消息时,曾可能将 URI 查询参数中携带的password、secret、private_key、access_token等敏感字段一并写入上报到 MQTT 的消息体,造成凭据泄露。读完本文,你将理解 LwM2M 网关注册/更新上报的完整数据链路、敏感字段过滤的具体实现位置与判定规则、对应的回归测试用例,以及如何在实际部署中规避同类风险。
修复项背景
本修复记录于 changes/ee/fix-17888.en.md,原文如下:
Fixed an issue where the LwM2M gateway could include sensitive REGISTER query fields such as
password,secret,private_key, andaccess_tokenin registration/update MQTT reports.
即:修复了 LwM2M 网关可能将 REGISTER 查询字段(如password、secret、private_key、access_token)包含在注册/更新 MQTT 报告中的问题。这是典型的信息泄露(sensitive data exposure)类缺陷——敏感凭据本应用于网关侧的身份校验,却因数据透传被意外转发到了 MQTT 消息里。
LwM2M 网关与 REGISTER/UPDATE 上报链路
EMQX 的 LwM2M 网关位于 apps/emqx_gateway_lwm2m,其职责是接收 LwM2M 客户端(基于 UDP/DTLS 传输,当前实现仅支持 v1.0.2,详见 README.md),并将设备的事件与消息翻译成 MQTT Publish 消息。设备上线时会在 CoAP 的/rd资源上发起 REGISTER 请求,之后可通过 UPDATE 请求刷新注册信息,这两类请求的 URI 查询参数中通常携带标准字段:
ep:端点名称(Endpoint Name),设备唯一标识;lt:生命周期(Lifetime);lwm2m:协议版本;imei、device_id:扩展字段(非标准协议字段,源码中已标注FIXME注释);b、t等其他厂商扩展参数。
从源码看,网关解析这些查询参数的入口位于 emqx_lwm2m_channel.erl 的enrich_conninfo/2与enrich_clientinfo/2(第 508-558 行):enrich_conninfo提取ep、lt等用于连接信息;enrich_clientinfo则额外提取imei作为用户名、password用于网关侧认证,其中password属于“非标准协议字段”的扩展用法。
设备完成注册后,网关会将会话注册信息打包成#{<<"msgType">> => <<"register">>, <<"data">> => RegInfo}结构发布到 MQTT(对应register_init/2,见 emqx_lwm2m_session.erl 第 465-478 行);UPDATE 请求则走update/5流程(第 416-463 行),同样以<<"update">>为msgType发布。上报的 MQTT 主题由translators.register/translators.update配置决定(见 emqx_lwm2m_schema.erl 第 155-167 行 与uplink_topic/1的读取逻辑)。
问题就出在RegInfo的构造上:如果直接沿用解析出的完整查询参数 Map,那么设备在 REGISTER/UPDATE 请求里携带的任何键值(包括敏感凭据)都会被原样写进data字段并发布到 MQTT——订阅了注册/更新主题的任何 MQTT 客户端都能看到这些敏感信息。
修复实现:drop_sensitive_reg_info 敏感字段过滤
本次修复的核心实现在 emqx_lwm2m_session.erl 中新增的drop_sensitive_reg_info/1函数:
drop_sensitive_reg_info(RegInfo) -> maps:filter( fun(Key, _Value) -> not emqx_utils_redact:is_sensitive_key(Key) end, RegInfo ).该函数遍历注册信息 Map,凡是被emqx_utils_redact:is_sensitive_key/1判定为敏感键的键值对一律剔除。它在两条路径上被调用:
- 首次 REGISTER 路径:
append_object_list/2(第 311-319 行)在合并 URI 查询参数与 payload 中的对象列表之后、进行lt类型修正之前,先调用drop_sensitive_reg_info(append_object_list2(Query, Payload))完成过滤,确保register_init/2中发布的RegInfo不含敏感字段; - UPDATE 路径:
update/5(第 425 行)在将新查询参数与旧注册信息合并后,同样执行drop_sensitive_reg_info(maps:merge(OldRegInfo, RegInfo)),既过滤了本次 UPDATE 请求新带来的敏感字段,也兜底清除了历史注册信息中残留的敏感数据。
两条路径都经过统一过滤,因此 REGISTER 与 UPDATE 两种上报消息(以及由reregister/3触发的重注册)均不再携带敏感字段。
敏感键判定规则:emqx_utils_redact:is_sensitive_key/1
过滤所依赖的判定函数位于 apps/emqx_utils/src/emqx_utils_redact.erl。is_sensitive_key/1是一个覆盖多种数据形态(原子、字符串、二进制)的敏感键白名单,与本次修复直接相关的条目包括:
| 键名 | 匹配形态 |
|---|---|
password | password/"password"/<<"password">> |
secret | secret/"secret"/<<"secret">> |
private_key | private_key/"private_key"/<<"private_key">> |
access_token | access_token/"access_token"/<<"access_token">> |
该模块同时覆盖了access_key_id、access_key_secret、api_key、api_secret、jwt、token等大量常见凭据键名(见 第 34-102 行)。这意味着 LwM2M 网关的过滤机制不只是针对修复说明中列举的四个字段——凡是查询参数中出现的、命中该敏感键表的键(无论以字符串还是二进制形式传入),都会被drop_sensitive_reg_info/1一并剔除,为上报链路提供了较为全面的防护。
值得说明的是,过滤仅作用于上报到 MQTT 的注册信息(RegInfo)。password等字段在网关内部依然会被enrich_clientinfo/2读取并用于连接认证(见 emqx_lwm2m_channel.erl 第 540-548 行),即敏感信息仍可在“网关内部消费”,只是不再被转发到 MQTT 主题上。
回归测试验证
修复配套了完整的回归测试,位于 apps/emqx_gateway_lwm2m/test/emqx_lwm2m_SUITE.erl 的case02_update_deregister/1用例(第 821-944 行)。该用例构造的 REGISTER 请求故意携带了全部四类敏感参数:
coap://127.0.0.1:~b/rd?ep=~ts<=345&lwm2m=1 &password=public&secret=top&private_key=priv&access_token=token随后网关应返回{ok, created},同时上报的 MQTT 报告(主题为lwm2m/<endpoint>/up/resp)中data字段应只包含标准字段alternatePath、ep、lt、lwm2m、objectList,并断言不存在任何敏感字段:
assert_no_secret_register_fields(#{<<"data">> := Data}) -> ?assertEqual( #{}, maps:with( [ <<"password">>, <<"secret">>, <<"private_key">>, <<"access_token">> ], Data ) ).maps:with/2会提取data中命中的键组成新 Map,断言其等于空#{},即上述四个键一个都不允许出现(见 第 6300-6312 行)。同一用例随后发起 UPDATE 请求并再次调用assert_no_secret_register_fields(Update)断言 UPDATE 报告同样干净,与源码中update/5的过滤逻辑一一对应,形成了 REGISTER 与 UPDATE 双向的回归保障。
配置参考与安全实践
结合修复后的行为,在生产环境使用 LwM2M 网关时可以从以下几点入手规避敏感信息风险:
避免在 REGISTER/UPDATE 查询参数中携带凭据。从源码看,标准 LwM2M 注册协议并不要求这些字段(
emqx_lwm2m_channel.erl中已将imei/password等标注为“不属于标准协议”的 FIXME 扩展)。即便本次修复保证了它们不会被转发到 MQTT,凭据出现在 CoAP 查询参数中本身也会增加被记录、被截获的风险。若确有认证需求,应优先使用网关的认证链(emqx_gateway_ctx:authenticate/2)配合正式机制处理。确认上报主题不被无关订阅者监听。REGISTER/UPDATE 报告发布到
translators.register/translators.update配置的主题(默认示例见 README.md 中的up/resp、up/update),应结合 EMQX 的 ACL 规则限制这些主题的订阅权限。按需控制 UPDATE 上报频率。网关提供
update_msg_publish_condition配置(默认always,可设为contains_object_list使仅当 UPDATE 携带对象列表时才发布),见 emqx_lwm2m_schema.erl 与 emqx_lwm2m_session.erl 第 451-463 行 的should_publish_update/1判定逻辑。合理配置可减少不必要的数据落盘与转发面。升级并关注日志中的脱敏行为。本次修复与仓库既有的
emqx_utils_redact脱敏体系一致,网关在记录错误日志时也会调用emqx_utils:redact/1对查询参数脱敏(见 emqx_lwm2m_channel.erl 第 553-555 行)。建议使用包含该修复的版本,确保注册/更新上报与日志两条路径都不再泄露敏感查询字段。
小结
本次安全修复通过在 LwM2M 会话层的 REGISTER 与 UPDATE 两条数据链路上统一引入drop_sensitive_reg_info/1,借助emqx_utils_redact:is_sensitive_key/1的敏感键表过滤,确保password、secret、private_key、access_token等凭据不会进入上报到 MQTT 的data字段;配套的case02_update_deregister回归用例对两类报告做了断言兜底。对于自行基于 EMQX 网关协议栈做二次开发的场景,本文所述的“内部消费、外部过滤”模式同样值得参考:敏感字段可以在网关内用于认证,但在跨协议转发时必须显式剥离。
- 后端
- 物联网
- 消息队列
- 通信
【免费下载链接】emqx
The most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles
相关推荐
FastJSON字段过滤:PropertyPreFilters实现敏感信息脱敏
FastJSON字段过滤:PropertyPreFilters实现敏感信息脱敏 引言:敏感信息泄露的隐形风险 在API接口开发中,用户数据JSON序列化常面临两
序列化后端KeystoneJS 关系(Relationships)完全指南:字段、过滤器、定义与反向查询
KeystoneJS 关系(Relationships)完全指南:字段、过滤器、定义与反向查询 本指南以 KeystoneJS 官方文档 docs/docume
后端VCR项目中的敏感数据过滤机制详解
VCR项目中的敏感数据过滤机制详解 引言:测试数据安全的挑战 在现代软件开发中,HTTP接口测试是不可或缺的一环。然而,测试过程中经常需要处理敏感数据,如API
测试开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考