防syn泛滥攻击、暴力破解攻击
1)max_connect_errors 网络超时异常错误次数。非连接密码错误次数。
错误:ERROR 1129 (00000): Host 'xxx' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'
很多资料说,这个是密码输入错误的尝试次数超过max_connect_errors变量,MySQL就会阻塞这个客户端登录。
官方描述:
If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from further connections. You can unblock blocked hosts by flushing the host cache. To do so, issue a FLUSH HOSTS statement or execute a mysqladmin flush-hosts command. If a connection is established successfully within fewer than max_connect_errors attempts after a previous connection was interrupted, the error count for the host is cleared to zero. However, once a host is blocked, flushing the host cache is the only way to unblock it. The default is 100.
大致如下:如果MySQL服务器连续接收到了来自于同一个主机的请求,而且这些连续的请求全部都没有成功的建立连接就被中断了,当这些连续的请求的累计值大于max_connect_errors的设定值时,MySQL服务器就会阻止这台主机后续的所有请求。
注意:其实这个就是因为由于网络异常而中止数据库连接。并没有提及密码错误尝试。
验证过程:
在MySQL数据库里面创建一个test账号,然后我们将max_connect_errors变量设置为3.
mysql> set global max_connect_errors=3;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%max_connect_error%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| max_connect_errors | 3 |
+--------------------+-------+
1 row in set (0.00 sec)
然后我们在另外一台测试机器,以错误的密码去连接这个MySQL数据库,如下所示,即使前面输入了三次错误密码,第四次输入是也没有碰到上面错误。那么可以排除这个变量与密码错误输入有关系。
[root@mytestlnx02 tmp]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 1045 (28000): Access denied for user 'test'@'mytestlnx02' (using password: YES)
[root@mytestlnx02 tmp]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 1045 (28000): Access denied for user 'test'@'mytestlnx02' (using password: YES)
[root@mytestlnx02 tmp]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 1045 (28000): Access denied for user 'test'@'mytestlnx02' (using password: YES)
[root@mytestlnx02 tmp]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 1045 (28000): Access denied for user 'test'@'mytestlnx02' (using password: YES)
其实,关于某个IP客户端输入了错误密码,MySQL会在performance_schema数据库下的host_cache表中记录。它会累计记录在COUNT_AUTHENTICATION_ERRORS字段,如下所示:
mysql> select * from performance_schema.host_cache\G;
*************************** 1. row ***************************
IP: 192.168.27.180
HOST: gettestlnx02
HOST_VALIDATED: YES
SUM_CONNECT_ERRORS: 0
COUNT_HOST_BLOCKED_ERRORS: 0
COUNT_NAMEINFO_TRANSIENT_ERRORS: 0
COUNT_NAMEINFO_PERMANENT_ERRORS: 0
COUNT_FORMAT_ERRORS: 0
COUNT_ADDRINFO_TRANSIENT_ERRORS: 0
COUNT_ADDRINFO_PERMANENT_ERRORS: 0
COUNT_FCRDNS_ERRORS: 0
COUNT_HOST_ACL_ERRORS: 0
COUNT_NO_AUTH_PLUGIN_ERRORS: 0
COUNT_AUTH_PLUGIN_ERRORS: 0
COUNT_HANDSHAKE_ERRORS: 0
COUNT_PROXY_USER_ERRORS: 0
COUNT_PROXY_USER_ACL_ERRORS: 0
COUNT_AUTHENTICATION_ERRORS: 4
COUNT_SSL_ERRORS: 0
COUNT_MAX_USER_CONNECTIONS_ERRORS: 0
COUNT_MAX_USER_CONNECTIONS_PER_HOUR_ERRORS: 0
COUNT_DEFAULT_DATABASE_ERRORS: 0
COUNT_INIT_CONNECT_ERRORS: 0
COUNT_LOCAL_ERRORS: 0
COUNT_UNKNOWN_ERRORS: 0
FIRST_SEEN: 2018-01-31 16:28:19
LAST_SEEN: 2018-01-31 16:28:26
FIRST_ERROR_SEEN: 2018-01-31 16:28:19
LAST_ERROR_SEEN: 2018-01-31 16:28:26
1 row in set (0.00 sec)
官方资料介绍,COUNT_AUTHENTICATION_ERRORS的字段是统计被视为“阻塞”的连接错误的数量(根据max_connect_errors系统变量进行评估)。 只计算协议握手错误,并且仅用于通过验证的主机(HOST_VALIDATED = YES)。
MySQL客户端与数据库建立连接需要发起三次握手协议,正常情况下,这个时间非常短,但是一旦网络异常,网络超时等因素出现,就会导致这个握手协议无法完成,MySQL有个参数connect_timeout,它是MySQL服务端进程mysqld等待连接建立完成的时间,单位为秒。如果超过connect_timeout时间范围内,仍然无法完成协议握手话,MySQL客户端会收到异常,异常消息类似于: Lost connection to MySQL server at 'XXX', system error: errno,该变量默认是10秒.
那么我们就构造一个网络超时引起的数据库连接被中断案例吧,我们用Linux下的netem与tc命令模拟构造出复杂环境下的网络传输延时案例,如下设置后,此时从测试服务器去访问MySQL服务器,都会出现延时11秒:
[root@gettestlnx02 ~]# ping 10.20.57.24
PING 10.20.57.24 (10.20.57.24) 56(84) bytes of data.
64 bytes from 10.20.57.24: icmp_seq=1 ttl=62 time=0.251 ms
64 bytes from 10.20.57.24: icmp_seq=2 ttl=62 time=0.330 ms
64 bytes from 10.20.57.24: icmp_seq=3 ttl=62 time=0.362 ms
64 bytes from 10.20.57.24: icmp_seq=4 ttl=62 time=0.316 ms
[root@gettestlnx02 ~]# tc qdisc add dev eth0 root netem delay 11000ms
[root@gettestlnx02 ~]# ping 10.20.57.24
PING 10.20.57.24 (10.20.57.24) 56(84) bytes of data.
64 bytes from 10.20.57.24: icmp_seq=1 ttl=62 time=11000 ms
64 bytes from 10.20.57.24: icmp_seq=2 ttl=62 time=11000 ms
64 bytes from 10.20.57.24: icmp_seq=3 ttl=62 time=11000 ms
我们在测试服务器gettestlnx02连接MySQL数据库,如下所示(注意,如果你是在通过ssh连接这台服务器的话,此时在gettestlnx02上操作会相当慢。当然你也可以在MySQL服务器模拟网络延时,或者你将connect_timeout和网络延时都设小一点)
[root@gettestlnx02 ~]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading authorization packet', system error: 0
如上所示,由于网络延时超过10秒,导致连接MySQL失败,此时,你在MySQL服务器上查询host_cache表时,那么你就会看到SUM_CONNECT_ERRORS变成1了,COUNT_HANDSHAKE_ERRORS也变成了1.
那么我们反复这样折腾三次,那么你会看到SUM_CONNECT_ERRORS变成3了,COUNT_HANDSHAKE_ERRORS也变成了3了.
mysql> select * from host_cache\G;
*************************** 1. row ***************************
IP: 192.168.27.180
HOST: gettestlnx02
HOST_VALIDATED: YES
SUM_CONNECT_ERRORS: 3
COUNT_HOST_BLOCKED_ERRORS: 1
COUNT_NAMEINFO_TRANSIENT_ERRORS: 0
COUNT_NAMEINFO_PERMANENT_ERRORS: 0
COUNT_FORMAT_ERRORS: 0
COUNT_ADDRINFO_TRANSIENT_ERRORS: 0
COUNT_ADDRINFO_PERMANENT_ERRORS: 0
COUNT_FCRDNS_ERRORS: 0
COUNT_HOST_ACL_ERRORS: 0
COUNT_NO_AUTH_PLUGIN_ERRORS: 0
COUNT_AUTH_PLUGIN_ERRORS: 0
COUNT_HANDSHAKE_ERRORS: 3
COUNT_PROXY_USER_ERRORS: 0
COUNT_PROXY_USER_ACL_ERRORS: 0
COUNT_AUTHENTICATION_ERRORS: 4
COUNT_SSL_ERRORS: 0
COUNT_MAX_USER_CONNECTIONS_ERRORS: 0
COUNT_MAX_USER_CONNECTIONS_PER_HOUR_ERRORS: 0
COUNT_DEFAULT_DATABASE_ERRORS: 0
COUNT_INIT_CONNECT_ERRORS: 0
COUNT_LOCAL_ERRORS: 0
COUNT_UNKNOWN_ERRORS: 0
FIRST_SEEN: 2018-01-31 16:28:19
LAST_SEEN: 2018-01-31 17:02:10
FIRST_ERROR_SEEN: 2018-01-31 16:28:19
LAST_ERROR_SEEN: 2018-01-31 17:02:10
1 row in set (0.00 sec)
然后我们用netem与tc 命令在测试服务器上取消网络延迟模拟,然后去测试连接MySQL数据库,如下测试所示:
[root@gettestlnx02 ~]# tc qdisc del dev eth0 root netem delay 11000ms
[root@gettestlnx02 ~]# mysql -h10.20.57.24 -utest -p
Enter password:
ERROR 1129 (HY000): Host '192.168.27.180' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
此时就能构造出开头的错误了。
解决方案
解决网络延迟。
临时解决方法:
1、将变量max_connection_errors的值设置为一个更大的值
mysql> set global max_connect_errors=150;
Query OK, 0 rows affected (0.00 sec)
可以在my.cnf配置文件里面配置。
2、使用flush hosts
mysql> flush hosts;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from performance_schema.host_cache;
Empty set (0.00 sec)
2)那么host cache是什么呢?
简单来说,就是MySQL服务器在内存中维护一个包含客户端信息的缓存:IP地址,主机名和错误信息等。 服务器会将非本地TCP连接信息缓存起来。它不会缓存使用环回接口地址(127.0.0.1或者:: 1)建立的TCP连接,或者使用Unix套接字文件,命名管道或共享内存建立的连接。host cache信息可以通过performance_schema数据库下的host_cache表查询。
查看是否开启,性能模式。
show variables like 'performance_schema';
开启性能模式,host_cache开会记录:
set global performance_schema=on;
总结:关于参数max_connect_errors,这个不能作为防暴力破解密码攻击的手段。但可以防止SYN泛滥攻击。
但是在performance_schema.host_cache记录了COUNT_AUTHENTICATION_ERRORS数量,是可以作为密码错误参考的数据。
3)Aborted_clients & Aborted_connects状态变量
Aborted Connect表示尝试连接到MySQL服务器失败的次数。这个状态变量可以结合host_cache表和错误日志一起来分析问题。
引起这个状态变量激增的原因如下:
1、 客户端没有权限但是尝试访问MySQL数据库。
2、 客户端输入的密码有误。
3、 错误的连接packet包信息。
4、 超过连接时间限制,主要是这个系统变量connect_timeout控制(mysql默认是10s)
Aborted Clients表示由于客户端没有正确关闭连接而中止的连接数。
当Aborted Clients增大的时候意味着有客户端成功建立连接,但是由于某些原因断开连接或者被终止了,这种情况一般发生在网络不稳定的环境中。主要的可能性有:
1、 客户端程序在退出之前未调用mysql_close()正确关闭MySQL连接。
2、 客户端休眠的时间超过了系统变量wait_timeout和interactive_timeout的值,导致连接被MySQL进程终止。
3、 客户端程序在数据传输过程中突然结束,网络异常。
还有一些原因导致两个变量增加:
1、max_allowed_packet变量值太小,或者查询需要的内存比分配给mysqld的内存多。
2、在Linux上使用Ethernet协议,包括半双工和全双工,可能产生异常。
3、线程库的问题导致读取时中断。
4、TCP/IP配置不当。
5、Ethernets, hubs, switches, cables等硬件故障。
1、 客户端没有权限但是尝试访问MySQL数据库。
(1)使用不存在的账号访问MySQL。
实验对比测试前,先将状态变量清零。
mysql> flush status;
Query OK, 0 rows affected (0.01 sec)
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 0 |
| Aborted_connects | 0 |
+------------------+-------+
2 rows in set (0.01 sec)
使用不存在的账号kkk访问MySQL后,你会发现状态变量Aborted_connects变为1了。
mysql -ukkk -p12314 -h192.168.2.101 -P3006
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 0 |
| Aborted_connects | 1 |
+------------------+-------+
2 rows in set (0.01 sec)
(2)这个账号本身存在,但是只允许特定IP地址才能访问。
mysql> grant all on *.* to 'kkk'@'192.168.2.102';
192.168.2.103>mysql -ukkk -p12314 -h192.168.2.101 -P3006
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 0 |
| Aborted_connects | 1 |
+------------------+-------+
2 rows in set (0.00 sec)
(3)客户端输入的密码有误或者根本就是尝试各个密码。(暴力破解)
192.168.2.103>mysql -ukkk -p12312312 -h192.168.2.101 -P3006
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 0 |
| Aborted_connects | 2 |
+------------------+-------+
2 rows in set (0.00 sec)
状态变量Aborted_connects就会发现状态变量Aborted_connects变为2了。
2、错误的连接packet包信息
可以对MySQL的端口进行端口测试(ping 端口),因为psping的包就是不包含正确的包信息。
下载:https://learn.microsoft.com/zh-cn/sysinternals/downloads/psping
在客户端对MySQL服务所在的主机进行端口连通性验证(psping)
C:\Users\Administrator>d:
d:\>cd sss\PSTools
d:\sss\PSTools>psping.exe 127.0.0.1:3306
PsPing v2.12 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2023 Mark Russinovich
Sysinternals - www.sysinternals.com
TCP connect to 127.0.0.1:3306:
5 iterations (warmup 1) ping test:
Connecting to 127.0.0.1:3306 (warmup): from 127.0.0.1:49834: 0.15ms
Connecting to 127.0.0.1:3306: from 127.0.0.1:49835: 0.27ms
Connecting to 127.0.0.1:3306: from 127.0.0.1:49840: 0.24ms
Connecting to 127.0.0.1:3306: from 127.0.0.1:49841: 0.29ms
Sent = 3, Received = 3, Lost = 0 (0% loss),
Minimum = 0.24ms, Maximum = 0.29ms, Average = 0.27ms
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 0 |
| Aborted_connects | 3 |
+------------------+-------+
2 rows in set (0.00 sec)
每psping一次Aborted_connects就加1.
从上可以看出,很多情况下,Aborted_connects都会增加。结合host_cache,以及warning_log来进行具体分析。
1、 客户端没有权限但是尝试访问MySQL数据库。
每次都会引起COUNT_AUTHENTICATION_ERRORS增1 ,第一次会引起COUNT_NAMEINFO_PERMANENT_ERRORS也增1
2、 客户端输入的密码有误
每次都会引起COUNT_AUTHENTICATION_ERRORS增1 ,第一次会引起COUNT_NAMEINFO_PERMANENT_ERRORS也增1
set global log_warnings=2;
例如:
2018-06-20 22:44:16 18026 [Warning] IP address '192.168.xxx.xxx' could not be resolved: Name or service not known
2018-06-20 22:44:16 18026 [Warning] Access denied for user 'kkkk'@'192.168.xxx.xxx' (using password: YES)
2018-06-20 22:45:18 18026 [Warning] Access denied for user 'test'@'192.168.xxx.xxx' (using password: YES)
3、 错误的连接packet包信息
每次引起COUNT_HANDSHAKE_ERRORS增1,
每次引起SUM_CONNECT_ERRORS增1
4、 超过连接时间限制
每次引起SUM_CONNECT_ERRORS增1,
每次引起COUNT_HANDSHAKE_ERRORS增1
第一次会引起COUNT_NAMEINFO_PERMANENT_ERRORS增1
注意: 3与4不会写入错误日志,3与4的区别可以通过COUNT_NAMEINFO_PERMANENT_ERRORS的值来区别。
4)Aborted_clients 变量
1、 客户端程序在退出之前未调用mysql_close()正确关闭MySQL连接。
import mysql.connector
try:
dbcon=mysql.connector.connect(
host='127.0.0.1',
user='root' ,
passwd='xxxxxxx',
database='information_schema'
)
cursor= dbcon.cursor()
sql_tex='select count(*) from MyDB.test'
cursor.execute(sql_tex)
dtlist= cursor.fetchall()
print dtlist
except mysql.connector.Error as e:
print('operation the sql fail!{0}'.format(e))
finally:
cursor.close;
# dbcon.close;
执行一下脚本,检查状态变量Aborted_clients,然后发现状态变量Aborted_clients的值增1了。
2、 客户端休眠的时间超过了系统变量wait_timeout和interactive_timeout的值,导致连接被MySQL进程终止
将全局系统变量interactive_timeout 和wait_timeout设置为4秒
mysql> set global interactive_timeout=4;
mysql> set global wait_timeout=4;
客户端连接到MySQL数据库,不做任何操作,过来4秒后,操作就会出现错误“ERROR 2013 (HY000): Lost connection to MySQL server during query”
状态变量Aborted_clients变为1了。
mysql> show status like 'Abort%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Aborted_clients | 1 |
| Aborted_connects | 0 |
+------------------+-------+
2 rows in set (0.00 sec
还有其他一些原因(客户端异常中断或查询超出了max_allowed_packet值)由于不方便构造,在此略过。
因此可以结合max_connect_errors,host_cache表,Aborted_clients,Aborted_connects以及错误日志来综合分析,某些情况的,获取同一IP来源的错误请求过多的IP,自动添加到防火墙禁止访问。
同时也可以抓包分析,在故障时间段或者复现时。
tcpdump -i eth0 port 3306 -s 1500 -w tcpdump.log
抓包工具有以下几种:
Wireshark:免费的开源抓包工具,适用于多个操作系统,捕获网络流量进行分析处理。可以打开tcpdump包进行分析。
Fiddler:针对HTTP(S)的免费抓包工具,适用于浏览器,可记录客户端和服务器之间的HTTP(S)流量。
Tcpdump:适用于无GUI界面的服务器。
参考资料:
http://mysqlblog.fivefarmers.com/2013/08/08/understanding-max_connect_errors/
https://dev.mysql.com/doc/refman/8.4/en/performance-schema-host-cache-table.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?