为何JDBC中的prepareStatement可以防止SQL注入

    技术2022-07-10  131

    首先介绍一下SQL注入

    SQL注入可以理解为通过添加恶意字段使得程序传入的SQL语句的语义发生改变,例如在登录账号并输入用户名之后添加注释符,使得后续的SQL语句失效,无需验证密码就可以进入系统;又或者在查询数据时添加or '1'='1'语句,可以获取数据库中的所有信息。

    1、那么为何会出现这种情况呢?

    这种情况主要是因为查找功能采用的是字符串拼接方法,使得前台传入的参数直接拼接到SQL语句中,然后通过statement直接执行该SQL语句,即使存在恶意字段,它也无法检测出来。

    //字符串拼接方法 public int selectByName(String name) throws SQLException { String sql = "SELECT * FROM test WHERE `name`='" + name + "'"; System.out.println(sql); Statement statement = connection.createStatement(); statement.executeQuery(sql); ResultSet resultSet = statement.getResultSet(); int count=0; while (resultSet.next()){ count++; } statement.close(); return count; //返回结果个数 }
    2、怎样解决?

    通过使用prepareStatement语句实现参数化查询,因为prepareStatement采用预编译机制,在创建prepareStatement对象时即导入了SQL语句进行预编译,此时SQL语句的参数用?代替;然后调用类似setString的方法传入参数,导致特殊字符被转义,从而使恶意字段失效。(转义的过程发生在MySQL驱动中)

    //参数化查询 public int selectByName2(String name) throws SQLException { String sql = "SELECT * FROM test WHERE `name`=?"; PreparedStatement statement = connection.prepareStatement(sql); statement.setString(1,name); System.out.println(statement); statement.executeQuery(); ResultSet resultSet = statement.getResultSet(); int count=0; while (resultSet.next()){ count++; } statement.close(); return count; //返回结果个数 }
    3、假设数据库中存在以下信息

    4、测试代码如下
    @Test public void injectTest() throws SQLException { System.out.println(selectByName("张三")); System.out.println(selectByName("张三' or '1'='1")); System.out.println(selectByName2("张三' or '1'='1")); }

    (前两条语句均使用的是字符串拼接方法,第三条语句采用参数化查询)

    5、执行结果如下

    简单描述一下从字符串拼接到参数化查询:

    即把恶意字段 B 作为字符串添加到参数中,而不是作为SQL语句中的独立字段。

    Processed: 0.008, SQL: 9