预编译真的能完美防御sql注入吗?

admin 2026-05-23 05:16:36 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文探讨预编译(参数化查询)对SQL注入的防御效果,指出预编译虽能有效防止值参数位置的注入,但无法用于SQL标识符(如排序方式、表名等)。文章通过代码示例展示安全与不安全场景,强调在不可参数化位置需结合白名单机制才能完美防御注入漏洞。 综合评分: 85 文章分类: WEB安全,安全开发,漏洞分析,应用安全,技术标准


cover_image

预编译真的能完美防御sql注入吗?

原创

Relish_Chen Relish_Chen

RelishSec

2026年4月17日 14:38 福建

在小说阅读器读本章

去阅读

前言

作为网安人,对开发的了解不像开发者那样深入,所以在我的认知中,要防御sql注入漏洞的最好方式是采用预编译(参数化查询),也就是只要采用预编译了,此处就不会有sql注入漏洞,但是在近期的学习中发现在某些时候,预编译却无法防御sql注入漏洞

安全的场景

如果我们要实现输入用户名,根据用户名在数据库中查询对应的信息,那么在开发眼中,使用预编译就能很好的防御sql注入漏洞

username = input("请输入用户名: ")sql = f"SELECT * FROM student WHERE name = %s;"cursor.execute(sql, (username,))results = cursor.fetchall()for row in results:    print(row)

在这个代码中,我们不管传入什么,都会被当作一个字符串,pymysql会自动处理我们传入的内容,如果传入zs’or’1那么执行的sql语句会是:

SELECT * FROM student WHERE name = 'zs\'or\'1';

他会把我们传入的zs’or’1当作是一个字符串来处理,这样就能防止sql注入

不安全的场景

username = input("请输入用户名: ")sql = f"SELECT * FROM student WHERE name = '{username}'"cursor.execute(sql)results = cursor.fetchall()for row in results:    print(row)

这个时候他将我们传入的username直接拼接在sql语句中,如果我们传入1’or’1就会把所有数据查询出来,造成了sql注入

预编译限制

在安全的场景下,我们写了一个%s占位符,%s表示字符串,不管我们传入什么都当作是一个字符串,但如果业务需要查询的数据需要我们传入的不是字符串呢?比如在排序中,我们可以传入asc和desc,那么我们不能这样写代码,否则会报错

order_by = input("请输入排序方式: ")sql = "SELECT * FROM student ORDER BY id %s"cursor.execute(sql, (order_by,))results = cursor.fetchall()for row in results:    print(row)

这是因为预编译不能参数化排序方式,为什么呢?因为排序方式不能是字符串,你想想,你写sql语句会这样写吗?

SELECT * FROM student ORDER BY id 'desc';

所以,预编译只能防御住可参数化位置的sql注入

不可参数化的位置还有tables、columns、order by、group by、limit等

预编译能防止sql注入吗

因此,如果业务要实现用户传入类似排序方式时,不可可以采用预编译,那么,如果业务要实现该功能,可能会这样写

order_by = input("请输入排序方式: ")sql = f"SELECT * FROM student ORDER BY id {order_by}"cursor.execute(sql)results = cursor.fetchall()for row in results:    print(row)

那么这样子写就引发sql注入了,比如我传入desc,0和desc,1就能发现存在sql注入了

那么,如何防御?只需要加上白名单

order_by = input("请输入排序方式: ").lower()if order_by not in ['asc','desc']:    passelse:    sql = f"SELECT * FROM student ORDER BY id {order_by};"    cursor.execute(sql)    results = cursor.fetchall()    for row in results:        print(row)

这样就能完美防御sql注入了

总结

预编译的占位符只能用于值参数,不能用于SQL标识符,否则会被当成字符串常量,导致排序/动态查询功能失效。开发为了实现业务功能只能拼接字符串,如果没有设置白名单,攻击者就能直接突破预编译的防护


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:RelishSec RelishChen RelishChen《预编译真的能完美防御sql注入吗?》

Java安全必学习类加载机制 网络安全文章

Java安全必学习类加载机制

文章总结: 本文系统解析Java类加载机制,涵盖静态与动态加载的区别、类加载的七个阶段(重点分析加载、验证、准备、解析、初始化)及触发条件。详细阐述三类加载器(
评论:0   参与:  0