ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL报错ERROR 1819:密码策略校验机制与解决方案全解析

MySQL报错ERROR 1819:密码策略校验机制与解决方案全解析 装完MySQL或者用Docker拉起一个实例之后大多数人做的第一件事就是设置密码、创建业务账号。SQL还没敲完回车之后MySQL直接甩回来一行红色报错ERROR 1819 (HY000): Your password does not satisfy the current policy requirements。新手第一反应往往是“我密码是不是打错了”或者是权限问题、网络问题。实际上这条报错是MySQL内置的密码强度校验机制在拦截你跟你写的SQL语法、账号权限、端口连通性都没关系。如果只是偶尔一次报错随便换个复杂密码就过去了。但如果是在Windows下用net start mysql管理服务、在Docker容器里初始化实例或者从MySQL 5.7换到8.0后按旧习惯写配置这条1819会变着花样折腾人。这篇文章把报错背后的校验规则、5.7和8.0的差异、三种可选的解决办法以及我在实际环境里踩过的坑完整梳理一遍遇到同一条错误可以直接照着排查。1. 1819报错出现的常见场景以及这条错误的本意1.1 三种典型触发场景我统计了一下实际工作中最常遇到1819的场景基本集中在下面几个地方。第一种初始化实例之后创建业务账号。比如刚执行完mysqld --initialize-insecureroot用户是空密码登录进去第一件事是建应用账号CREATE USER app% IDENTIFIED BY 123456;这里如果密码策略没有调整过MySQL会直接报错ERROR 1819 (HY000): Your password does not satisfy the current policy requirements。“123456”这种纯数字弱密码属于一看就过不了校验的类型。第二种root或者普通用户修改自己的登录密码。ALTER USER rootlocalhost IDENTIFIED BY abc123;这种情况在测试环境里特别多开发同事想统一用简单密码结果被策略卡住然后过来问“为什么MySQL不让我改密码”。第三种Docker部署MySQL时通过环境变量初始化。比如docker run -d --name mysql-test \ -e MYSQL_ROOT_PASSWORD123456 \ mysql:8.0如果官方镜像初始化时密码策略是默认的MEDIUM容器日志里很容易出现ERROR 1819导致初始化失败容器一直处于反复启动的状态。相比前两种情况这种更隐蔽因为你可能根本不记得自己执行过SQL。还有一个场景是在一些自动化部署脚本里通过mysql init.sql批量导入建库建用户语句脚本里的密码不符合策略日志里也会出现1819但脚本不会中断容易埋到后面才爆发。1.2 报错本质不是权限问题是密码强度校验组件在拦你要理解1819先要接受一个事实MySQL从5.6开始就内置了密码校验的能力只不过5.7以前默认不开。5.7版本以插件形式提供8.0.4之后则以组件形式常驻。这个机制在MySQL里叫validate_password。它的作用和小区门禁类似——你设置的密码必须满足一定条件否则门禁不放行放到MySQL里就是不让你创建用户、不让你改密码。所以当MySQL报1819时意思是你的密码本身太弱不符合当前策略。报错代码HY000是通用错误编号真正有用的信息在后面的Your password does not satisfy the current policy requirements这句话才是定位问题的关键。需要特别注意的是1819不是权限问题不是网络问题更不是密码写错了。哪怕你密码完全正确只要强度不达标MySQL照样拒绝。这也是很多人绕圈子的原因总以为自己在配置文件里漏了什么其实只是密码太简单。2. 密码策略的完整画像LOW、MEDIUM、STRONG到底卡在哪些规则上2.1 一条SQL看穿当前策略遇到1819之后第一步不是急着改密码而是先看当前实例的密码策略长什么样。用这一条语句即可SHOW VARIABLES LIKE validate%;执行结果类似下面这样MySQL 8.0--------------------------------------------- | Variable_name | Value | --------------------------------------------- | validate_password.check_user_name | ON | | validate_password.dictionary_file | | | validate_password.length | 8 | | validate_password.mixed_case_count | 1 | | validate_password.number_count | 1 | | validate_password.policy | 1 | | validate_password.special_char_count | 1 | ---------------------------------------------在MySQL 5.7里变量名中间是下划线比如validate_password_policy、validate_password_length在MySQL 8.0里是点号比如validate_password.policy、validate_password.length。这个差异非常重要后文单独说。注意输出的policy值是1这是数字表示。0对应LOW1对应MEDIUM2对应STRONG。有的版本会把变量值直接显示成英文LOW、MEDIUM、STRONG但数字形式更常见。如果你执行SHOW VARIABLES LIKE check_%或者SHOW VARIABLES LIKE validate_password%查不到任何结果说明当前实例根本没有加载密码校验组件。这种情况多见于旧版本5.6、或者有人手动卸载过组件也可能是社区版的某些精简包。没有组件时密码再简单也不会报1819。2.2 三个级别分别校验什么默认参数逐个数validate_password的三种策略限制力度从松到严具体规则如下策略级别校验规则默认是否启用LOW只校验密码长度默认8位以上否MEDIUM在LOW基础上还要求至少1个大写字母、1个小写字母、1个数字、1个特殊字符是默认STRONG在MEDIUM基础上还要求密码不能出现在字典文件里否光看表格可能不够直观。默认情况下MySQL 8.0是MEDIUM策略所以一个密码必须同时满足长度至少8位至少包含1个大写字母至少包含1个小写字母至少包含1个数字至少包含1个特殊字符比如!#$%^*()_等换句话说abc12345这种8位密码即使长度够没有大写和特殊字符照样过不了。而Aa1!aaaa这种刚好8位的密码虽然看起来奇怪但能通过校验。validate_password.length的默认值是8但需要注意的是这个长度要求只在policy等于LOW以上时才生效。如果你把策略从MEDIUM降到LOW同时把validate_password.length设为6那么6位纯数字密码123456也能通过。2.3 容易被忽略的隐藏规则用户名、密码内容、字符类型有几个规则容易被忽略但实际经常导致“明明密码看起来挺复杂为什么还是报1819”。第一个是validate_password.check_user_name。这个选项默认是ON意思是密码里如果包含了用户名会被直接拒绝。举个例子用户名是alex你设置的密码是Alex2024!这里面包含了alex这个字串哪怕其他条件全部满足MySQL照样报1819。第二个是特殊字符的范围。MySQL不是所有键盘符号都算特殊字符它认的是! # $ % ^ * ( ) _ - [ ] { } ; : , . / ?这类可见符号。如果你用的特殊字符恰好不在这个集合里比如某些语言环境下的引号、破折号可能会被忽略掉。保险起见用最常见的!#$%^*就没有问题。第三个是密码长度按字符计算多字节字符要小心。中文字符或者某些宽字符在不同字符集下占的字节数不同如果密码里混入中文计算长度的逻辑可能会跟你想的不一样。为了省事生产环境密码老实使用ASCII可见字符。第四个是隐藏的用户名匹配规则。check_user_name检查的不仅是完整用户名还包括用户名去掉域名部分、大小写转换后是否出现在密码中。比如用户user%密码User123里含user大小写不敏感所以也会被拦。这点很多人踩过坑明明密码很强就是过不了查了半天发现是自己的名字躺在密码里。3. 三种解法按“锁门水平”从重到轻依次说明3.1 方案A让密码满足策略一条能过审的密码公式最正统的解法是换个符合策略的强密码。这里给一个可直接套用的密码公式大写字母 小写字母 数字 特殊符号 至少8位。按这个公式C0mpl3x!Pass就是一个合格密码。执行ALTER USER rootlocalhost IDENTIFIED BY C0mpl3x!Pass;如果想顺便新建一个业务账号CREATE USER app% IDENTIFIED WITH caching_sha2_password BY C0mpl3x!Pass; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app%; FLUSH PRIVILEGES;之所以推荐“先满足策略”而不是“关掉策略”是因为密码强度校验本身是安全功能。把密码换成强密码几乎不需要额外操作也不用改配置是最省事、最稳妥的办法。对于只在本地开发用的实例这同样适用。一个小小的提醒在命令行里执行ALTER USER时密码如果包含shell特殊字符建议用单引号把整个密码包起来避免被shell解析。如果密码里本身包含单引号可以用双引号包起来或者用转义。3.2 方案B临时把策略降到LOW并保留校验组件有时候确实需要把密码设得简单比如联调环境、临时测试库或者客户要求统一密码长度。这时候可以临时把策略降到LOW只要长度满足即可不需要大小写和特殊字符。在MySQL 8.0中执行SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;在MySQL 5.7中执行SET GLOBAL validate_password_policy LOW; SET GLOBAL validate_password_length 6;这样设置之后再执行ALTER USER rootlocalhost IDENTIFIED BY 123456;就不会报1819了。这里有一个关键细节SET GLOBAL是立即生效的作用于整个实例不需要重启MySQL。但它是“运行时设置”MySQL进程重启之后会回到配置文件里的值或者回到默认值。所以如果只是临时图方便用这种方式就好重启后策略恢复默认不会留下长期隐患。另一个细节是SET GLOBAL不需要重启但已经建立的连接可能需要重新登录一次才能看到新策略。某些客户端用连接池的时候旧连接里执行密码修改可能依然报错重连一次就好了。我遇到过同事改了策略还是报1819就是因为他用的是老连接。3.3 方案C彻底卸载校验组件开发机专用如果是在个人开发机上不想被密码策略限制可以彻底卸载校验组件。这是最彻底的方案但只建议在隔离环境使用。MySQL 8.0使用组件方式卸载命令是UNINSTALL COMPONENT file://component_validate_password;MySQL 5.7使用插件方式卸载命令是UNINSTALL PLUGIN validate_password;卸载之后SHOW VARIABLES LIKE validate%就查不到任何结果了密码长度、复杂度都不再受限制想设成123都行。但这里有一个特别需要注意的坑如果你在配置文件my.cnf或my.ini里已经写过validate_password.policyLOW之类的配置卸载组件后再重启MySQL服务会直接起不来报错信息通常是Unknown system variable validate_password.policy。原因很简单配置项还在组件没了MySQL找不到对应的变量。所以彻底卸载之前一定要顺手把配置文件里的相关行删掉。默认的my.cnf位置在/etc/my.cnf、/etc/mysql/my.cnfWindows下一般在你安装目录里的my.ini。3.4 让修改在重启后依然有效my.cnf / my.ini 配置如果想把密码策略永久改成LOW或者把长度要求永久降下来直接改配置文件更合适。Linux下编辑/etc/my.cnfWindows下编辑my.ini在[mysqld]段添加MySQL 8.0组件版[mysqld] validate_password.policyLOW validate_password.length6 validate_password.number_count0 validate_password.special_char_count0 validate_password.mixed_case_count0MySQL 5.7插件版[mysqld] validate_password_policyLOW validate_password_length6保存后重启MySQL服务即可永久生效。Linux下重启方式一般是systemctl restart mysqldWindows下是net stop mysql再net start mysql或者直接在服务管理器里重启。这里再补充一个细节如果策略已经改成LOWvalidate_password.length设为6那么密码最少6位即可其他三项计数全部为0大小写、数字、特殊字符都不再被单独要求。如果只想限制长度其他全放开把计数项设成0即可。4. 从Windows安装到Docker容器那些绕不开的连环坑4.1 Windows下net start mysql后的1819在Windows上用MySQL 8.0的zip压缩包安装时常见操作是手动初始化实例然后注册成Windows服务再用net start mysql启动。很多人走到ALTER USER设置密码这一步就碰到1819。先说一个容易忽略的细节Windows下服务启动时读取的配置文件路径可能跟你的直觉不一样。如果你的MySQL安装目录是D:\tool\mysql-8.0.46-winx64默认情况下MySQL会在C:\ProgramData\MySQL\MySQL Server 8.0\my.ini找配置或者根本不主动找配置文件。所以如果你把validate_password相关的配置写进了安装目录下的my.ini但服务启动时根本没用这个文件策略还是默认的MEDIUM密码还是会报1819。建议在Windows下排查时先启动MySQL之后执行SHOW VARIABLES LIKE validate%;看看当前实例实际生效的配置是从哪里来的。如果想确认MySQL到底读了哪个配置文件可以用命令mysqld --verbose --help | findstr my.ini或者直接运行mysqld --print-defaults这会输出MySQL认为自己在用的配置路径比人肉找配置文件靠谱得多。另一个Windows常见坑是安装完服务之后用net start mysql启动失败日志里显示ERROR 1819。这种情况往往是初始化实例时设了过弱的密码而实例本身已经启动了。此时应该先用mysqld --console在前台启动观察完整输出能看到是哪条SQL触发了1819而不是傻傻地反复重启服务。4.2 Docker容器里初始化MySQL时的1819Docker场景比较特殊。用docker run拉起MySQL容器时如果通过环境变量传入弱密码初始化容器时也可能出现1819。例如docker run -d --name mysql-dev \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEtestdb \ mysql:8.0容器日志里出现类似“Your password does not satisfy the current policy requirements”然后容器状态异常。因为官方镜像的entrypoint在初始化阶段会用环境变量里的密码去设置root账号如果密码不符合策略初始化就会失败容器反复重启。处理办法有三个。第一个直接把环境变量里的密码换成强密码比如M_y!Str0ng123然后重启容器。第二个挂载一个自定义配置文件把策略降下来docker run -d --name mysql-dev \ -v /opt/mysql-conf/my.cnf:/etc/mysql/conf.d/custom.cnf \ -e MYSQL_ROOT_PASSWORD123456 \ mysql:8.0其中/opt/mysql-conf/my.cnf内容跟上文一样写[mysqld]下的策略配置即可。第三个先启动一个临时容器用强密码完成初始化再进入容器修改密码docker run -d --name mysql-dev -e MYSQL_ROOT_PASSWORDTemp!Pass2024 mysql:8.0 docker exec -it mysql-dev mysql -uroot -p进入MySQL后执行SET GLOBAL validate_password.policy LOW; ALTER USER root% IDENTIFIED BY 123456;注意容器里root账号的host可能就是%不是localhost修改时要看清楚。4.3 变量名的版本差异5.7的下划线碰到8.0的点号这个坑非常经典很多人搜到一篇5.7的解决方案拿过来在8.0上执行结果报ERROR 1193 (HY000): Unknown system variable validate_password_policy反过来在5.7上执行8.0的点号变量同样报错。MySQL 5.7的变量名统一用下划线validate_password_policy validate_password_length validate_password_number_count validate_password_mixed_case_count validate_password_special_char_count validate_password_check_user_nameMySQL 8.0的组件变量名统一用点号validate_password.policy validate_password.length validate_password.number_count validate_password.mixed_case_count validate_password.special_char_count validate_password.check_user_name其实你只要在实例里执行一次SHOW VARIABLES LIKE validate%;看输出里变量名是下划线还是点号就知道当前版本应该用哪一套写法。如果实在记不住可以查SELECT VERSION();得到版本号8.0.4之后默认组件版5.7则是插件版。两条路径从原理上说是一样的但SQL写法完全不能混。5. 生产环境的正确姿势不要一关了之5.1 降级策略带来的实际风险有不少人被1819惹烦了直接在配置里把策略关掉或者降到LOW。开发环境无所谓但生产环境这么做等于把MySQL的密码防线主动拆了。生产环境里的账号往往有较高权限比如DBA账号、备份账号、主从复制账号。如果密码只有纯数字6位暴力破解、撞库、日志泄露的风险都会显著上升。而且生产数据库一旦出现密码泄露事件第一波被问责的就是DBA或者运维因为“为什么密码策略是LOW”这个问题根本答不上来。我见到过一家公司的内部数据库所有服务器的密码统一是123456因为最初建库时为了省事把策略降成了LOW结果几年下来所有实例都是弱密码。后来做等保整改光改密码就花了三天还要协调几十个应用系统同步修改过程极其痛苦。5.2 在审计和合规要求下的折中做法如果公司有等保测评、内部安全审计密码策略这一项几乎是必查项。与其临时抱佛脚不如从一开始就保持默认的MEDIUM策略然后把密码生成和管理流程规范化。折中做法是对内网测试环境可以适度放宽比如保留MEDIUM但把长度要求降到8生产环境保持官方推荐的MEDIUM以上。另外开启validate_password.check_user_name ON防止密码里面包含用户名。8.0版本还支持把字典文件配起来比如设置validate_password.dictionary_file指向一个包含公司名、产品名的文本文件密码中禁止出现这些词。还有一个思路是定期自动轮换密码。不要指望开发同事自己记住三个月改一次密码用密码管理器生成的随机密码到期自动更换对所有人都省心。5.3 一个可以直接抄的强密码模板和管理习惯如果不想每次都为想密码头疼可以直接用下面这个模板长度12位以上至少一个大写、一个小写、一个数字、一个特殊字符不要包含用户名和常见英文单词。比如K9vF2#mL7$xQ这种密码用工具生成就好不需要自己硬凑。Linux下可以随手用openssl生成openssl rand -base64 18 | tr -d /输出的字符串通常包含大小写字母和数字可能缺少特殊字符可以手动在后面补一个!#之类。密码确定之后建议放进密码管理工具比如KeePass、Bitwarden不要直接写在项目代码的注释里。如果MySQL的账号要提供给其他人使用至少把密码放到内部机密管理平台而不是在群里明文发送。最后给一条可以复用的排查主链路报1819之后先SELECT VERSION();确认版本再SHOW VARIABLES LIKE validate%;确认组件是否加载、策略是哪一档、变量名是点号还是下划线。如果是Windows额外确认服务到底读的哪个my.ini如果是Docker先看容器日志再进容器执行SQL如果是从5.7迁到8.0先核对变量名再改配置。按这个顺序走一遍大部分1819在五分钟之内都能解决。
返回列表