最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
功能测试全过,AI 生成的登录接口仍在 cursor.execute 中拼接 SQL
时间:2026-09-12 08:24:01 编辑:袖梨 来源:一聚教程网
一个登录接口能正确返回 200 和 401,只能说明业务结果符合预期,却无法证明数据库调用方式安全。尤其在审查 AI 生成代码时,字符串拼接 SQL 很可能藏在全绿的功能测试之后。要识别这类问题,需要把检查点推进到 cursor.execute 的调用边界。
我做了个很小的对照实验:
两份登录实现,四个功能测试全部通过。
但其中一份代码把用户名和密码直接拼进 SQL,另一份使用参数化查询。
功能测试看到的是一样的结果:
正确密码 -> 200
错误密码 -> 401
functional_total=4/4
测试很绿,SQL 却不一定安全。

问题不在登录结果,而在 SQL 是怎么来的
容易出问题的实现:
def login(db, username: str, password: str):
sql = (
"SELECT id FROM users "
f"WHERE username = '{username}' AND password = '{password}'"
)
db.execute(sql)
row = db.fetchone()
return ({"ok": True}, 200) if row else ({"ok": False}, 401)
参数化实现:
def login(db, username: str, password: str):
db.execute(
"SELECT id FROM users WHERE username = ? AND password = ?",
(username, password),
)
row = db.fetchone()
return ({"ok": True}, 200) if row else ({"ok": False}, 401)
如果测试只断言“正确密码能登录、错误密码返回 401”,两份实现都能通过。
原因是测试看的路径不同:
功能测试:
请求参数 -> 登录结果
SQL 边界:
请求参数 -> SQL 字符串或参数元组 -> cursor.execute
返回值正确,不能证明实际执行的 SQL 已经参数化。
补一条契约测试
不要只测登录响应,直接检查 cursor.execute 收到了什么。
def assert_parameterized(db, username, password):
sql = db.sql or ""
assert "?" in sql
assert db.params == (username, password)
assert username not in sql
assert password not in sql
同一组断言,对两种实现的结果不同:
unsafe_login: FAIL
params=None
sql="SELECT id FROM users WHERE username = 'alice' AND password = 'correct-password'"
safe_login: PASS
params=('alice', 'correct-password')
sql='SELECT id FROM users WHERE username = ? AND password = ?'
这时,四个功能测试仍然全绿,但契约测试已经能把不安全实现拦下来。
扫描器负责找位置,契约测试负责守边界
再用 code-audit-cli 扫两份实现:
unsafe_login: findings=1
severity=high
pattern=sql-concat
line=6
safe_login: findings=0
扫描器给出一行值得人工检查的位置:
sql = (
"SELECT id FROM users "
f"WHERE username = '{username}' AND password = '{password}'"
)
契约测试则保证修复之后,这条路径不会悄悄退回字符串拼接。
两者是不同层级的检查:
- 扫描器:在代码库里定位危险写法;
- 人工确认:判断输入能不能到执行器;
- 契约测试:锁定实际交给执行器的 SQL 和参数;
- 功能测试:验证登录行为没有回归。
完整顺序是:
扫描定位
-> 人工确认
-> 参数化修复
-> 契约测试
-> 功能回归
这段代码最值得改的一句话
不要只问:
正确密码能不能登录?
还要问:
登录时,cursor.execute 实际收到的 SQL 和参数是什么?
前者证明功能正常,后者证明 SQL 边界没有失控。
AI 生成的代码也应该这样审。模型写出的登录逻辑可能完全正确,但执行路径仍然可能把输入拼进 SQL。功能测试全绿时,这个差异不会自动暴露。
实验边界:这里用的是可控的假数据库执行器,只验证“参数化查询”这一条契约,不代表覆盖了密码哈希、频率限制、账号枚举、日志和权限等全部安全问题。
公开规则和示例报告:
github.com/yuan1521913…
需要本地扫描器完整源码版:Gumroad