SQL注入漏洞本质是未使用参数化查询而直接拼接用户输入,静态分析工具通过识别危险模式(如字符串拼接+数据库执行函数)和污点传播路径来检测,需结合上下文、数据流追踪与人工验证。

SQL注入漏洞在代码里长什么样
静态分析工具不是靠猜,而是靠识别危险模式。最常见的就是把用户输入直接拼进sql字符串里,比如 Python 里用 format()、% 或 + 拼接,Java 里用 String.concat() 或 StringBuilder.append() 接入 request.getParameter() 的结果。
真正触发漏洞的往往不是“写了 SQL”,而是“没走参数化”。工具会盯住这些组合:executeQuery() / executeUpdate() 前的字符串变量、cursor.execute() 的第一个参数、db.query() 中含 {} 或 $ 占位符但没绑定值的地方。
- 别只查
"SELECT * FROM user WHERE id = " + id这种明显写法——更常见的是从配置读 SQL 模板,再用反射或 Map 替换占位符,这种链式调用容易漏检 - ORM 调用也未必安全:
User.objects.raw("SELECT * FROM user WHERE name = '" + name + "'")就是典型绕过 ORM 保护的写法 - JavaScript 服务端(如 Node.js)里
pg.query("SELECT * FROM t WHERE x = '" + req.query.x + "'")同样会被标记,但工具可能误报模板字面量(`SELECT * FROM t WHERE x = ${x}`),需确认是否真用了pg.query()而非pg.query('SELECT...', [x])
选静态分析工具时看这三点
不是所有 SAST 工具都适合查 SQL 注入。重点不是“能不能扫”,而是“能不能分清上下文”——比如同一个 concat(),在日志拼接里无害,在 SQL 构造里就是高危。
Bandit(Python)、FindSecBugs(Java)、ESLint 配 eslint-plugin-security(JS)是较轻量且可落地的选择。商业工具如 Checkmarx 或 Fortify 能做跨文件数据流追踪,但配置成本高,小团队容易卡在“报太多误报不敢信”。
- 必须开启“污点分析(taint analysis)”模式,否则只能匹配字面量,漏掉变量中转场景
- 检查工具是否支持你项目里的数据库驱动:比如
psycopg2的execute()和execute_batch()参数规则不同,老版本Bandit可能只认前者 - 别跳过
node_modules外的依赖扫描——有些漏洞藏在封装了 DB 调用的私有 npm 包里,工具默认不进这些目录
误报和漏报最常发生在哪几个环节
静态分析本质是“基于规则的推理”,它不知道你那段 SQL 实际有没有走数据库,也不知道那个 user_input 其实刚被 re.sub(r'[^a-zA-Z0-9]', '', x) 清洗过。所以真假阳性都集中在边界逻辑上。
漏报多出现在动态 SQL 组装场景:比如用 getattr(model, field_name) 拼表名、用 dict.keys() 生成 WHERE 条件、或通过配置文件加载 SQL 片段再替换——这些路径工具很难完整建模。
- 误报典型:工具把
logging.info("SQL: " + sql)当成执行点,其实只是打日志;或者把测试用的硬编码 SQL("SELECT 1")也标为风险 - 漏报高发:SQL 字符串被拆成多行 + 多变量拼接(尤其带三元表达式)、使用宏或代码生成器(如 MyBatis
<if>标签嵌套)、或 SQL 构造逻辑分散在多个函数中未显式传递 - 一个简单验证法:对工具报出的每个疑似点,手动加断点跑一遍,看最终传给驱动的
sql字符串里是否真含未过滤的外部输入
上线前必须补的手动检查项
工具扫完只是起点。真正卡住 SQL 注入的最后一道关,是你自己得确认每条“可疑路径”到底走没走通。
重点关注三个位置:接口入参解析后立刻用于 SQL 的地方、通用 DAO 方法里接受 Map<String, Object> 或 Object... 的方法、以及任何带 dynamic、build、generate 字样的函数名。
- 翻一遍项目里所有
.execute、.query、.find等调用,逐个倒查参数来源——别信注释,看实际赋值语句 - 检查所有
PreparedStatement(Java)或cursor.execute(sql, params)(Python)是否真的用了参数占位符,而不是把参数拼进sql字符串再传进去 - 特别留意日志、监控、权限校验等“非主业务”模块——它们常被当成低风险区域,反而藏着直连数据库的 SQL 构造逻辑
复杂点从来不在工具会不会报,而在于你愿不愿意花十分钟,对着报出来的每一行,顺藤摸到最上游的输入源。那根藤,往往就藏在某个没写注释的 util 函数里。










