找回密码
 立即注册
首页 业界区 安全 MySQL 密码防暴力破解插件:Connection Control ...

MySQL 密码防暴力破解插件:Connection Control

王妍芳 2025-8-18 08:41:55
Connection Control 是 MySQL 8.0 引入的一个安全功能插件,后移植到 MySQL 5.7.17 和 5.6.35 版本。
其核心功能是:当客户端因账号或密码错误连续多次登录失败时,服务端会对该客户端的后续请求进行延迟处理,且失败次数越多,延迟时间越长。这一机制能显著增加密码被暴力破解的耗时,从而有效遏制此类攻击。
适用场景:

  • 面向公网开放的 MySQL 服务器。
  • 合规性与安全性要求较高的环境。
插件效果

首先我们看看该插件的效果。
  1. # time mysql -h10.0.0.108 -uroot -p123
  2. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  3. real    0m0.013s

  4. # time mysql -h10.0.0.108 -uroot -p123
  5. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  6. real    0m0.013s

  7. # time mysql -h10.0.0.108 -uroot -p123
  8. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  9. real    0m0.013s

  10. # time mysql -h10.0.0.108 -uroot -p123
  11. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  12. real    0m1.013s

  13. # time mysql -h10.0.0.108 -uroot -p123
  14. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  15. real    0m2.013s

  16. # time mysql -h10.0.0.108 -uroot -p123
  17. ERROR 1045 (28000): Access denied for user 'root'@'10.0.0.75' (using password: YES)
  18. real    0m3.014s
复制代码
前三次没有延迟,第四次开始延迟 1 秒,之后每次失败都会增加 1 秒的延迟。
当连接被延迟时,其在SHOW PROCESSLIST中的状态是Waiting in connection_control plugin:
  1. mysql> show processlist;
  2. +------+-----------------+-----------------+------+---------+--------+--------------------------------------+------------------+
  3. | Id   | User            | Host            | db   | Command | Time   | State                                | Info             |
  4. +------+-----------------+-----------------+------+---------+--------+--------------------------------------+------------------+
  5. |    5 | event_scheduler | localhost       | NULL | Daemon  | 418840 | Waiting on empty queue               | NULL             |
  6. | 1179 | root            | localhost       | NULL | Query   |      0 | init                                 | show processlist |
  7. | 1187 | root            | 10.0.0.75:40920 | NULL | Connect |      3 | Waiting in connection_control plugin | NULL             |
  8. +------+-----------------+-----------------+------+---------+--------+--------------------------------------+------------------+
  9. 3 rows in set, 1 warning (0.00 sec)
复制代码
接下来我们看看该插件的使用方法、相关参数、禁用方法、注意事项以及实现细节。
使用方法

安装插件
  1. INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so';
  2. INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so';
复制代码
其中,connection_control 是核心功能插件,connection_control_failed_login_attempts 提供记录客户端失败尝试次数的information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS表。
卸载插件
  1. UNINSTALL PLUGIN CONNECTION_CONTROL;
  2. UNINSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
复制代码
从 MySQL 9.2 开始,引入了 Connection Control component 来替代 Connection Control plugin。
相关参数和状态变量

参数
  1. mysql> show variables like '%connection_control%';
  2. +-------------------------------------------------+------------+
  3. | Variable_name                                   | Value      |
  4. +-------------------------------------------------+------------+
  5. | connection_control_failed_connections_threshold | 3          |
  6. | connection_control_max_connection_delay         | 2147483647 |
  7. | connection_control_min_connection_delay         | 1000       |
  8. +-------------------------------------------------+------------+
  9. 3 rows in set (0.03 sec)
复制代码
其中,

  • connection_control_failed_connections_threshold:触发延迟的失败尝试次数阈值,默认为 3。
  • connection_control_min_connection_delay:首次触发延迟时的最小延迟时间,默认 1000 毫秒,即 1 秒。
  • connection_control_max_connection_delay:最大延迟时间,默认 2147483647 毫秒,相当于 24.8 天。
状态变量
Connection_control_delay_generated
显示触发延迟的总次数。
除此之外,还可以通过information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS查询不同客户端的失败尝试次数,例如:
  1. mysql> select * from information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
  2. +----------------------+-----------------+
  3. | USERHOST             | FAILED_ATTEMPTS |
  4. +----------------------+-----------------+
  5. | 'root'@'10.0.0.108'  |               5 |
  6. | 'root'@'127.0.0.1'   |               4 |
  7. | 'root1'@'10.0.0.108' |               7 |
  8. +----------------------+-----------------+
  9. 3 rows in set (0.00 sec)
