JAVA新手容易忽略的SQL注入漏洞

1 登录认证的SQL注入​

再以前没有orm框架时,很多都是拼接的sql语句,有了hibernate、jpa、mybatis 等orm框架后, 开发变得更加容易,但是自定义的sql 场景仍然存在,这里我们以登录接口为例。

1.1 自定义sql漏洞代码示例​

这是一个登陆接口,使用自定义的sql, 原理是使用Statement对象执行查询操作。

// 用户登录验证(存在SQL注入)
public boolean login(String username, String password) throws Exception {
    Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    return rs.next(); // 若查询到记录则登录成功
}

1.2 正常输入逻辑​

-- 用户输入:username="admin", password="123456"
SELECT * FROM users WHERE username = 'admin' AND password = '123456';

按照预期逻辑,仅当用户名密码匹配时返回数据,一切都很完美。

1.3 非正常输入会怎么样呢?

  • 入参: username = "admin’ – "
    SQL:SELECT … WHERE username = ‘admin’ – ’ AND password = ‘…’
    结果:密码被绕过了

  • 入参:username = “’ OR ‘1’='1”
    SQL:SELECT … WHERE username = ‘’ OR ‘1’=‘1’ AND password = ‘…’
    结果:布尔逻辑注入, 返回永远为true

  • 入参:username = "’ UNION SELECT null, username, password FROM users – "
    SQL:SELECT … WHERE username = ‘’ UNION SELECT … FROM users – ‘…’
    结果:联合查询泄露敏感数据

1.4 防御方案

public boolean safeLogin(String username, String password) throws Exception {
    Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, username); // 参数1绑定username
    pstmt.setString(2, password); // 参数2绑定password
    ResultSet rs = pstmt.executeQuery();
    return rs.next();
}

使用PreparedStatement参数化查询, 预编译使SQL结构与参数分离,数据库将输入视为纯数据而非代码,从而实现非正常输入导致的sql注入。

1.2 Mybatis框架中的sql注入

错误示例:使用$符号传参

<slect id = 'login' paramType = "java.util.Map"     resultType="java.util.Map">         
SELECT * FROM t_user  where user_name = ${userName}  and password = ${password} 
</select>

使用$传参,相当于直接使用参数拼接SQL语句,若是恶意输入,未作校验处理,则就有SQL注入的风险。

正确示例:使用#传入参数

<slect id = 'login' paramType = "java.util.Map"      resultType="java.util.Map">          
SELECT *  FROM t_user  where user_name = #{userName}  and password = #{password} </select>

使用#传参, 预编译才能够真正达到将SQL语义逻辑和数据分离的效果。

2 什么是SQL注入?

根据上面的登陆场景,想必你已经对sql注入有了基础的了解,它是一种通过恶意构造输入参数,欺骗后端数据库执行非法SQL代码的攻击手段。随着互联网的发展,SQL注入攻击在过去二十年中一直是导致数据泄露的主要原因之一,因此了解并预防SQL注入对保护信息安全至关重要。

2.1 SQL 注入本质

在理解SQL注入之前,我们需要了解一下典型的Web应用程序如何与数据库进行交互。Web应用程序通常分为前端、后端和数据库三层架构。用户在前端输入信息后,后端会将这些信息传递给数据库执行相应的SQL查询或命令。数据库处理这些查询后,将结果返回给后端,然后再展示给用户。

它的本质是把用户输入的数据当作代码来执行,违背了“数据与代码分离”的原则。本质是程序未严格区分代码与数据,导致攻击者输入的恶意字符串被拼接为SQL语句的一部分,从而操控数据库行为。

  • 用户能控制输入的内容;
  • web应用把用户输入的内容带入到数据库执行;

2.2 SQL注入攻击流程

• 输入点的识别:
攻击者首先需要识别出应用程序中可能存在SQL注入漏洞的输入点。这些输入点通常是用户可以输入数据的位置,如登录表单、搜索框、URL参数等。

• 构造恶意SQL语句
攻击者在输入点注入恶意SQL代码。由于应用程序未对输入数据进行充分的验证或过滤,注入的SQL代码会直接拼接到SQL查询中。

• 执行恶意SQL代码
数据库接收到由攻击者构造的恶意SQL查询并执行。根据查询的内容,攻击者可以获取数据库中的敏感数据、修改表结构、删除数据等。

• 获取或破坏数据
执行后的SQL查询结果通常会返回给攻击者,从而获取到敏感信息或对数据库进行破坏。

2.3 典型危害场景

• 数据泄露
窃取用户隐私、支付信息等敏感数据(如通过UNION SELECT提取其他表内容)。

• 数据篡改
修改订单金额、删除业务数据(如利用UPDATE或DELETE语句)。

• 权限提升
利用数据库漏洞执行系统命令(如MySQL的xp_cmdshell)或上传WebShell。

• 拒绝服务
通过大规模注入攻击耗尽数据库资源。

2.4 防御流程

• 输入验证与过滤
在后端代码中严格验证和过滤用户输入,确保只接受合法的数据格式。

• 使用预编译语句
通过使用预编译语句(Prepared Statements)和参数化查询,避免SQL语句与用户输入直接拼接,从根本上防止SQL注入。

• 最小权限原则
限制数据库用户的权限,确保即使攻击者成功注入SQL语句,也无法对数据库造成严重破坏。

• 定期安全测试与监控
定期进行安全测试(如渗透测试)和数据库访问日志监控,及时发现和修复潜在的安全漏洞。

写在最后

本文主要讲解了SQL注入攻击的一些原理和场景案例,以及日常开发中的用法。其实系统的攻击还有很多方面,比如XML注入,OS注入等,虽然没有SQL注入攻击常见,也是我们需要注意防范的。

SQL注入防御需​​代码层​​(参数化查询+输入校验)、​​架构层​​(最小权限+错误抑制)、​​流程层​​(代码审计+渗透测试)三重联动。尤其注意:​​预编译不能解决所有问题​​(如动态表名、存储过程注入),需结合业务场景设计防护策略。建议定期使用SQLMap扫描接口,并参考OWASP Cheat Sheet更新防护方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值