遇到数据库被注入,先不要急着改代码或删表。通常先做一件事:把“注入入口、被利用的账号、实际执行的语句、数据是否被改写”四个事实确认下来,再决定是止血、清理还是回滚。搜「网站数据库被注入怎么办」的人多数卡在同一处——看到异常告警或页面报错,但不知道问题出在 Web 层、数据库层还是运维配置层,于是到处乱改,改完还是复发。
处理数据库注入的合理顺序是:先用数据库日志和进程列表确认注入是否真的发生、由哪个账号执行;再看应用日志和入口参数定位注入点;然后收紧数据库账号权限并修复参数拼接;最后用只读账号验证注入语句不再生效,并观察一段时间。如果数据已被改写,优先从备份恢复或按 binlog 回补,不要在原表上手工修补。风险边界:任何在线的权限变更和语句拦截都要先在测试库跑一遍,生产库操作前先做一次完整备份。
网站数据库被注入先定位在哪一层
注入的表现可能出现在三个不同层面,定位错了就会反复。可以按下面的对应关系先判断一次:
- Web 应用层:页面出现 SQL 报错、参数里带单引号或
union select就能改变返回结果。检查位置是应用日志里记录的请求参数,判断依据是某个参数值异常变长或含 SQL 关键字。 - 数据库层:数据库连接数异常、出现陌生账号或陌生来源 IP。检查位置是数据库的慢查询日志、通用日志和
SHOW PROCESSLIST,判断依据是出现应用代码里不存在的语句。 - 运维配置层:数据库端口对公网开放、账号使用
%通配来源、账号权限是ALL PRIVILEGES。检查位置是数据库用户表和防火墙规则,判断依据是应用服务器以外的地址能连上数据库。
三层里只要有一层成立就先记录下来,不用一次全查完。常见情况是应用层有注入点,同时数据库层账号权限过大,两件事需要分开处理。
网站数据库被注入:先看这四个位置
下面四个位置的路径和命令都写完整,可直接按环境替换后执行。MySQL/MariaDB 的日志路径以实际配置为准,用 SHOW VARIABLES LIKE '%log%'; 查到具体文件位置再打开。
1. 数据库日志
-- 确认通用查询日志和慢查询日志的实际路径
SHOW VARIABLES LIKE 'general_log%';
SHOW VARIABLES LIKE 'slow_query_log_file';
-- 临时打开通用日志用于短时间取证,取证后关闭
SET GLOBAL general_log = 'ON';
SET GLOBAL general_log = 'OFF';打开后对应日志文件通常在 /var/log/mysql/ 或 /var/lib/mysql/ 下,用 tail -n 2000 日志文件 | grep -iE "union|select|sleep\(|benchmark\(|information_schema" 筛一遍可疑语句。通用日志会明显增加磁盘写入,取证完成就关掉。
2. 当前连接与进程
SHOW FULL PROCESSLIST;
SELECT user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC;看 host 是不是应用服务器地址,看 info 里有没有拼接查询、information_schema 探测或 sleep()。发现可疑长连接可以先用 KILL 连接ID; 切断,但切断只是止血,不等于修复。
3. 账号与权限配置
SELECT user, host FROM mysql.user;
SHOW GRANTS FOR 'app_user'@'10.0.0.%';重点看是否存在来源为 % 的账号、是否存在只有运维才该有的 FILE、PROCESS、SUPER 权限被授予了应用账号。应用账号通常只需要目标库的 SELECT, INSERT, UPDATE, DELETE。

4. 面板与防火墙字段
如果用了云数据库或主机面板,检查安全组入站规则里数据库端口是否只允许应用服务器内网地址;检查面板的数据库管理页里“访问来源”“远程访问”字段是否被设成任意地址。这两处改完要在应用侧重新连一次,确认业务仍可用。
网站数据库被注入:按可能性从高到低排除
- 查询语句直接拼接参数。确认动作:在代码里搜
SELECT、UPDATE、DELETE附近的字符串拼接和f"...{变量}..."写法,对照应用日志里那条异常请求的参数值。处理:改为参数化查询(预编译占位符),这是根本处理方式。 - 数据库账号权限过大或来源过宽。确认动作:执行上面的
SHOW GRANTS和SELECT user, host FROM mysql.user;。处理:把应用账号限制到具体库和具体来源网段,收回多余权限,改完执行FLUSH PRIVILEGES;。 - 数据库端口暴露在公网。确认动作:从非应用服务器执行
nc -zv 数据库地址 3306,能连上就说明暴露。处理:安全组只放行应用服务器内网地址,数据库监听地址收到内网网卡。 - 管理后台或第三方组件存在注入点。确认动作:对照访问日志里后台路径的请求参数,看是否有未经校验的筛选、排序字段传入数据库。处理:对排序字段使用白名单映射,不接受原样拼接的列名。
- 框架或 ORM 版本存在已知问题。确认动作:记录当前框架版本号,到其官方仓库的 issue 或安全公告页核对,具体版本以官方页面当前显示为准。处理:升级到公告中给出的修复版本,升级前先在测试环境跑一遍。
网站数据库被注入:改完怎么确认真的解决了
验证要分两步:先证明注入语句不再被执行,再观察一段时间确认没有新的可疑连接。
验证命令
-- 用应用账号尝试执行一条不该被执行的语句,应被权限拒绝
SHOW GRANTS FOR 'app_user'@'10.0.0.%';
-- 观察是否还有异常来源连接
SELECT user, host, db, command, time, info
FROM information_schema.processlist
WHERE host NOT LIKE '10.0.0.%';
-- 检查日志里是否仍有可疑关键字
# tail -n 500 /var/log/mysql/general.log | grep -icE "union|sleep\(|benchmark\("再用一个带引号或 SQL 关键字的测试参数请求原注入点,页面应返回正常业务结果或参数校验错误,而不是数据库报错。若应用日志里能记录并拦截该请求,把拦截日志一并留存。
观察时长没有统一标准,建议至少覆盖一个完整业务高峰周期,比如连续观察一天以上的连接列表和日志关键字计数,期间计数不再增长、无非应用来源连接,才算相对稳定。
回滚办法
操作前先做备份,不同存储引擎的备份方式不同,常用的是 mysqldump -u root -p --single-transaction 库名 > backup_$(date +%F).sql。回滚步骤:
- 停止应用对数据库的写入,避免回滚期间继续产生新数据。
- 若只改了账号权限,用备份的授权语句恢复:从备份文件里提取
GRANT语句重新执行,再FLUSH PRIVILEGES;。 - 若改了数据,用备份文件导入到临时库,核对后再按主键回补到原表,不要直接整表覆盖。
- 若改了代码,回退到上一版本发布包并重启应用,确认连接数与业务请求恢复正常。
容易踩的坑:只把注入点参数加了过滤,却没有收紧数据库账号权限。过滤规则一旦有遗漏,攻击者仍能用同一个账号做更大范围的查询。权限收紧和参数化查询要一起做,单做一项都算没处理完。