1. SQL注入漏洞的代码审计与修复

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 注入。

字符串拼接(存在注入) username admin' -- password 任意值 SELECT id, role FROM users WHERE username = 'admin' --' AND password = '' 密码条件被注释消除 认证绕过 → 登录成功 注入点:username、password 拼接位 风险:认证绕过 / 数据泄露 参数化查询(修复后) username admin' -- password 任意值 SELECT id, role FROM users WHERE username = ? AND password = ? 输入作为纯数据绑定 -- 不再被解析为注释 查询无匹配行 → 登录失败 注入失效,认证逻辑恢复

参数化查询修复

将拼接改为占位符 ?,由数据库驱动负责参数绑定,用户输入不再参与 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错误密码NoneNone

验证通过条件:参数化版本对 admin' -- 返回 None,且正常账号密码仍能返回用户行。若返回行,说明占位符未生效或仍存在其他拼接路径。

审计时优先搜索 execute(、cursor(、+、%、format( 与 SQL 关键字共现的位置;修复后应确认所有动态值都通过参数元组传入,而不是仅替换部分字段。