解决SQL注入问题
- SQL注入攻击是指利用SQL漏洞越权获取数据的黑客行为
- SQL注入攻击根源是未对原始SQL中的敏感字符做特殊处理
- 解决方法:放弃Statement改用PreparedStatement处理SQL
那么什么是PreparedStatement?
PreparedStatement 预编译Statement是Statement的子接口,作为这个对象它可以对SQL进行参数化,预防SQL注入攻击,而且PreparedStatement比Statement执行效率更高
原始代码:
//存在SQL注入风险
//dno值为“ or 1=1 or 1=”时,所有筛选条件均失效
//SQL:select * from employee where pdname=" or 1=1 or 1="
String sql ="select * from employee where dname ='"+pdname+"'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){}
....
}
PreparedStatement改造以后的代码:
//利用PreparedStatement预防SQL注入风险
//当dname值为“or 1=1 or 1=”时,查询不到任何结果
//SQL:select * from employee where dname='\' or 1=1 or 1=\"
String sql ="select * from employee where dname=?";
PreparedStatement pstmt = conn.PreparedStatement(sql);
pstmt.setString(1,dname);//设置SQL参数,参数从1开始
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
....
}
String sql = "select * from employee where dname=? ";
这段代码创建了一个SQL查询语句字符串,该语句的目的是从名为“employee”的表中查询所有满足特定部门名称(dname)条件的记录。其中“?”是一个占位符,用于后面设置具体的参数值
PreparedStatement pstmt = conn.PreparedStatement(sql);
这段代码使用数据库连接对象conn创建一个预编译的SQL语句对象pstmt,并传入前面定义的SQL查询语句。预编译的语句可以提高性能并且防止SQL注入攻击
pstmt.setString(1,dname);
这段代码将变量dname的值设置为SQL查询语句中第一个占位符的值。参数的索引从1开始,这是java中处理预编译语句参数的方式
ResultSet rs=pstmt.executeQuery();
这段代码执行预编译的SQL查询语句,并返回一个结果集rs,其中包含了满足查询条件的所有记录
while(rs.next()){
......
}
这个循环遍历结果集rs中的每一行记录。每次调用rs.next()会将游标移动到下一行记录,如果有下一行记录则返回true,否则返回false。在循环体中,可以通过rs的方法获取当前行各个列的值,并进行相应的处理
在IDEA代码中利用PreparedStatement接口实现查询部门信息,主要作用提供了一种更安全,更高效的方式来执行SQL查询,帮助防止SQL注入攻击。
首先我们定义一个接口Command,创建要用于连接数据库查询操作的execute方法
package hrapp;
public interface Command {
public void execute();//执行的意思
}
在QueryCommand类中声明Command接口,并实现接口中的execute方法
package hrapp;
import java.sql.*;
import java.util.Scanner;
public class QueryCommand implements Command {
@Override
public void execute() {
System.out.println("请输入部门名称:");
Scanner in = new Scanner(System.in);
String pdname = in.next();
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//1.加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true","root","123456");
//3.创建PreparedStatement对象
String sql = "select * from employee where dname=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, pdname); //注意:参数索引从1
//结果集
rs = pstmt.executeQuery();
//4.遍历查询结果
/*rs.next()返回布尔值,代表是否存在下一条记录
* 如果有,返回true,同时结果集提取下一条记录
* 如果没有,返回false,循环就会停止
* */
while (rs.next()) {
Integer eno = rs.getInt(1);//将当前数据的第一个字段的值提取出来转化为Int类型 JDBC中字段索引从1开始,而非0
String ename = rs.getString("ename");//getString获取字符串
Float salary = rs.getFloat("salary");
String dname = rs.getString("dname");
System.out.println(dname + "-" + eno + "-" + ename + "-" + salary);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//5.关闭连接,释放资源
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null && !conn.isClosed()) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
//5.关闭连接,释放资源
}
}
}
创建HumanResourceApplication类用于调用上述实现方法
package hrapp;
import com.im.jdbc.sample.hrapp.command.Command;
import com.im.jdbc.sample.hrapp.command.PstmtQueryCommand;
import com.im.jdbc.sample.hrapp.command.QueryCommand;
import java.util.Scanner; //导入了Scanner类 用于从用户输入中读取数据
public class HumanResourceApplication {
public static void main(String[] args) {
System.out.println("1-查询部门员工");
Scanner in = new Scanner(System.in);
Integer cmd = in.nextInt();
//创建了一个Scanner对象in,用于从标准输入读取用户输入。然后,读取一个整数cmd,这个整数代表用户选择的命令
switch (cmd) {
case 1: //查询部门员工
Command command = new PstmtQueryCommand();
command.execute();
break;
}
}
}
然后我们对运行结果进行查询操作
输入要查询的部门名称
可以看到两名员工信息被正确打印出来
我们再进行测试SQL注入风险操作
回车后,我们可以发现没有任何数据被打印
作为PreparedStatement在传入参数之前,已经对原始的输入数据做了敏感字符的特殊处理,故查询不到数据。