网站数据库被注入怎么办

文章导读
遇到数据库被注入,先不要急着改代码或删表。通常先做一件事:把“注入入口、被利用的账号、实际执行的语句、数据是否被改写”四个事实确认下来,再决定是止血、清理还是回滚。搜「网站数据库被注入怎么办」的人多数卡在同一处——看到异常告警或页面报错,但不知道问题出在 Web 层、数据库层还是运维配置层,于是到处乱改,改完还是复发。
📋 目录
  1. Ⅰ 网站数据库被注入先定位在哪一层
  2. Ⅱ 网站数据库被注入:先看这四个位置
  3. Ⅲ 网站数据库被注入:按可能性从高到低排除
  4. Ⅳ 网站数据库被注入:改完怎么确认真的解决了
A A

遇到数据库被注入,先不要急着改代码或删表。通常先做一件事:把“注入入口、被利用的账号、实际执行的语句、数据是否被改写”四个事实确认下来,再决定是止血、清理还是回滚。搜「网站数据库被注入怎么办」的人多数卡在同一处——看到异常告警或页面报错,但不知道问题出在 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. 面板与防火墙字段

如果用了云数据库或主机面板,检查安全组入站规则里数据库端口是否只允许应用服务器内网地址;检查面板的数据库管理页里“访问来源”“远程访问”字段是否被设成任意地址。这两处改完要在应用侧重新连一次,确认业务仍可用。

网站数据库被注入:按可能性从高到低排除

  1. 查询语句直接拼接参数。确认动作:在代码里搜 SELECT、UPDATE、DELETE 附近的字符串拼接和 f"...{变量}..." 写法,对照应用日志里那条异常请求的参数值。处理:改为参数化查询(预编译占位符),这是根本处理方式。
  2. 数据库账号权限过大或来源过宽。确认动作:执行上面的 SHOW GRANTS 和 SELECT user, host FROM mysql.user;。处理:把应用账号限制到具体库和具体来源网段,收回多余权限,改完执行 FLUSH PRIVILEGES;。
  3. 数据库端口暴露在公网。确认动作:从非应用服务器执行 nc -zv 数据库地址 3306,能连上就说明暴露。处理:安全组只放行应用服务器内网地址,数据库监听地址收到内网网卡。
  4. 管理后台或第三方组件存在注入点。确认动作:对照访问日志里后台路径的请求参数,看是否有未经校验的筛选、排序字段传入数据库。处理:对排序字段使用白名单映射,不接受原样拼接的列名。
  5. 框架或 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。回滚步骤:

  1. 停止应用对数据库的写入,避免回滚期间继续产生新数据。
  2. 若只改了账号权限,用备份的授权语句恢复:从备份文件里提取 GRANT 语句重新执行,再 FLUSH PRIVILEGES;。
  3. 若改了数据,用备份文件导入到临时库,核对后再按主键回补到原表,不要直接整表覆盖。
  4. 若改了代码,回退到上一版本发布包并重启应用,确认连接数与业务请求恢复正常。

容易踩的坑:只把注入点参数加了过滤,却没有收紧数据库账号权限。过滤规则一旦有遗漏,攻击者仍能用同一个账号做更大范围的查询。权限收紧和参数化查询要一起做,单做一项都算没处理完。