SQL注入
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
一、SQL注入核心定义
SQL注入(SQL Injection)是Web应用领域最常见、危害极高的安全漏洞,属于代码注入类攻击手段。攻击者通过在应用可控输入点(登录框、搜索框、URL参数、Cookie、表单等)插入恶意SQL语句片段,利用程序未严格校验过滤输入的漏洞,让恶意代码被数据库当作合法SQL指令执行,最终实现非法窃取、篡改、删除数据,甚至控制数据库服务器和Web服务器的目的。
该漏洞的本质是代码与数据未分离,开发者将用户可控输入直接拼接进SQL语句,导致输入内容突破“数据”边界,变成可执行的数据库指令。
二、漏洞形成的三大必要条件
SQL注入并非随机出现,必须同时满足以下三个核心条件,缺一不可:
- 用户可控输入:前端传入的参数(GET/POST请求、HTTP头、Cookie等)可被攻击者自由修改、构造恶意内容,程序无法阻断非法输入的传入。
- 动态SQL拼接:开发者采用字符串拼接的方式构造SQL语句,直接将用户输入嵌入SQL模板,而非使用安全的参数化方式。
- 无安全校验拦截:程序未对输入内容做过滤、转义、合法性校验,数据库驱动直接解析执行拼接后的恶意SQL语句。
三、SQL注入核心原理(通俗演示)
以最常见的用户登录场景为例,直观对比正常逻辑和注入攻击的差异:
1. 正常业务逻辑
用户输入用户名:admin,密码:123456
程序拼接SQL语句:SELECT * FROM users WHERE username='admin' AND password='123456'
数据库执行合法查询,仅返回匹配的用户数据,登录流程正常。
2. 恶意注入攻击
攻击者输入用户名:admin' #,密码:任意内容
程序拼接后恶意SQL:SELECT * FROM users WHERE username='admin' #' AND password='xxx'
其中'闭合原有引号,#注释掉后续密码校验语句,数据库直接查询admin用户并放行,实现无密码登录。
四、常见SQL注入类型及攻击手法
根据注入场景、数据类型和利用方式,SQL注入可分为六大主流类型,覆盖绝大多数Web攻击场景:
1. 按数据类型分类(基础分类)
- 数值型注入:输入参数为数字格式(如ID、页码),SQL语句中无引号包裹,攻击者直接拼接AND/OR等逻辑语句篡改查询,例:?id=1 AND 1=1。
- 字符型注入:输入参数为字符串,SQL语句用单/双引号包裹,攻击者需先闭合引号,再拼接恶意代码,是最常见的注入类型。
2. 按利用方式分类(进阶分类)
- 联合查询注入:利用UNION关键字拼接自定义查询语句,直接获取数据库表名、字段名、敏感数据,适用于页面有查询结果回显的场景。
- 报错注入:构造恶意语句触发数据库报错,通过报错信息泄露数据库结构、数据内容,适用于页面无正常回显但有错误提示的场景。
- 布尔盲注:页面无任何数据回显,仅返回“真/假”两种状态,攻击者通过构造逻辑判断,逐字符猜解数据内容。
- 时间盲注:页面无任何状态反馈,攻击者通过sleep()等函数让数据库延迟执行,根据响应时间判断语句真假,逐步窃取数据。
- 堆叠注入:利用分号分隔多条SQL语句,同时执行查询、增删改、创建用户等操作,危害极大,可直接控制数据库。
五、SQL注入的实战危害
SQL注入漏洞一旦被利用,会对业务和数据造成毁灭性影响,核心危害包括:
- 敏感数据泄露:窃取用户账号密码、手机号、身份证、支付信息、商业机密等核心数据,引发隐私泄露和合规风险。
- 数据篡改删除:修改订单金额、用户权限、业务配置,甚至删除 entire 数据表,导致业务瘫痪、数据丢失。
- 服务器权限劫持:通过数据库提权、写入木马文件,获取Web服务器甚至服务器操作系统的控制权。
- 拖库与黑产利用:批量窃取全库数据,在黑产市场售卖,引发大规模账号被盗、电信诈骗等次生风险。
六、全方位SQL注入防御方案(可落地实操)
防御SQL注入的核心是彻底隔离代码与数据,以下方案按优先级排序,兼顾安全性和开发效率:
1. 优先使用参数化查询(预编译语句,黄金标准)
这是防御SQL注入最有效、最推荐的手段,通过占位符(?、$1等)替代直接拼接,数据库将用户输入当作纯数据处理,彻底杜绝注入风险。主流编程语言(Java、PHP、Python、Go)均支持PreparedStatement预编译机制,开发中严禁手动拼接SQL。
2. 严格的输入校验与过滤
对用户输入做白名单校验,仅允许合法字符通过;过滤SQL关键字(UNION、SELECT、OR、AND、sleep等)和特殊符号(单引号、分号、注释符),同时对特殊字符做转义处理。
3. 数据库权限最小化
Web应用连接数据库的账号仅分配必要权限(如仅查询、修改指定表),禁止授予DROP、ALTER、FILE等高危权限,即使出现注入漏洞,也能限制攻击范围。
4. 部署Web应用防火墙(WAF)
通过WAF实时检测拦截恶意SQL注入请求,对典型注入 payload 做规则匹配,形成前置防护屏障,弥补代码层面的防护疏漏。
5. 日志审计与漏洞扫描
开启数据库和Web应用操作日志,监控异常SQL执行行为;定期用自动化工具(SQLmap、AWVS)做漏洞扫描,结合代码审计排查动态拼接风险。
6. MyBatis框架防注入专项规范(实战重点)
MyBatis作为主流持久层框架,SQL注入风险主要源于语法使用不规范,核心是区分参数传递方式,针对性规避漏洞,具体落地规则如下:
✅ 核心原则:优先使用 #{ } 参数占位符(预编译)
#{ } 是MyBatis自带的参数化查询语法,底层会自动生成PreparedStatement预编译语句,将用户输入当作纯字符串处理,自动转义特殊字符(单引号、注释符等),从根源阻断注入,是MyBatis防注入的唯一首选方案。示例:
<select id="getUser" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
❌ 严禁滥用 ${ } 字符串拼接(高危风险)
${ } 属于直接字符串拼接,不会做任何预编译和转义,会将用户输入直接嵌入SQL语句,等同于手动拼接SQL,极易触发SQL注入漏洞。仅允许在非用户可控、固定枚举值场景使用${ },如排序字段、表名(必须做白名单校验),绝对禁止接收前端用户输入。
🔍 MyBatis #{}与${}核心差异(防注入关键)
MyBatis的SQL注入风险,完全源于#{}和${}的语法特性混用不当,二者底层执行逻辑天差地别,也是防注入的核心分界线:
- #{}:预编译参数占位符(安全)底层自动生成 PreparedStatement 预编译语句,将用户输入视为纯数据,自动转义单引号、注释符等特殊字符,从根源阻断SQL注入,是业务开发的唯一首选。
- ${}:字符串直接拼接(高危)纯文本拼接模式,无预编译、无转义,直接将用户输入嵌入SQL语句,等同于原生手动拼接SQL,用户可控参数传入必出注入漏洞,仅允许非用户可控的固定场景使用。
📌 三大高频场景:错误写法+注入SQL+安全写法对比
以下针对MyBatis最易出漏洞的三类场景,完整还原恶意注入Payload、拼接后的危险SQL,以及对应的安全修复方案:
场景1:模糊查询(LIKE)防注入规范
❌ 高危错误写法(${}字符串拼接)
直接拼接用户输入,无预编译无转义,属于致命漏洞写法,XML代码如下:
<select id="getUserByLike" resultType="User">
SELECT * FROM users WHERE username LIKE '%${username}%'
</select>
执行逻辑:用户输入直接嵌入SQL语句,输入内容可突破数据边界,变为可执行指令。
⚠️ 两种典型恶意注入SQL演示
攻击者构造Payload后,会生成非法可执行SQL,危害极大:
1. 万能查询注入(全表数据泄露)
攻击者输入Payload:%' OR 1=1 --
拼接后真实执行的恶意SQL:
SELECT * FROM users WHERE username LIKE '%%' OR 1=1 -- %'
危害:注释符屏蔽后续查询条件,1=1恒成立,数据库返回整张用户表的敏感数据。
2. 延时盲注注入(数据逐字符猜解)
攻击者输入Payload:%' AND SLEEP(5) --
拼接后真实执行的恶意SQL:
SELECT * FROM users WHERE username LIKE '%%' AND SLEEP(5) -- %'
危害:触发数据库延时响应,攻击者通过响应时长,逐步破解库表名、字段名、账号密码等核心数据。
✅ 安全正确写法(#{}预编译)
搭配CONCAT函数使用#{}占位符,底层开启预编译,自动转义特殊字符,XML代码如下:
<select id="getUserByLike" resultType="User">
SELECT * FROM users WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
最终预编译安全SQL:
SELECT * FROM users WHERE username LIKE CONCAT('%', ?, '%')
场景2:IN批量查询防注入规范
❌ 高危错误写法(${}拼接)
用${}直接拼接IN参数,极易被篡改注入,XML代码如下:
<select id="getUserByIds" resultType="User">
SELECT * FROM users WHERE id IN (${ids})
</select>
⚠️ 注入风险演示
攻击者传入恶意Payload:1) OR 1=1 --
拼接后恶意执行SQL:
SELECT * FROM users WHERE id IN (1) OR 1=1 --
危害:绕过IN范围限制,直接返回全表数据,造成批量信息泄露。
✅ 安全正确写法(#{} + foreach标签)
通过foreach循环生成#{}占位符,全程预编译防护,XML代码如下:
<select id="getUserByIds" resultType="User">
SELECT * FROM users WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
最终预编译安全SQL:
SELECT * FROM users WHERE id IN (?, ?, ?)
场景3:动态SQL(if/choose)防注入规范
❌ 高危错误写法(${}嵌套)
动态SQL内嵌套${}接收用户输入,漏洞风险极高,XML代码如下:
<select id="getUserByCondition" resultType="User">
SELECT * FROM users
<if test="username != null">
WHERE username = '${username}'
</if>
</select>
⚠️ 注入风险演示
攻击者传入恶意Payload:admin' #
拼接后恶意执行SQL:
SELECT * FROM users WHERE username = 'admin' #'
危害:单引号闭合原有语法,#注释后续密码校验逻辑,实现无密码越权登录。
✅ 安全正确写法(全程#{})
动态SQL内部统一使用#{},保证全程预编译,XML代码如下:
<select id="getUserByCondition" resultType="User">
SELECT * FROM users
<if test="username != null">
WHERE username = #{username}
</if>
</select>
最终预编译安全SQL:
SELECT * FROM users WHERE username = ?
🛡️ 额外加固措施
针对${ }必需场景,必须做白名单校验,限定合法取值范围;开启MyBatis日志监控,排查异常SQL执行记录;代码审计重点核查Mapper文件中所有${ }用法,杜绝用户可控参数传入。
七、SQL注入快速检测方法
- 手工测试:在输入点添加单引号、AND 1=1、AND 1=2等payload,观察页面响应、报错信息判断是否存在漏洞。
- 工具扫描:使用SQLmap等专业工具自动化探测注入点,识别漏洞类型并完成数据窃取测试。
- 代码审计:排查项目中所有动态SQL拼接代码,重点检查未做参数化的查询、增删改语句。
核心总结:SQL注入的根源是开发规范缺失,只要严格遵循参数化查询、严控输入校验、最小化权限三大原则,就能杜绝99%以上的SQL注入漏洞。
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
本专栏聚焦Java主流持久层框架MyBatis,从基础搭建到源码原理,系统拆解核心组件、动态SQL、结果映射与缓存机制。助力开发者从入门到精通,掌握高效数据层开发技能,适配电商、金融等复杂业务场景。
