前言
我们要先明确,java是强类型语言,实战我想我们也遇到不少尝试sql注入但是遇到java强制类型要求,参数只能是integer类型的java报错,这种是不存在sql注入的,所以只有变量是String类型的时候,才会存在sql注入。
现在就来看看常规的可以执行sql语句的程序:
JDBC
具体的介绍就不说了,直接进入正题吧!
1、jdbc的java.sql.Statement
Statement是Java JDBC下执行SQL语句的一种原生方式,执行语句时需要通过拼接来执行。若拼接的语句没有经过过滤,将出现SQL注入漏洞
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/security";
String username = "root";
String password = "root";
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
connection = DriverManager.getConnection(url, username, password);
// 创建 Statement 对象
statement = connection.createStatement();
// 执行 SQL 查询
String sql = "select * from users where id=1"; //这里是String
resultSet = statement.executeQuery(sql);
// 处理查询结果
while (resultSet.next()) {
// 假设表中有 id 和 name 两列
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String pass = resultSet.getString("password");
System.out.println("ID: " + id + ", Name: " + name + ", Password: "+pass);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
try {
if (resultSet != null) resultSet.close();
if (statement != null) statement.close();
if (connection != null) connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
String sql = “select * from users where id=1”;,此处没有进行任何过滤,就会存在sql注入漏洞,此处只是形式,真实情况下,id值是我们可控的!看看预编译的情况!
2、jdbc的PreparedStatement
PreparedStatement使用了预编译的方式,是statement的升级版,更加安全、效率也更高。能有效防止sql注入
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/security";
String username = "root";
String password = "root";
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取数据库连接
connection = DriverManager.getConnection(url, username, password);
// 创建预编译的 SQL 语句
String sql = "SELECT * FROM users WHERE id = ?";
preparedStatement = connection.prepareStatement(sql);
// 设置参数
preparedStatement.setInt(1, 1);
//如果是int型则用setInt方法,如果是string型则用setString方法
// 执行查询
resultSet = preparedStatement.executeQuery();
// 处理查询结果
while (resultSet.next()) {
// 假设表中有 id 和 name 两列
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String pass = resultSet.getString("password");
System.out.println("ID: " + id + ", Name: " + name + ", Password: "+pass);
}
System.out.println(preparedStatement);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
try {
if (resultSet != null) resultSet.close();
if (preparedStatement != null) preparedStatement.close();
if (connection != null) connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用占位符?,并且进行预编译,锁定了语法树和查询结构。找到无法跳出user表的束缚,导致了无法进行sql注入!
ORM简介
ORM 是一种编程技术,用于在面向对象的编程语言(如 Java、Python)和关系型数据库(如 MySQL、PostgreSQL)之间进行数据映射。它的主要目的是让开发者使用面向对象的方式操作数据库,而不是直接写 SQL。也就是说它不是直接与数据库尽心交互,而是通过操作对象间接操作数据库。
下面的Mybatis、Hibernate就具有ORM框架的属性。
Mybatis下的sql注入
MyBatis 是对 JDBC 的进一步封装与优化,它属于 半自动化的 ORM 框架,相比 Hibernate 灵活性更强,保留了 SQL 的可控性与性能优势。
工作原理简述:
- 开发者在 Mapper 文件(XML)中提前定义 SQL 语句。
- 在 Java 正文中,只需调用 Mapper 接口方法,框架会自动完成 SQL 执行。
- MyBatis 本质上是对 JDBC 的封装与升级版,简化了数据库操作流程。
直接步入mybtis的sql查询,根据上面的原理简述,我们的sql语句在Mapper的xml文档中,所以下面的代码也是Mapper文件中的。
安全用法(#{}):
<select id="getUserById" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
生成的 SQL:
SELECT * FROM users WHERE id = ?
参数通过 JDBC 绑定,无法注入。
危险用法(${})
<select id="getUsersBySort" resultType="User">
SELECT * FROM users ORDER BY ${sortColumn}
</select>
如果 sortColumn 是用户输入,传入:
sortColumn=id desc; DROP TABLE users —
最终拼接为:
SELECT * FROM users ORDER BY id desc; DROP TABLE users —
造成严重 SQL 注入漏洞,其利用形同最简单的sql注入
下面我们来看看常见的肯能存在漏洞的几种情况
基础漏洞代码
预编译语法错误导致sql注入,预编译虽然能够防止sql注入,但是如果程序员没有养成良好的习惯,也会存在sql注入的风险
String sql = "SELECT * FROM users WHERE id = ?";
String username1="null";
sql = sql + " and username like '%" + username1 + "%'";
如果我的username1传参为user%’ or ‘1’=’1’# ,此时语句就变成了
SELECT * FROM users WHERE id = 1 and username like ‘%user%’ or ‘1’=’1’#%’
返回结果也会是全部信息
![图片[1]-【代码审计】SQL注入审计总结 - HackTwoHub社区-HackTwoHub社区](https://oss.hacktwohub.com/wp-content/uploads/2026/03/20260329211843889..png?x-oss-process=style/Image-webpszip)
下面就看一看需要用到${}的情况
Order by 注入
还有就是order by注入(order by 字段名)不能使用预编译的写法,因为order by语句后面必须要跟字段名(username、id这种)或者字段列数(1、2这种),字段列数肯定是可以预编译的,因为它是Integer类型,但是如果要采用字段名进行排序,就会出现问题,因为setString(1,username)后会自动给usernname套一个引号,而引号会让数据库认为这是一个字符串,而不是字段名,导致order by语句失效,字段列数就不会导致无法使用预编译,如下就是使用预编译的情况下的order by情况:
![图片[2]-【代码审计】SQL注入审计总结 - HackTwoHub社区-HackTwoHub社区](https://oss.hacktwohub.com/wp-content/uploads/2026/03/20260329211843956..png?x-oss-process=style/Image-webpszip)














暂无评论内容