11-java安全基础——java中的sql注入
Posted songly_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11-java安全基础——java中的sql注入相关的知识,希望对你有一定的参考价值。
jdbc中的sql注入
JDBC是sun公司制定的使用java语言来操作数据库的驱动接口,程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
新建一个maven项目,导入jdbc依赖包:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
</dependencies>
JDBC提供的开发接口:
java.sql :所有与 JDBC 访问数据库相关的接口和类
javax.sql :数据库扩展包,提供数据库额外的功能。如:连接池
DriverManager 类 :管理和注册数据库驱动 ,数据库连接对象
Connection 接口 :一个连接对象,可用于创建 Statement 和 PreparedStatement 对象
Statement 接口 :一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器
PreparedStatemen 接口 :一个 SQL 语句对象,是 Statement 的子接口
ResultSet 接口 :用于封装数据库查询的结果集,返回给客户端 Java 程序
来看一个简单的JDBC示例程序,查询一个用户:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/**
* @auther songly_
* @data 2021/8/12 19:30
*/
public class JdbcTest {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_test", "root", "123456");
Scanner scanner = new Scanner(System.in);
System.out.print("输入要查询的id:");
String id = scanner.nextLine();
//3.构造sql语句
String sql = "select * from user where id = " + id;
//4.获取执行sql的对象 Statement
Statement statement = connection.createStatement();
//5.执行sql
ResultSet resultSet = statement.executeQuery(sql);
//6.输出结果
String username;
String password;
while (resultSet.next()) {
id = resultSet.getString("id");
username = resultSet.getString("username");
password = resultSet.getString("password");
System.out.println(id + " , " + username + " , " + password);
}
//7.释放数据库资源
statement.close();
connection.close();
}
}
当我们输入id为1 or 1=1时,程序会把所有的结果查询出来,从执行的sql语句来看,JDBC使用了拼接的方式构造sql语句,id的内容1 or 1=1破坏了原有的sql的语义,使得sql语句的作用发生了改变,产生了歧义性,因此产生了sql注入问题。
jdbc提供了一个PreparedStatement对象来防止sql注入,PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它使用了预编译SQL语句的方式来防止sql注入。
对之前的程序进行改写,使用preparedStatement对象来执行sql
Scanner scanner = new Scanner(System.in);
System.out.print("输入要查询的id:");
String id = scanner.nextLine();
//3.构造sql语句,使用了占位符?
String sql = "select * from user where id = ?";
//4.获取执行sql的对象 preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1 , id);
ResultSet resultSet = preparedStatement.executeQuery();
程序执行结果:
preparedStatement对象在构造sql语句时不是简单sql拼接的方式,而是使用了站位符?来构造SQL语句,prepareStatement方法首先会将sql语句发送给数据库进行预编译,然后调用了setString方法将id的值传给了preparedStatement对象。preparedStatement对象会将id的内容1 or 1=1被作为一个整体传给sql语句,没有破坏sql语句原有的语义,因此只能查询出id为1的用户。
通过查看源码发现,底层实际上对1 or 1=1这些内容使用了单引号进行了闭合,被当作要给整体赋值给id,由于id字段的数据类型是整形,只会取第一个字符1作为id的值,即id=1。
有小伙伴可能会说,既然底层使用了单引号进行了闭合,那我们也可以使用单引号再次闭合啊,我们输入1' or 1 = '1再次进行闭合:
实际上底层对单引号进行了转义,输入的内容仍然被当作一个整体赋值给id,因此使用preparedStatement对象来执行sql语句可以有效的防止sql注入。
mybatis中的sql注入
mybatis是一个用于操作数据库的持久层框架,它内部对JDBC进行了封装,屏蔽了JDBC接口底层访问细节,开发人员只需通过mybatis框架提供的xml或者注解方式就可以完成数据库的持久化操作。
mybatis框架执行sql语句支持两种传参方式:${ }和# { },“#”符号会点语句进行预编译,而${ }只是进行string 替换,动态解析SQL的时候会进行变量替换,有可能会造成sql注入问题,因此在项目中尽量使用预编译方式#{ },当只能使用${ }方式的话,需要做好参数校验防止sql注入。
以上是关于11-java安全基础——java中的sql注入的主要内容,如果未能解决你的问题,请参考以下文章