内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

浅析Statement和PreparedStatement SQL注入

2018-01-12 17:56 出处:清屏网 人气: 评论(0

一、SQL注入

所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

上述语句摘自百度百科,可能对于有些人来说晦涩难懂,那么我举个最简单的例子,来体验这一过程;

假设有一个登录表单,后台数据校验sql为 select * from user where username = 'XXX' and password = 'XXX' ,XXX为传入的用户名和密码,根据sql返回的结果判断登录是否成功;(方便解释,简单处理)

这条sql是预先拼接好然后提交给数据库执行的,假设我们把 password 的内容改为 1' or '1' = '1注意里面的单引号! 如果登录名为 caojiantao ,那么最终生成的sql便是:

select * from user where username = 'caojiantao' and password = '1' or '1' = '1'

!!这样一来不用知道用户 caojiantao 的密码也能够登录成功了,更有甚者,在密码处输入 "1';drop table user;" ,直接删除了数据表,十分的危险。

这就是一个最简单sql注入的例子,输入包含sql命令的内容,欺骗服务器执行破坏数据。

二、Statement

JDBC核心接口,对象用于将SQL语句发送到数据库中。一个简单的demo演示Statement的使用;

public static void main(String[] args) {
        // 数据库配置
        String url = "jdbc:mysql://127.0.0.1/ssm";
        String name = "com.mysql.jdbc.Driver";
        String user = "root";
        String password = "Cjt00382114.";
        // 登录账号密码
        String username = "caojiantao";
        String pwd = "123";
        // 拼接sql
        String sql = "select * from user where username = '" + username + "' and password = '" + pwd + "'";
        try {
            // jdbc操作
            Class.forName(name);
            Connection connection = DriverManager.getConnection(url, user, password);
            // Statement
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()){
                System.out.println(resultSet.getString("nickname"));
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

代码很简单,判断用户名和密码是否匹配,匹配则输出用户的 nickname 昵称;

根据上面sql注入的问题,我们试着把pwd传入 1' or '1' = '1 ,前面的1可以随便填,运行程序可以发现输出了用户昵称 曹建涛 ,说明sql注入成功,那么,我们该怎么避免呢?

三、PreparedStatement

采用字符串匹配?筛选sql命令字符串?没那么麻烦,JDBC已经有现成的处理方案了,那就是 PreparedStatement

PreparedStatement 继承自 Statement ,字面可译为 声明,强调一个预,内部包含一个 预编译 的sql语句,参数采用占位符 ? 进行填充,还是看一段代码体会下;

public static void main(String[] args) {
        // 数据库配置
        String url = "jdbc:mysql://127.0.0.1/ssm";
        String name = "com.mysql.jdbc.Driver";
        String user = "root";
        String password = "Cjt00382114.";
        // 登录账号密码
        String username = "caojiantao";
        String pwd = "123";
        String preSql = "select * from user where username = ? and password = ?";
        try {
            // jdbc操作
            Class.forName(name);
            Connection connection = DriverManager.getConnection(url, user, password);
            // PreparedStatement
            PreparedStatement preparedStatement = connection.prepareStatement(preSql);
            preparedStatement.setString(1, username);
            preparedStatement.setString(2, pwd);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                System.out.println(resultSet.getString("nickname"));
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

我们可以修改pwd内容为 1' or '1' = '1 ,运行程序没有任何输出,说明 PreparedStatement 有效地避免了sql注入问题;

因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令,如此,就起到了SQL注入的作用了!

上述摘自 java中预处理PrepareStatement为什么能起到防止SQL注入的作用??!!

因为是继承关系,因而 Statement 具备的 PreparedStatement 全都有,而且 PreparedStatement 还具备独有的预处理功能,相比 StatementPreparedStatement 好处有三:

  1. 提高代码的可读性,便于维护;
  2. 提高了sql执行效率;
  3. 增强了安全性,避免sql注入;

四、花絮——myBatis的 # 和 $

# 相当于对数据 加上 双引号,$ 相当于直接显示数据。

一句话言简意赅。分条陈述:

  1. # 能够sql注入问题,$ 不行;
  2. $ 用于传入数据库对象,例如表名;
  3. 能用 # 就不要使用 $;

参考文章: mybatis中的#和$的区别

分享给小伙伴们:
本文标签: StatementPreparedStatSQL注入

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号