- 后端
- 消息队列
- 消息路由
【免费下载链接】mosquitto
Eclipse Mosquitto - An open source MQTT broker
在 Eclipse Mosquitto 的版本演进中,密码文件的存储格式经历了一次关键变革:自 1.0 起,broker 要求密码文件使用加盐哈希存储,而早期版本遗留的明文密码文件无法直接被新版本使用。本篇技术指南以 Mosquitto 官方博客文章 updating-password-files.md 所记录的升级需求为起点,结合当前仓库中mosquitto_passwd工具(apps/mosquitto_passwd/mosquitto_passwd.c)的完整实现,系统讲解如何将明文密码文件安全、正确地升级为哈希密码文件,并深入剖析-U转换选项的底层原理、密码哈希格式的演进(sha512 → sha512-pbkdf2 → argon2id)以及升级过程中的安全注意事项。读完本文,你将能够独立完成 Mosquitto 密码文件从明文到哈希的迁移,并理解其背后的哈希算法与文件格式细节。
一、背景:为什么需要"升级密码文件"
Mosquitto 1.0 是一个分水岭版本。在此之前,密码文件中的密码以明文形式存放;从 1.0 开始,broker 引入并使用加盐哈希(salted hash)后的密码文件,以降低密码文件泄露带来的风险。官方博客 updating-password-files.md 明确记录了当时面临的问题:
Mosquitto 1.0 introduced the use of password files with hashed passwords but had no way to convert from the old plain text password files. This feature will be available in version 1.1...
也就是说,1.0 虽然强制要求哈希格式,却没有提供从旧明文密码文件转换的工具,导致存量用户升级受阻。该转换能力随后在 1.1 版本中随mosquitto_passwd工具的新选项落地。时至今日,这一能力已经发展为mosquitto_passwd -U选项,并沉淀在当前仓库的源码与 man 手册中。
需要特别说明的是,这一问题在今天依然具有现实意义:从较老版本(或第三方工具生成的明文密码文件)迁移到现代 Mosquitto 时,仍可能遇到明文格式的密码文件,此时就需要本文所讲的升级流程。
二、核心工具:mosquitto_passwd 与 -U 选项
mosquitto_passwd是 Mosquitto 官方提供的密码文件管理工具,位于仓库 apps/mosquitto_passwd/mosquitto_passwd.c。其完整用法如下(来自源码中的print_usage(),见 mosquitto_passwd.c#L113-L127):
Usage: mosquitto_passwd [-H argon2id | -H sha512-pbkdf2] [-c | -D] passwordfile username mosquitto_passwd [-H argon2id | -H sha512-pbkdf2] [-c] -b passwordfile username password mosquitto_passwd -U passwordfile各选项含义:
| 选项 | 说明 |
|---|---|
-b | 批处理模式,允许直接在命令行传入密码(注意:命令行密码可能被进程列表等途径窥探,非交互场景才使用) |
-c | 创建新的密码文件,若文件已存在则覆盖 |
-D | 删除指定用户名,而非新增/更新密码 |
-H | 指定哈希算法:argon2id、sha512-pbkdf2或sha512;默认 argon2id(推荐)。Mosquitto 2.0 及更早默认 sha512-pbkdf2,1.6 及更早默认 sha512 |
-U | 将明文密码文件升级为哈希密码文件,直接修改指定的文件 |
-U 的转换语义
从 man 手册 mosquitto_passwd.1.xml#L114-L126 可以确认-U的行为:
- 将包含明文密码的密码文件转换为使用哈希密码的文件;
- 该选项会原地修改(in-place)指定的文件;
- 它不会检测密码是否已经被哈希:如果对一个已经包含哈希密码的文件再次运行
-U,会把旧的哈希值当作"明文"再哈希一次,生成"哈希的哈希",导致整个密码文件不可用。
因此,-U只能用于确实包含明文密码的文件,且每个文件只能执行一次。
-U 的实现原理
源码层面,-U走的是"读旧文件 → 逐行重新生成哈希 → 写回新内容"的流程,核心代码如下(见 mosquitto_passwd.c#L265-L284):
/* Update a plain text password file to use hashes */ static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) { if(helper){ return output_new_password(ftmp, username, password, helper->iterations); }else{ return output_new_password(ftmp, username, password, -1); } } static int update_file(FILE *fptr, FILE *ftmp) { return pwfile_iterate(fptr, ftmp, update_file_cb, NULL); }output_new_password()为每一行调用mosquitto_pw_new()创建密码哈希对象、mosquitto_pw_hash_encoded()生成带随机盐的哈希编码(见 mosquitto_passwd.c#L130-L152)。值得注意的是,整个更新过程中,文件先写入临时文件(mpw_tmpfile()),最后再copy_contents()覆盖回原文件,并通过create_backup()预先创建passwordfile.backup.XXXXXX备份(见 mosquitto_passwd.c#L643-L711)。升级成功后备份会被自动删除,因为"备份中可能包含旧密码,不应长期保留"——这正是源码注释所强调的安全考虑。
实操示例
假设有一个旧的明文密码文件pwfile,内容形如:
alice:secret123 bob:hunter2执行升级:
mosquitto_passwd -U pwfile命令执行后,pwfile中每个用户名后的明文密码会被替换为类似$7$1000$<salt>$<hash>的哈希编码(具体格式取决于默认哈希算法与-H参数)。转换完成后,即可将该文件配置给 broker 使用。
三、密码哈希格式的演进与底层实现
理解密码文件的升级,就绕不开哈希格式本身。当前仓库中,密码哈希类型定义在 include/mosquitto/libcommon_password.h#L29-L34:
enum mosquitto_pwhash_type { MOSQ_PW_DEFAULT, MOSQ_PW_SHA512 = 6, MOSQ_PW_SHA512_PBKDF2 = 7, MOSQ_PW_ARGON2ID = 8, };三种格式在 libcommon/password_common.c 中均有完整实现:
| 哈希类型 | 编码前缀 | 说明 |
|---|---|---|
sha512($6$) | $6$<salt>$<hash> | 单轮 SHA-512 + 随机盐,对应 Mosquitto 1.6 及更早版本,是 1.0 引入的首个哈希格式 |
sha512-pbkdf2($7$) | $7$<iterations>$<salt>$<hash> | 基于 OpenSSLPKCS5_PBKDF2_HMAC的迭代加盐哈希,默认迭代次数PW_DEFAULT_ITERATIONS = 1000(见 password_common.c#L47),对应 2.0 及更早默认 |
argon2id($8$) | $8$...(argon2 标准编码) | 现代抗 GPU 暴力破解的内存困难型哈希,当前默认与推荐算法;默认参数为MOSQ_ARGON2_T=1(迭代)、MOSQ_ARGON2_M=47104(内存 KiB)、MOSQ_ARGON2_P=1(并行度),见 password_common.c#L40-L45 |
从编码实现可以看到格式细节(见 password_common.c#L449-L475):
/* sha512: */ snprintf(pw->encoded_password, len, "$%d$%s$%s", pw->hashtype, salt64, hash64); /* sha512-pbkdf2: */ snprintf(pw->encoded_password, len, "$%d$%d$%s$%s", pw->hashtype, pw->params.sha512_pbkdf2.iterations, salt64, hash64);也就是说,密码文件的每一行格式为username:password_hash,:之后即上述编码。哈希验证统一走mosquitto_pw_verify(),对 sha512 与 sha512-pbkdf2 使用恒定时间比较(CRYPTO_memcmp或手工常数时间比较,见 password_common.c#L76-L94),降低时序侧信道风险。
为什么必须升级而非继续使用明文
从源码结构看,broker 端在加载密码文件后,对每个用户名执行的是哈希校验逻辑(password_file__check以MOSQ_EVT_BASIC_AUTH事件挂钩,见 src/password_file.c#L46-L82),其校验入口即上述mosquitto_pw_verify()。明文密码无法通过哈希校验,因此明文格式的密码文件在现代 Mosquitto 上无法直接用于认证,升级是迁移到新版本的必要步骤。
四、相关命令行参数与配套配置
其他管理操作
升级之外,mosquitto_passwd的日常管理命令同样围绕同一密码文件格式工作:
- 新增/更新用户密码(交互式输入,避免密码出现在命令行历史中):
mosquitto_passwd pwfile username - 批处理方式新增/更新(适合脚本):
mosquitto_passwd -b pwfile username 'newpassword' - 删除用户:
mosquitto_passwd -D pwfile username - 新建密码文件并添加首个用户:
mosquitto_passwd -c pwfile username - 指定哈希算法(例如需要兼容 1.6 及更早 broker 时):
mosquitto_passwd -H sha512 -c pwfile username - 调整 sha512-pbkdf2 迭代次数(
-I <iterations>,须大于 0):mosquitto_passwd -I 10000 -b pwfile username 'newpassword'
关于选项互斥,源码 mosquitto_passwd.c#L513-L528 明确禁止-c与-D、-c与-U、-D与-U、-b与-D同时使用,使用冲突组合时工具会直接报错退出。
broker 侧配置
升级完成的密码文件,通过 broker 配置中的password_file选项启用。在 src/conf.c#L2340-L2344 中可以看到该配置项的解析:
password_file /path/to/pwfile仓库根目录还提供了可直接参考的 pwfile.example 示例文件,展示现代哈希格式下密码文件的真实行形态。配置完成后,broker 会在客户端连接时触发MOSQ_EVT_BASIC_AUTH事件并完成哈希校验(见 src/password_file.c)。
五、升级流程与安全实践
将上面的知识点串成一套可执行的安全升级流程:
- 备份:转换前先自行复制一份明文密码文件(例如
cp pwfile pwfile.plain.bak),作为最后的回退手段。 - 确认格式:用
cat或cut -d: -f2 pwfile检查文件中:后的内容是否为明文;若已是$6$/$7$/$8$开头的编码,不要运行-U。 - 执行升级:
mosquitto_passwd -U pwfile。工具内部会自动生成临时文件与随机盐备份,成功后删除备份。 - 验证:重新读取
pwfile,确认所有行已变为username:$<n>$...形式的哈希编码;可再用mosquitto_passwd -b pwfile username password复写一次密码,用已知明文重新生成哈希以验证文件可用。 - 配置并重启:确保 broker 配置中
password_file指向该文件,重启 broker 后用客户端以用户名/密码连接测试。 - 清理:确认认证正常后,删除第 1 步的明文备份(明文密码文件本身即安全风险,不宜长期保留)。
常见注意事项
- 严禁对已哈希文件重复执行
-U:man 手册与源码注释均明确说明,这会把旧哈希当作明文再哈希,导致所有用户无法认证。 - 选择与目标版本匹配的哈希算法:默认 argon2id 推荐用于新部署;若需兼容 1.6 及更早的旧版 broker,应显式使用
-H sha512。 - 保护文件权限:无论明文还是哈希,密码文件都应收紧权限(建议仅属主可读写),哈希只是降低泄露后的危害,不等于可以放松文件管理。
- 命令行密码的暴露:
-b模式会把密码暴露在进程参数中,脚本化使用时需权衡;交互式输入更安全。
六、结语
从 1.0 引入哈希密码文件、到 1.1 提供转换工具,再到今天mosquitto_passwd -U配合 argon2id 的成熟形态,Mosquitto 密码文件管理走过了一条清晰的演进路线。本文以官方博客 updating-password-files.md 记录的升级需求为主线,结合 mosquitto_passwd.c 的实现、mosquitto_passwd.1.xml 的手册说明以及 password_common.c 的哈希算法细节,完整覆盖了明文密码文件升级为哈希密码文件的背景、工具用法、格式原理与安全实践。无论你正在从旧版本迁移,还是需要为存量部署重建密码文件,mosquitto_passwd -U都是完成这一迁移的标准路径。
- 后端
- 消息队列
- 消息路由
【免费下载链接】mosquitto
Eclipse Mosquitto - An open source MQTT broker
相关推荐
copyparty 如何用 Argon2 密码哈希避免配置文件明文密码,并处理 Docker 与 systemd 的 salt 同步
copyparty 如何用 Argon2 密码哈希避免配置文件明文密码,并处理 Docker 与 systemd 的 salt 同步 copyparty 的账户
后端存储网络通信John the Ripper 破解 Eclipse Mosquitto 密码文件:mosquitto2john 完整实战指南
John the Ripper 破解 Eclipse Mosquitto 密码文件:mosquitto2john 完整实战指南 导读 本指南基于 John th
网络安全密码学渗透测试应用安全Tiny-Universe密码哈希:安全存储密码
Tiny Universe密码哈希:安全存储密码 引言:为什么密码安全存储至关重要? 在当今数字化时代,用户密码安全是任何系统的第一道防线。你是否曾担心过:如果
示例工程教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考