sqlmapapi.py不能用于单元测试,因其依赖真实环境且违背单元测试的毫秒级、零依赖、可预测原则;应直接测试dao层参数化逻辑,用mock隔离数据库并验证sql模板与参数分离。

sqlmapapi.py 不是单元测试工具,它跑的是真实扫描任务,和单元测试目标完全冲突。想在 UnitTest 里验证 SQL 注入防护逻辑,必须剥离数据库、网络、外部命令等所有真实依赖。
为什么不能用 sqlmapapi.py 做单元测试
单元测试要求「毫秒级执行」「零外部依赖」「可重复、可预测」。sqlmapapi.py 启动的是完整 HTTP 服务,依赖 Python 环境、Sqlmap 二进制、网络端口、HTTP 轮询、结果解析——这些全是集成行为。哪怕你 mock 了 requests.post,也掩盖了真正要测的点:你的代码是否安全拼接了用户输入。
真正该测的是 DAO 层的参数化逻辑
SQL 注入防护失效,99% 发生在数据访问层(DAO)对用户输入的处理上。单元测试要直接覆盖这一层,而不是绕到外部工具去“间接验证”。
- 测
get_user_by_id方法是否用了cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)),而不是f"WHERE id = {user_id}" - 测
search_products是否把 keyword 传给了execute的参数元组,而不是字符串格式化 - 测任何包含
format、%、f-string拼接 SQL 的函数——它们应该被标记为 禁止出现,并在 CI 中用静态检查(如bandit)拦截
用 Mock 隔离数据库驱动,只验证 SQL 构造行为
你不需连接真实 MySQL,只需确认你的 DAO 方法调用了正确的 API,并传入了预期参数:
def test_get_user_safe_with_parametrized_query():
mock_cursor = MagicMock()
mock_conn = MagicMock()
mock_conn.cursor.return_value = mock_cursor
<pre class="brush:php;toolbar:false;"># 模拟用户输入
user_input = "1' OR '1'='1"
# 执行被测方法
result = get_user_by_id(mock_conn, user_input)
# 断言:SQL 字符串里没有 user_input 的原始值
mock_cursor.execute.assert_called_once()
executed_sql = mock_cursor.execute.call_args[0][0]
assert "OR" not in executed_sql # 不是拼接,是占位符
assert mock_cursor.execute.call_args[0][1] == (user_input,) # 参数单独传入
关键点:mock_cursor.execute.call_args[0][1] 是第二个参数(即参数元组),它必须包含原始输入;而第一个参数 executed_sql 必须是纯模板,不含任何用户数据。
边界输入必须覆盖,但不用 sqlmap
单元测试不模拟攻击,而是验证防御逻辑是否覆盖常见注入载荷:
-
"admin' -- "→ 应触发参数化,不报语法错误 -
"1 OR 1=1"→ 查询应返回 0 条或 1 条(取决于是否存在 ID=1 的记录),而非全表 -
"'; DROP TABLE users; -- "→ 不应执行第二条语句,也不应抛出ProgrammingError(说明未参数化)
这些输入直接喂给 DAO 方法,观察返回值或异常类型即可。不需要启动服务、发 HTTP 请求、解析 json 结果——那已经是集成测试的事了。
真正的难点不在写断言,而在于让团队接受:SQL 注入不是靠外部扫描器“发现”,而是靠代码层的参数化约定 + 单元测试强制兜底。一旦允许字符串拼接进 SQL,再强的 sqlmap 报告也只是马后炮。