复制代码
如何禁用延迟功能

以下两种方法都可以:

  • 卸载插件。
  • 将 connection_control_failed_connections_threshold 设置为 0。
注意事项

1. 当用户连续多次登录失败后,即使后续输入正确密码,首次成功登录仍会被延迟(仅限首次成功登录,后续登录不受影响)。这种设计虽然可能不符合部分用户的习惯,但从防止暴力破解的角度来看是合理的。如果正确密码不触发延迟,攻击者便可通过是否存在延迟来判断密码是否正确,这样就会削弱防暴力破解的效果。
2. 修改 connection_control_failed_connections_threshold 会:

  • 将 Connection_control_delay_generated 重置为 0。
  • 清空 information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS。
如果某个用户的失败次数过多,但希望在输入正确密码时不再等待,可以通过以下命令清空其延迟状态:
  1. SET GLOBAL connection_control_failed_connections_threshold = DEFAULT;
复制代码
3. 被延迟的连接依旧会占用连接数。
如果 connection_control_min_connection_delay 设置过大,且攻击端重试频率很高,可能导致连接数被耗尽,影响正常用户的连接。
实现细节

以下是 Connection Control plugin 被调用的时机:
  1. static int check_connection(THD *thd) {
  2.   ...
  3. if (!thd->m_main_security_ctx.host().length)  // If TCP/IP connection
  4.   {
  5.     ...
  6.     if (acl_check_host(thd, thd->m_main_security_ctx.host().str,
  7.                        main_sctx_ip.str)) { // 检查客户端IP是否允许连接到 MySQL 服务器
  8.       /* HOST_CACHE stats updated by acl_check_host(). */
  9.       my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
  10.                thd->m_main_security_ctx.host_or_ip().str);
  11.       return1;
  12.     }
  13.   } 
  14.   ...
  15.   auth_rc = acl_authenticate(thd, COM_CONNECT); // 这里验证用户密码是否正确

  16. if (mysql_event_tracking_connection_notify(
  17.           thd, AUDIT_EVENT(EVENT_TRACKING_CONNECTION_CONNECT))) { // 这里会调用 Connection Control 插件
  18.     return1;
  19.   }

  20.   ...
  21. return auth_rc;
  22. }
复制代码
首先检查客户端的 IP 是否被允许连接到 MySQL 服务器。如果 IP 地址不在允许的范围内,则提示ERROR 1130 (HY000): Host 'xxx' is not allowed to connect to this MySQL server 。
接着验证用户的密码是否正确。如果密码错误,则提示ERROR 1045 (28000): Access denied for user 'xxx'@'xxx' (using password: YES),错误在返回给客户端之前,会调用 Connection Control 插件,判断是否需要延迟连接。
这实际上提供了一个思路,如果担心延迟的连接占用过多的连接数,可以通过缩小用户账号的 host 范围,在密码验证之前拒绝不符合条件的连接,从而有效减少无效连接的占用。
此外,当 MySQL 实例可能暴露于公网时,切忌将 host 设置为%,否则数据库将面临较高的恶意攻击风险。
接下来我们看看  Connection Control 触发延迟的实现逻辑。
  1. bool Connection_delay_action::notify_event(
  2.     MYSQL_THD thd, Connection_event_coordinator_services *coordinator,
  3.     const mysql_event_connection *connection_event,
  4.     Error_handler *error_handler) {
  5.   DBUG_TRACE;
  6. bool error = false;
  7. constunsignedint subclass = connection_event->event_subclass;
  8.   Connection_event_observer *self = this;
  9. // 只处理连接建立和用户切换事件
  10. if (subclass != MYSQL_AUDIT_CONNECTION_CONNECT &&
  11.       subclass != MYSQL_AUDIT_CONNECTION_CHANGE_USER)
  12.     return error;

  13. RD_lock rd_lock(m_lock);
  14. // 获取参数 connection_control_failed_connections_threshold 的值
  15. const int64 threshold = this->get_threshold();

  16. // 如果 connection_control_failed_connections_threshold 小于等于 0,则直接返回,相当于禁用了插件
  17. if (threshold <= DISABLE_THRESHOLD) return error;

  18.   int64 current_count = 0;
  19. bool user_present = false;
  20.   Sql_string userhost;
  21. // 根据当前线程信息生成用户标识,格式为 user@host
  22.   make_hash_key(thd, userhost);

  23.   DBUG_PRINT("info", ("Connection control : Connection event lookup for: %s",
  24.                       userhost.c_str()));

  25. // 从哈希表中查找该用户的失败连接次数
  26.   user_present = m_userhost_hash.match_entry(userhost, (void *)&current_count)
  27.                      ? false
  28.                      : true;
  29. // 如果该用户的失败连接次数大于或等于阈值,则触发延迟
  30. if (current_count >= threshold || current_count < 0) {
  31.     // 根据失败次数计算延迟时间
  32.     const ulonglong wait_time = get_wait_time((current_count + 1) - threshold);
  33.     // 触发状态变量自增(Connection_control_delay_generated)
  34.     if ((error = coordinator->notify_status_var(
  35.              &self, STAT_CONNECTION_DELAY_TRIGGERED, ACTION_INC))) {
  36.       error_handler->handle_error(
  37.           ER_CONN_CONTROL_STAT_CONN_DELAY_TRIGGERED_UPDATE_FAILED);
  38.     }
  39.     // 执行延迟等待
  40.     rd_lock.unlock();
  41.     conditional_wait(thd, wait_time);
  42.     rd_lock.lock();

  43.     DBUG_EXECUTE_IF("delay_after_connection_delay", sleep(2););
  44.   }

  45. if (connection_event->status) {
  46.     // 连接失败,更新哈希表中的失败计数
  47.     if (m_userhost_hash.create_or_update_entry(userhost)) {
  48.       error_handler->handle_error(
  49.           ER_CONN_CONTROL_FAILED_TO_UPDATE_CONN_DELAY_HASH, userhost.c_str());
  50.       error = true;
  51.     }
  52.   } else {
  53.     // 连接成功,从 m_userhost_hash 表中删除该用户对应的记录
  54.     if (user_present) {
  55.       (void)m_userhost_hash.remove_entry(userhost);
  56.     }
  57.   }

  58. return error;
  59. }
