1. 为什么SQL注入依然是Web安全头号威胁
SQL注入攻击已经存在超过20年,却依然是OWASP Top 10安全风险榜单的常客。去年某知名音乐平台就因SQL注入导致百万用户数据泄露,攻击者仅仅通过精心构造的用户名字段就突破了系统防线。作为Python开发者,我们必须深刻理解:任何将用户输入直接拼接到SQL语句的行为,都是在给黑客发邀请函。
我在安全审计工作中见过最典型的案例是:开发者为了快速实现搜索功能,直接使用f-string拼接SQL查询,结果被攻击者通过' OR 1=1 --这样的简单payload获取了整个数据库权限。下面这个看似无害的代码片段就是定时炸弹:
python复制# 危险!绝对不要这样写!
def search_products(keyword):
conn = sqlite3.connect('products.db')
cursor = conn.cursor()
query = f"SELECT * FROM products WHERE name LIKE '%{keyword}%'" # 直接拼接用户输入
cursor.execute(query)
return cursor.fetchall()
当用户输入' UNION SELECT username, password FROM users --时,所有用户凭证就会泄露。这就是为什么我们需要系统性地防御SQL注入。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 参数化查询:你的第一道防线
2.1 底层原理深度解析
参数化查询之所以安全,是因为它实现了SQL语句与数据的严格分离。数据库驱动程序会将参数值以"纯数据"形式传递给数据库引擎,而不是可执行的SQL代码。以Python的sqlite3模块为例:
python复制# 安全写法
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,)) # 参数单独传递
这里的问号?是占位符,实际执行时数据库引擎会:
- 先编译不带参数的SQL语句模板
- 将参数值进行适当的转义处理
- 最后将转义后的值插入到编译好的模板中
关键点:参数化查询不是简单的字符串替换,而是通过预编译机制确保参数值永远不会被解释为SQL语法
2.2 不同数据库的参数化实践
不同数据库的占位符语法略有差异:
| 数据库类型 | 占位符样式 | Python驱动示例 |
|---|---|---|
| SQLite | ? | sqlite3 |
| MySQL | %s | PyMySQL |
| PostgreSQL | %s | psycopg2 |
| Oracle | :name | cx_Oracle |
跨数据库兼容性写法建议:
python复制# 使用命名占位符(兼容性更好)
query = "SELECT * FROM users WHERE id = %(id)s"
cursor.execute(query, {'id': u
