xiao1star2026-01-29文章来源:SecHub网络安全社区
之前大部分对sql注入的研究都是在PHP代码中,最近想要了解java代码中的sql注入又是怎么形成呢,因此就开启来java的sql注入的探索之旅
mybatis标志
在其xml文件中出现mybatis的字样

${}与#{}的性质#{}
${}
${}仅仅为一个纯粹的 string 替换,在动态sql解析阶段将会进行变量替换${} 解析之后是什么就是什么${} 用在sql字符串拼接中,使用时需要非常谨慎。但是像一些没有直接和系统用户接触的功能如动态切换表名,库名呀就不存在注入问题了。一旦要使用在要被用户直接接触的sql中,一定要注意!
${}的使用在登录框中输入正确的账号密码
admin
1
输入之后控制台输出的数据

当输入危险字符串
admin' or '1'='1
密码随便



当登录框中输入如下
admin
1

当登录框中输入
admin' or '1'='1
密码随便

发现并没有造成闭合形成攻击
1.在order by语句中如果使用#{},例如输入
<select id="queryByParams" parameterType="MyTestDO" resultMap="MyTestMap">
SELECT * FROM users order by #{ID}
</select>
那么最后的sql语句会变为SELECT * FROM users order by 'ID'
而在sql语句中虽然select * from users order by 'ID'可以执行但是执行的结果并不是我们想要的
如下图所示,发现select * from users order by 'id'并没有按顺序排列

2.还需要注意的是,其实like语句是可以使用#{}执行的
<select id="queryUserByName" parameterType="String" resultType="com.entity.User">
SELECT * FROM users WHERE username like concat('%',#{value},'%')
</select>

JDBC使用标志
JDBC编程步骤:
可以去工具层(until)或者mapper层中查看是否出现com.mysql.jdbc.Driver的驱动名字,或者使用DriverManager.getConnection()去连接数据库
@RequestMapping("/dynamic")
public String jdbcdynamic(@RequestParam("id") String id) throws ClassNotFoundException, SQLException {
StringBuilder result = new StringBuilder();
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);//连接数据库
Statement statement = conn.createStatement();//创建一个sql对象
String sql = "select * from user where id = '" + id + "'";
ResultSet rs = statement.executeQuery(sql);//执行sql语句
while (rs.next()) {
String rsUsername = rs.getString("username");
String rsPassword = rs.getString("password");
String info = String.format("%s: %s\n", rsUsername, rsPassword);
result.append(info);
}
rs.close();
conn.close();
return result.toString();
}
分析
上述代码中使用了jdbc来进行数据库的链接,在执行的sql语句为select * from user where id = '" + id + "',通过由前端获得的id值直接拼接的形式完成了sql语句的执行,当我们在输入' or '1'='1 时就会形成select * from user where id = '' or '1'='1'从而形成闭合完成注入
@RequestMapping("/dynamic")
public String jdbcDynamic(@RequestParam("id") String id) throws ClassNotFoundException, SQLException {
StringBuilder result = new StringBuilder();
Class.forName(driver); // 加载数据库驱动
Connection conn = DriverManager.getConnection(url, user, password); // 连接数据库
// 使用预处理语句
String sql = "SELECT * FROM user WHERE id = ?"; //?为占位符
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, id); // 设置SQL语句中的参数
ResultSet rs = preparedStatement.executeQuery(); // 执行查询
while (rs.next()) {
String rsUsername = rs.getString("username");
String rsPassword = rs.getString("password");
String info = String.format("%s: %s\n", rsUsername, rsPassword);
result.append(info);
}
// 关闭资源
rs.close();
preparedStatement.close();
conn.close();
return result.toString();
}
分析
在上述代码中使用了预处理的机制,使用了prepareStatement函数对sql语句进行了处理
conn.createStatement() 方法用于创建一个 Statement 对象,该对象用于执行静态的 SQL 语句。由于它直接接受完整的 SQL 语句作为字符串,因此如果 SQL 语句中包含来自用户输入的数据,那么这些数据可能会被恶意地修改以执行未授权的 SQL 命令,这就是所谓的 SQL 注入攻击。conn.prepareStatement() 方法用于创建一个 PreparedStatement 对象,该对象也用于执行 SQL 语句,但它允许你将 SQL 语句中的某些部分(如条件值)作为参数来设置,而不是直接嵌入到 SQL 语句字符串中。这样做可以防止 SQL 注入,因为即使参数值包含恶意的 SQL 片段,数据库也会将它们视为普通的数据值而不是 SQL 代码来执行。当我们输入的id值为' or '1'='1时,会经过预处理之后,直接会整个' or '1'='1当作一个字符串去进行查询操作,从而有效的避免sql注入
Hibernate是一个基于jdbc的开源的持久化框架,是一个优秀的ORM实现,它很大程度的简化了dao层编码工作。Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
在分层结构中处于持久化层,封装对数据库的访问细节,使业务逻辑层更专注于实现业务逻辑。
使用Hibernate标志
查看其xml中的文件是否出现后Hibernate的字样

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
Configuration configuration = new Configuration();
configuration.configure(); // 确保你有正确的配置文件路径
SessionFactory sessionFactory = configuration.buildSessionFactory();
String input = request.getParameter("sqlin");//从前端页面获取参数值
String hqlString = "select * from Classes where name='" + input + "'"; // sql语句
Session session = sessionFactory.openSession();
List<Classes> classesList = session.createQuery(hqlString).list(); //执行sql语句
for (Classes clazz : classesList) {
response.getWriter().write(clazz.getName());
response.getWriter().print("<br/>");
}
session.close(); // 在循环外关闭 Session
}
分析
hqlString变量中,sql语句是直接拼接而成的,没有任何限制,完全可以通过闭合单引号来形成闭合从而造成注入public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
// 配置Hibernate
Configuration configuration = new Configuration();
configuration.configure(); // 确保你有正确的配置文件路径
SessionFactory sessionFactory = configuration.buildSessionFactory();
// 获取用户输入
String input = request.getParameter("sqlin");
// 使用参数化查询来防止HQL注入
String hqlString = "select * from Classes where name = :name";
Session session = sessionFactory.openSession();
Query query = session.createQuery(hqlString);
query.setParameter("name", input); // 使用setParameter来设置参数值
// 执行查询并获取结果
List<Classes> classesList = query.list();
// 处理查询结果
for (Classes clazz : classesList) {
response.getWriter().write(clazz.getName());
response.getWriter().print("<br/>");
}
// 关闭Session
session.close();
}
使用了命名参数:name来代替直接将input变量拼接到HQL字符串中。之后使用Query对象的setParameter方法来设置参数的实际值。这样,Hibernate就可以安全地处理这个值,而不必担心它包含恶意代码或SQL/HQL注入攻击。