mysql输入验证错误漏洞

2016年夏天,入验一个做安全审计的证错朋友给我发了条消息:“你猜MySQL的密码验证逻辑能离谱到什么程度?”他当时在测试一个老版本的MySQL 5.5,发现只要构造特定的误漏用户名输入,就能绕过密码校验直接登录。入验这不是证错什么高深的溢出攻击,纯粹是误漏输入验证没做干净。

MySQL的入验输入验证问题,很多出在对特殊字符的证错处理上。比如用户名里塞进' OR '1'='1这种经典SQL注入片段,误漏如果应用层没过滤,入验MySQL自己又对某些边界情况校验不严,证错就可能出岔子。误漏更隐蔽的入验是编码问题——GBK编码下,某些多字节字符会被MySQL误解析,证错导致引号被“吃掉”,误漏后面的内容变成SQL语句的一部分。宽字节注入就是这么来的。

我翻过MySQL的CVE列表,和输入验证相关的漏洞占了相当比例。CVE-2012-2122是个典型:当使用某些认证插件时,攻击者反复尝试密码,由于比较逻辑的缺陷,大约256次尝试中就有一次能成功登录。这漏洞不需要任何特殊输入,就是暴力尝试,但根因还是验证逻辑不够严谨。

另一个让我印象深刻的是CVE-2016-6662。这个漏洞允许攻击者通过SELECT ... INTO OUTFILE写入恶意配置文件,进而以MySQL进程权限执行任意代码。问题出在MySQL对文件路径的验证上——它没检查路径里是否包含换行符或特殊字符,导致攻击者可以把配置项注入到my.cnf里。一个输入验证的疏忽,直接变成远程代码执行。

实际开发中,很多人以为用了预处理语句就万事大吉。预处理确实能解决大部分SQL注入,但它管不了所有输入验证问题。比如ORDER BY后面的字段名没法用占位符,只能拼接,这时候如果字段名来自用户输入,照样出事。还有LIMIT子句,某些驱动不支持绑定参数,也得手动校验。

MySQL 8.0之后,官方在输入验证上收紧了不少。比如默认开启secure_file_priv,限制文件读写路径;密码认证插件也换成了caching_sha2_password,比老的mysql_native_password更抗暴力破解。但老系统迁移慢,很多生产环境还在跑5.7甚至5.6,这些老版本的坑依然存在。

说个具体的。有次帮人排查一个登录绕过问题,应用用的是PHP+MySQL,代码里明明用了mysqli_real_escape_string转义,但攻击者输入admin'--还是进去了。查了半天发现,数据库连接字符集设的是gbk,而转义函数对%bf%27这种宽字节序列处理有缺陷。把字符集改成utf8mb4,问题立刻消失。这就是典型的“验证逻辑依赖环境配置”,换个字符集就破防。

防御上,别指望单一手段。预处理语句要上,但字段名、表名这些没法参数化的地方,得用白名单校验——只允许字母数字下划线,长度也卡死。数据库连接字符集统一用utf8mb4,别用GBK。MySQL版本尽量往8.0靠,老版本该升级就升级。还有,别用root跑应用,权限最小化,就算被注入了也翻不出大浪。

最后提一句,输入验证不是MySQL一家的事。应用层、驱动层、数据库层,每一层都得做自己该做的校验。指望MySQL把一切脏输入都挡在外面,不现实。反过来,应用层觉得“反正数据库会处理”,也迟早翻车。安全这事,从来都是层层设防,没有银弹。