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注入。

package com.tarena.dingdang.filter; 02 03 import java.io.IOException; 04 import java.util.Enumeration; 05 06 import javax.servlet.Filter; 07 import javax.servlet.FilterChain; 08 import javax.servlet.FilterConfig; 09 import javax.servlet.ServletException; 10 import javax.servlet.ServletRequest; 11 import javax.servlet.ServletResponse; 12 import javax.servlet.http.HttpServletRequest; 13 14 public class AntiSqlInjectionfilter implements Filter { 15 16 public void destroy() { 17 // TODO Auto-generated method stub 18 } 19 20 public void init(FilterConfig arg0) throws ServletException { 21 // TODO Auto-generated method stub 22 } 23 24 public void doFilter(ServletRequest args0, ServletResponse args1, 25 FilterChain chain) throws IOException, ServletException { 26 HttpServletRequest req=(HttpServletRequest)args0; 27 HttpServletRequest res=(HttpServletRequest)args1; 28 //获得所有请求参数名 29 Enumeration params = req.getParameterNames(); 30 String sql = ""; 31 while (params.hasMoreElements()) { 32 //得到参数名 33 String name = params.nextElement().toString(); 34 //System.out.println("name===========================" + name + "--"); 35 //得到参数对应值 36 String[] value = req.getParameterValues(name); 37 for (int i = 0; i < value.length; i++) { 38 sql = sql + value[i]; 39 } 40 } 41 //System.out.println("============================SQL"+sql); 42 //有sql关键字,跳转到error.html 43 if (sqlValidate(sql)) { 44 throw new IOException("您发送请求的参数含有非法字符"); 45 //String ip = req.getRemoteAddr(); 46 } else { 47 chain.doFilter(args0,args1); 48 } 49 } 50 51 //效验
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值