SQL注入攻击与PreparedStatement

这几天在学习JDBC,一开始用的是Statement,后来一位老师给我们讲课时用的是PreparedStatement,一开始只是觉得PreparedStatement方便了很多,不用反复拼接SQL语句,只需要用?占位就可以了。无聊时百度了一下这两个的区别,才发现PreparedStatement要比Statement强大很多,而且在程序中要使用PreparedStatement。

先来说说为什么要使用PreparedStatement,一个很大的原因就是PreparedStatement可以防止SQL注入攻击。

通过代码来说说什么是SQL注入攻击

连接数据库什么的代码就不贴了,这里写了一个方法通过传入的用户名和密码来查询用户的所有信息,用的是Statement

    public static void find(String user,String password) throws SQLException {
        try {
            statement = getConn().createStatement();
            rs = statement
                    .executeQuery("select * from test where name = '"+user+"'and password='"+password+"'");
            while (rs.next()){
                System.out.print(rs.getInt(1)
                        +", "+rs.getString(2)
                        +", "+rs.getString(3));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeResource(connection,statement,rs);
        }
    }

表中数据,表名为test

在主方法里调用一下这个find方法,传入用户名和密码

    public static void main(String[] args) throws SQLException {
        find("A","123");
    }

查询结果

没什么问题,现在重点来了,当我传入的是一些其他的东西呢?

find("A","' or 1 = '1");

当我给密码传入' or 1 = '1,查询结果为

可以看到所有的数据都查询出来了,这是因为传入的这个密码使SQL语句变成了

执行where 1 = ‘1’ 能查询到表中所有数据,相当于执行了select * from test,这就是一种SQL注入攻击,只要知道是什么原理,理解起来就很简单,所以我们也可以给用户名传入'or 1 = 1 #,也可以查询出所有数据,因为在MySql中#后面的是注释

程序根本不会执行# 后面的语句,所以这句话还是执行的是select * from test。

甚至我们可以传入一个';drop table table_name;这样其他人知道名字的表就能被删除。

现在我们不使用Statement,改用PreparedStatement

修改一下代码

    public static void find(String user,String password) throws SQLException {
        try {
            preparedStatementstatement = getConn()
                    .prepareStatement("select * from test where name =? and password = ?");
            preparedStatementstatement.setObject(1,user);
            preparedStatementstatement.setObject(2,password);
            rs = preparedStatementstatement.executeQuery();
            while (rs.next()){
                System.out.print(rs.getInt(1)
                        +", "+rs.getString(2)
                        +", "+rs.getString(3));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeResource(connection,preparedStatementstatement,rs);
        }
    }

当我们传入' or 1 = '1或者'or 1 = 1 #时会发现什么也查询不到了,原因在于PreparedStatement表示预编译的 SQL 语句的对象,SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。PreparedStatement使用了?作为占位符,通过预编译,SQL语句的结构就已经存储好了,无法通过传入的参数来改变这条SQL语句的结构,说白了就是像上面的SQL语句,PreparedStatement对象已经知道这条SQL语句有一个where条件并且需要一个name值和一个password值,这个结构就已经固定了,所以我们无法通过传入的参数来改变SQL语句结构。

不仅如此,使用PreparedStatement要比使用Statement的效率高,同样的原因PreparedStatement是预编译的,即使执行1000次查询,只需要改变占位符的值就可以了,不会再编译整条SQL语句,而使用Statement,每执行一条SQL语句都会编译,这样效率明显不如PreparedStatement。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值