热补丁需在SQL执行前截获输入并校验结构,而非仅字符串过滤;须挂钩驱动层、用SQL解析器分析AST、锚定正则白名单、覆盖所有调用路径且兼容合法注释。

热补丁不是加个装饰,是得让SQL语句在执行前就“卡住”
Legacy系统往往用硬编码拼接SQL,改源码风险高、测试周期长,根本等不到完整重构。热补丁的关键不是“修逻辑”,而是“截输入”——在SQL执行链路最上游插入校验点,把恶意字符串挡在cursor.execute之前。这要求补丁必须能挂钩到数据库驱动层或ORM底层执行入口,比如Python的sqlite3.connect返回的Connection对象,或Django的django.db.backends.postgresql.base.DatabaseWrapper类。
常见错误是直接改业务函数,结果漏掉定时任务、管理命令、异步任务里的SQL调用路径。必须确认所有数据库访问都经过同一套连接池或驱动封装,否则补丁形同虚设。
正则白名单兜底必须带锚定,且只放行ASCII字母数字
很多团队写re.match(r"[a-zA-Z0-9]+", short_code),但re.match默认不检查结尾,"abc123--"也能过。必须强制$锚定:re.fullmatch(r"[a-zA-Z0-9]{4,8}", short_code)。
- 长度限制要匹配业务实际:短码通常6位,但自定义短码可能允许4–12位,不能一刀切写死
{6} - 绝对禁止包含
_、-、.等符号,除非你明确知道它们在ORDER BY或LIMIT中不会被解析为标识符或关键字 - 对URL字段做协议校验时,不能只
.startswith("http"),得用urllib.parse.urlparse解析后检查scheme是否为http或https,并拒绝scheme为空或含@、\的netloc
热补丁必须绕过WAF,直接作用于SQL语法树
WAF拦不住UNION/**/SELECT这类变形,因为它是字符串匹配。热补丁如果只做字符串过滤,同样失效。真正有效的做法是:在SQL执行前,用轻量级SQL解析器(如sqlglot或sqlparse)做一次结构分析,检查AST里是否存在Union、Subquery、Function(如SLEEP、LOAD_FILE)节点。
示例判断逻辑:
import sqlglot
try:
parsed = sqlglot.parse_one(sql, read="mysql")
if any(isinstance(node, (sqlglot.exp.Union, sqlglot.exp.Subquery)) for node in parsed.walk()):
raise SecurityError("Suspicious SQL structure detected")
except Exception:
raise SecurityError("Invalid SQL syntax")注意:sqlglot不执行SQL,只解析语法;它比sqlparse更准,且支持MySQL/PostgreSQL多方言。别用正则去“匹配UNION”,那是给自己挖坑。
补丁上线前必须验证三类逃逸路径
Legacy系统里,SQL注入入口远不止HTTP参数。热补丁上线前,务必手动触发以下三类调用,确认拦截生效:
- 通过管理后台的“手动执行SQL”功能提交
SELECT * FROM users WHERE id = 1 OR 1=1 - 从Celery任务中调用
cursor.execute("UPDATE t_short_link SET clicks = clicks + 1 WHERE short_code = %s", [user_input]),传入含' OR '1'='1的user_input - 在日志分析脚本里用原始
psycopg2.connect直连数据库,执行含注释的SELECT 1; -- comment,看是否被误杀(合法注释应放行)
最容易被忽略的是第三点:热补丁如果粗暴拦截所有含--或/*的SQL,会直接搞崩DBA的日常运维脚本。防御必须精准,不能靠“宁可错杀”。

















