mysql 8.0+ 真正实现输错密码后自动锁定账户,唯一有效方式是 failed_login_attempts 与 password_lock_time 成对配置于 caching_sha2_password 或 mysql_native_password 用户;connection_control 插件仅增加延迟,不修改 account_locked 字段,不属于真锁定。

MySQL 8.0+ 真正能实现「输错密码后自动锁定账户」的,只有 FAILED_LOGIN_ATTEMPTS 和 PASSWORD_LOCK_TIME 成对配置这一条路;其他方案如 connection_control 插件只是加延迟,不改 account_locked 字段,不算真锁定。
FAILED_LOGIN_ATTEMPTS 必须显式声明才生效
这个参数不是全局开关,而是每个用户的元数据,创建或修改用户时必须手动写进去:
-
CREATE USER 'u'@'%' IDENTIFIED BY 'p' FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1;—— 输错 5 次锁 24 小时 -
ALTER USER 'u'@'%' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 0;—— 锁定后永不自动恢复(需管理员手动ACCOUNT UNLOCK) - 漏掉任意一个字段(比如只设
FAILED_LOGIN_ATTEMPTS不设PASSWORD_LOCK_TIME),锁定不会触发 - 该策略只对认证插件为
caching_sha2_password或mysql_native_password的用户生效;sha256_password用户完全无视
查账户是否真被锁,别信错误提示
输错密码和账户被锁,MySQL 都返回 ERROR 1045 (28000): Access denied for user,无法靠客户端错误码区分。得查系统表:
SELECT user, host, account_locked, failed_login_attempts FROM mysql.user WHERE user = 'u';
关键看两列:
-
account_locked = 'Y'→ 已触发自动锁定,后续登录直接拒绝,不走密码校验 -
failed_login_attempts > 0但account_locked = 'N'→ 还在累计失败次数,未达阈值
connection_control 插件只能加延迟,不能锁账号
它常被误认为是“锁定方案”,实际只是让第 N+1 次失败响应变慢,所有连接仍能到达 MySQL:
- 必须同时加载两个插件:
connection_control和connection_control_failed_login_attempts,缺一不可 -
connection_control_failed_connections_threshold = 5表示从第 6 次失败开始启用延迟逻辑 - 延迟单位是毫秒:
connection_control_min_connection_delay = 60000是 1 分钟;设成 0(默认值)就等于没配 - 确认是否生效,运行
SHOW STATUS LIKE 'Connection_control_delayed_connections';,返回值非零才算真起作用 - 它不记录 IP、不写
account_locked、不拦截连接,爆破工具根本不在乎这点延迟
真正封 IP 得靠 fail2ban + 错误日志解析
MySQL 本身不提供按 IP 封禁能力,max_connect_errors 是按 IP 封整个来源(包括合法用户),已弃用且不推荐。生产环境最稳的路径是:
- 在
my.cnf中开启详细日志:log_error_verbosity = 3 - 确保错误日志里有带真实 IP 的行,例如:
[Warning] [MY-010054] Access denied for user 'root'@'192.168.1.100' - fail2ban 的
jail.local配[mysqld-auth]段,正则匹配Access denied.*@'(?P<host>\S+)'</host> - 封禁动作调用
iptables或ufw,端口限定为3306
注意:FAILED_LOGIN_ATTEMPTS 是账号维度,fail2ban 是 IP 维度,两者互补,但不能互相替代;最容易被忽略的是——PASSWORD_LOCK_TIME 只接受整数天,想锁 30 分钟?必须自己在应用层或代理层做计数控制。











