MySQL 8.0 默认使用 caching_sha2_password 插件自动加盐散列数据库用户密码,无需手动干预;但业务表中存储用户密码时,必须在应用层使用 bcrypt/Argon2 等专业密码哈希函数,不可用 SHA2() 手动拼接 salt。

caching_sha2_password 是 MySQL 8.0 默认认证插件,它自动对用户密码加盐并散列,但这个过程完全由 MySQL 内部管理,你无法控制盐值、无法导出或复现该哈希逻辑。所以问题本质不是“如何手动加盐”,而是:你是否需要在业务表中自己实现加盐散列?
如果你是在 mysql.user 表里创建数据库用户(比如 CREATE USER 'app'@'%' IDENTIFIED BY 'p@ssw0rd';),那就不用管加盐——MySQL 已经做了,且比你自己写更可靠。
但如果你是在自己的业务表(如 users)里存用户登录密码,那必须在应用层做,MySQL 不提供安全的加盐散列原语。
为什么不能用 SHA2() + 手动拼接 salt?
很多人试过:INSERT INTO users (pwd_hash) VALUES (SHA2(CONCAT('mypassword', 'randomsalt123'), 256)); —— 这看似加盐,实则危险:
- SHA2 是快速哈希,适合校验,不适合密码:GPU 能每秒暴力跑上亿次
- 你得自己生成、存储、管理 salt 字段,稍有疏忽(比如 salt 复用、长度太短、硬编码)就失效
- 没有迭代次数控制,无法抵抗暴力破解
- MySQL 不支持 bcrypt/scrypt/PBKDF2,
SHA2()无法替代专业密码哈希函数
应用层加盐散列的最小可行做法
别在 SQL 里拼 salt,也别用 MD5() 或 SHA1() —— 它们已被淘汰。正确路径只有一条:交给语言原生安全函数。
- PHP:直接用
password_hash()(默认PASSWORD_ARGON2I或PASSWORD_BCRYPT),它自动生成随机 salt 并嵌入输出字符串中;验证用password_verify() - Python:用
bcrypt库的hashpw()和checkpw(),或passlib的argon2 - Java:用
BCryptPasswordEncoder(Spring Security)或Argon2Factory - Node.js:用
bcrypt或argon2npm 包
示例(PHP):
$hash = password_hash('user_input', PASSWORD_ARGON2ID); // 返回类似 $argon2id$v=19$m=65536,t=3,p=4$... 的字符串<br>INSERT INTO users (username, password_hash) VALUES ('alice', ?); // 直接存整个 hash 字符串注意:这个字符串里已包含 salt 和参数,无需额外字段存 salt。
误用 mysql_native_password 插件的风险
有些旧应用为兼容性强制切换认证插件:ALTER USER 'u'@'h' IDENTIFIED WITH mysql_native_password BY 'p';。这会退回到 SHA-1 哈希(前缀 *A...),且不加盐或 salt 可预测。在 MySQL 8.0 中应避免,除非对接极老客户端且无法升级。
- 现象:查询
mysql.user发现authentication_string以*开头且长度为 41 字符(SHA-1) - 影响:SHA-1 已被实际碰撞攻击,且无有效 salt,彩虹表可秒破常见密码
- 修复:优先升级客户端驱动,或显式指定
caching_sha2_password
真正关键的不是“怎么让 MySQL 加盐”,而是分清场景:DB 用户密码由 MySQL 管,业务用户密码由应用管。混淆这两者,最容易掉进自己拼 SHA2() + 固定 salt 的坑里——看着像加盐,实际等于没加。


















