立即物理隔离数据库连接,禁用被攻破账号权限,通过processlist或pg_stat_activity实时终止恶意会话,精准定位并修复sql拼接代码行,检查web目录与数据库异常文件。

立刻断开数据库连接,别等日志分析完
发现注入行为(比如后台报错含 MySQL syntax error、访问日志出现大量 UNION SELECT 或 SLEEP(5))时,第一反应不是查代码,而是物理隔离。攻击者可能已拿到数据库连接句柄,正在持续读取或写入数据。
- 临时修改应用配置中的数据库连接串,指向一个不存在的地址,或直接重启应用服务(避免优雅关闭,防止残留会话继续执行)
- 在数据库侧立即禁用被攻破账号:
REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'%'(MySQL);ALTER ROLE app_user NOLOGIN(PostgreSQL) - 注意:改密码无效——攻击者早已绕过认证;删账号更危险——可能触发应用 panic 报错甚至雪崩
用 PROCESSLIST 或 pg_stat_activity 反向抓活跃会话
日志没开、审计功能未启用?别翻 access.log,直接查数据库自身运行时视图。这是唯一能实时看到“谁在干坏事”的窗口。
- MySQL:查
information_schema.PROCESSLIST,重点关注Command = 'Query'且Info字段含UNION、SELECT ... FROM information_schema、INTO OUTFILE或超长SLEEP的记录 - PostgreSQL:查
pg_stat_activity,过滤state = 'active'且query ~ '(union|pg_sleep|copy.*from)'的行 - 查到可疑 PID 后,立刻终止:
KILL [ID](MySQL);SELECT pg_terminate_backend(pid)(PostgreSQL)
权限撤销必须处理角色继承和级联授权
只对用户执行 REVOKE 常常白忙——现代数据库普遍用角色管理权限,而角色权限默认不随用户撤销自动失效。
- MySQL:若账号通过角色获得权限,先查
SELECT * FROM mysql.role_edges WHERE TO_HOST = '%',再执行REVOKE r1 FROM 'app_user'@'%',最后撤角色本身 - PostgreSQL:
REVOKE默认不级联,若app_user曾把权限转授给report_user,必须加CASCADE参数,否则后门仍在 - 特别注意高危权限:MySQL 的
FILE、PROCESS、GRANT OPTION;PostgreSQL 的rolsuper,这些要优先清零
别信“修复完就安全了”,漏洞位置必须精准定位到代码行
应急止血只是开始。90% 的二次入侵,源于没真正找到拼接 SQL 的那行代码——尤其当项目用了 ORM、中间件或历史封装类时,WHERE 1=1 这种动态拼接可能藏在 DAO 层深处,甚至前端 JavaScript 里也有 sql = `SELECT * FROM t WHERE id = ${input}` 这种致命写法。
- 从攻击日志中提取原始参数(如 URL 中的
id=1%20UNION%20SELECT),回溯到对应接口的 Controller → Service → Mapper/DAO 调用链 - 重点扫描所有含字符串拼接的
executeQuery、query、find_by_sql、db.query等调用点,尤其是带where、order by、limit动态拼接的逻辑 - 确认是否用了预编译:PHP 的
mysqli_prepare、Python 的cursor.execute("SELECT * FROM t WHERE id = %s", [id])、Java 的PreparedStatement才算安全;mysql_query("SELECT * FROM t WHERE id = " . $_GET['id'])就是炸弹
真实场景里最常被忽略的一点:攻击者很可能已通过 SELECT ... INTO OUTFILE 或 PostgreSQL 的 COPY 写入 Webshell,或把敏感数据导出到外部服务器。应急响应做完权限清理和代码修复后,必须检查 Web 目录下有没有异常 PHP/JSP 文件,以及数据库中是否存在非业务表(如 tmp_xxx、log_shell)。











