1. SQL注入漏洞的代码审计与修复
漏洞代码与注入点定位
下面是一段典型的 Python + SQLite 登录校验代码,用户输入通过字符串拼接进入 SQL 语句:
def login(username, password):
conn = sqlite3.connect("app.db")
cur = conn.cursor()
sql = "SELECT id, role FROM users WHERE username = '" + username + \
"' AND password = '" + password + "'"
cur.execute(sql)
row = cur.fetchone()
conn.close()
return row
注入点位于 username 与 password 两个拼接位置。当输入 username = admin' -- 时,拼接结果为:
SELECT id, role FROM users WHERE username = 'admin' --' AND password = ''
-- 之后的内容被当作注释,密码条件被消除,只要存在 admin 用户即可登录成功。这是最典型的认证绕过型 SQL 注入。
参数化查询修复
将拼接改为占位符 ?,由数据库驱动负责参数绑定,用户输入不再参与 SQL 语法解析:
def login_safe(username, password):
conn = sqlite3.connect("app.db")
cur = conn.cursor()
sql = "SELECT id, role FROM users WHERE username = ? AND password = ?"
cur.execute(sql, (username, password))
row = cur.fetchone()
conn.close()
return row
关键点:SQL 模板固定,? 只代表数据位置;admin' -- 会被当作完整字符串与 username 字段比较,不会截断语句。
验证注入失效
用同一组恶意输入分别调用两个函数,观察返回结果:
| 输入 username | 输入 password | 拼接版返回 | 参数化版返回 |
|---|---|---|---|
| admin' -- | 任意 | admin 行(绕过成功) | None(登录失败) |
| admin | 正确密码 | admin 行 | admin 行 |
| admin | 错误密码 | None | None |
验证通过条件:参数化版本对 admin' -- 返回 None,且正常账号密码仍能返回用户行。若返回行,说明占位符未生效或仍存在其他拼接路径。
审计时优先搜索
execute(、cursor(、+、%、format( 与 SQL 关键字共现的位置;修复后应确认所有动态值都通过参数元组传入,而不是仅替换部分字段。