复制代码
这个函数中需要注意的地方有两点:

  • 延迟时间。
    延迟时间 = (当前失败次数 + 1 - 阈值)秒,失败次数越多,延迟时间越长。
    延迟时间受到最小值和最大值 (connection_control_min_connection_delay 和 connection_control_max_connection_delay) 的限制。
  • USERHOST 的构造规则。
    如果连接的用户名在mysql.user表中存在,则 USERHOST 的 host 部分取自于mysql.user表中该用户的host字段值。
    如果用户名不存在,则 host 部分会使用客户端的 IP 地址。
看下面这个示例。
  1. mysql> select user,host from mysql.user whereuser='root'and host='%';
  2. +------+------+
  3. | user | host |
  4. +------+------+
  5. | root | %    |
  6. +------+------+
  7. 1 row in set (0.00 sec)

  8. # 用户 root 在 mysql.user 表中存在,且 host 为 %,所以生成的 USERHOST 是 'root'@'%'。
  9. # mysql -h10.0.0.108 -uroot -p123
  10. mysql: [Warning] Using a passwordon the command line interface can be insecure.
  11. ERROR1045 (28000): Access denied foruser'root'@'10.0.0.75' (usingpassword: YES)

  12. mysql> select * from information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
  13. +------------+-----------------+
  14. | USERHOST   | FAILED_ATTEMPTS |
  15. +------------+-----------------+
  16. | 'root'@'%' |               1 |
  17. +------------+-----------------+
  18. 1 row in set (0.00 sec)

  19. # 用户 root1 在 mysql.user 表中不存在,所以生成的 USERHOST 是 'root1'@'客户端IP'。
  20. # mysql -h10.0.0.108 -uroot1 -p123
  21. mysql: [Warning] Using a passwordon the command line interface can be insecure.
  22. ERROR1045 (28000): Access denied foruser'root1'@'10.0.0.75' (usingpassword: YES)

  23. mysql> select * from information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
  24. +---------------------+-----------------+
  25. | USERHOST            | FAILED_ATTEMPTS |
  26. +---------------------+-----------------+
  27. | 'root1'@'10.0.0.75' |               1 |
  28. +---------------------+-----------------+
  29. 1 row in set (0.00 sec)
复制代码
参考资料


  • https://dev.mysql.com/doc/refman/8.4/en/connection-control-plugin-installation.html
  • https://dev.mysql.com/doc/refman/9.2/en/connection-control-component.html
  • https://dev.mysql.com/blog-archive/the-connection_control-plugin-keeping-brute-force-attack-in-check/

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册