引言
在上一篇最末,我展示了sql注入现象,接下来我们来探究sql注入的本质原理
Sql注入及解决方法
我们打个断点,debug调试一下(不清楚代码的可以看上一篇)
我们可以看到“泊进之介”和“or ‘1’=‘1’”被拼接在一起作为s2对象
而最后的sql查询语句就变成了 “select * from KamenRider where ridername=‘Driver’ and username=‘泊进之介’ or ‘1’ =‘1’”,而因为满足了‘1’=‘1’,所以获得了查询结果。
用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。所以对于登录这种操作,如果利用sql注入可以直接登录而无需正确的用户名和密码。
解决方法:需要使用PreparedStatement类解决SQL注入。
我们写的SQL语句让数据库执行,数据库不是直接执行SQL语句字符串。和Java一样,数据库需要执行编译后的SQL语句(类似Java编译后的字节码文件)。
prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement能够使用预编译后的语句,多次传入不同的参数给PreparedStatement对象并执行。这就相当于相当于Java中多次调用相同的方法传入不同的参数,达到一个复用的效果。
接下来我们使用PreparedStatement来改造我们的代码
public static void main(String[] args) throws Exception {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接对象并连接数据库
String url = "jdbc:mysql:///demo";
String username = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, username, password);
System.out.println("请输入骑士名");
Scanner scanner = new Scanner(System.in);
String s1 = scanner.nextLine();
System.out.println("请输入使用者名");
Scanner scanner1 = new Scanner(System.in);
String s2 = scanner.nextLine();
//预编译
String sql = "select * from KamenRider WHERE ridername=? AND username=?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//传入参数
preparedStatement.setString(1,s1);
preparedStatement.setString(2,s2);
//处理执行结果
ResultSet resultSet =preparedStatement.executeQuery();
if (resultSet.next()){
System.out.println("变身成功");
}else{
System.out.println("变身失败");
}
//释放资源
preparedStatement.close();
}
可以看到在传入数据之前,我们先用prepareStatement方法先预编译了sql语句并返回一个PrepareStatement对象,其中在sql语句中待传入数据的位置我们使用?来代替。而传入参数时第一个int常量表示要传入的变量所在的?是第几个。
我们来测试下结果
我们可以看到在数据库中存在的宝生永梦变身成功了,但是数据库中不存在的泊进之介,即使利用了sql注入也没有变身成功,这就是我们想要达成的效果。(数据库的数据也请翻看上一篇)