51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1904|回复: 0
打印 上一主题 下一主题

MySQL for Java的SQL注入测试

[复制链接]
  • TA的每日心情
    无聊
    4 天前
  • 签到天数: 530 天

    连续签到: 2 天

    [LV.9]测试副司令

    跳转到指定楼层
    1#
    发表于 2018-12-21 15:27:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    只要你学JDBC,基本上所有的人都会和你说,Statement不能防止SQL注入, PreparedStatement能够防止SQL注入. 基本上参加工作了一段时间之后还是这么认为的, 没错, 这句是没有问题的, 但到底如何进行SQL注入?怎么直观的去了解SQL注入?这还是需要花一定的时间去实验的. 前提:以下的测试都是在一种理想环境下 首先准备好数据库环境, 以下是数据库的schema:
    Sql代码
    create database java_mysql;
    use java_mysql;
    drop table if exists pstest;
    create table pstest(
        id int(10) not null primary key auto_increment,
        name varchar(32),
        age int(3)
    );

    insert into pstest (name, age) values ('Tom', 23);
    insert into pstest (name, age) values ('Tom1', 23);
    insert into pstest (name, age) values ('Tom2', 23);
    insert into pstest (name, age) values ('Tom3', 23);
    insert into pstest (name, age) values ('Tom4', 23);
    insert into pstest (name, age) values ('Tom5', 23);

    以上就是建立了pstest表, 并插入了一些测试数据.
    1. 测试Statement
    Java代码



    public class StatementTest {
        public static void main(String[] args) throws SQLException {
            Connection con = null;
            Statement stmt = null;
            // name很强大, 传入了这么多东西
            String name = "Tom';delete from pstest;select * from pstest where name='Tom";
            String sql = createSql(name);   // SQL
            System.out.println(sql);

            try {
                con = DBConn.getConnection();
                stmt = con.createStatement();

                stmt.execute(sql);
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                stmt.close();
                con.close();
            }
        }

        // 根据参数的name参数查询
        private static String createSql(String name) {
            String sql = "select id, name, age from pstest ";

            // 拼接一下SQL
            if(name != null && name.length() != 0) {
                sql += "where name ='" + name + "'";
            }
            return sql;
        }
    }


    数据库连接的URL为:
    Java代码



    "jdbc:mysql://localhost:3306/java_mysql";

    其实上面的意图很简单:
    Tom';delete from pstest;select * from pstest where name='Tom

    就是想先执行一条SQL查询语句,然后把表的数据删除。

    这只是理想环境. 实际上要想传入这么复杂的数据, 真的很难想象

    这里将URL单独拎出来是有作用的, 继续看下面

    Run一下StatementTest. 会发现报异常了:
    Java代码





    com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delete from pstest;select * from pstest where name='Tom'' at line 1

    想法很简单, 现实很残酷, 未能如所愿.

    看到这里,你也应该想到了Statement的execute(String sql)默认是只能执行一条SQL的.

    若想让execute(String sql)能够同时只能几条SQL语句, 怎么办?修改连接的URL:
    Java代码





    jdbc:mysql://localhost:3306/java_mysql?allowMultiQueries=true
    重点是allowMultiQueries=true这个参数

    再来Run一下StatementTest. OK,没有报任何的异常.打印的SQL为:
    Sql代码

    select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';


    上面的SQL到底做了什么呢?

    我们先观察一下:第一条是查询SQL, 第二条是delete, 第三条是查询SQL,其实第一条和第三条是一样的.

    为了直观的看上面SQL的执行效果, 我们再次执行下最开始的schema.sql
    这时候数据库有6条数据
    Sql代码

    mysql> select * from pstest;
    +----+------+------+
    | id | name | age  |
    +----+------+------+
    |  1 | Tom  |   23 |
    |  2 | Tom1 |   23 |
    |  3 | Tom2 |   23 |
    |  4 | Tom3 |   23 |
    |  5 | Tom4 |   23 |
    |  6 | Tom5 |   23 |
    +----+------+------+

    执行一下
    Sql代码



    select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';


    这时候可以看到MySQL客户端
    Sql代码

    mysql> select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';
        +----+------+------+
        | id | name | age  |
        +----+------+------+
        |  1 | Tom  |   23 |
        +----+------+------+
        1 row in set (0.00 sec)                 -- 执行第一条查询SQL

        Query OK, 6 rows affected (0.05 sec)    -- 执行第二条delete语句, Oh, No, 数据库全部的6条数据被删除了

        Empty set (0.00 sec)                     -- 执行第三条SQL查询, 没有查询到任何数据


    上面的注释已经写好了,就不多说了。
    这时候的确已经实现了SQL注入.

    2. 测试PreparedStatemet
    Java代码





    public class PreparedStatementTest {
        public static void main(String[] args) throws SQLException {
            Connection con = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            String sql = "select id, name, age from pstest where name = ? ";
            try {
                con = DBConn.getConnection();
                ps = con.prepareStatement(sql);
                ps.setString(1, "Tom';delete from pstest;select * from pstest where name='Tom");
                rs = ps.executeQuery();
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                rs.close();
                ps.close();
                con.close();
            }
        }
    }
    直接Run一下,OK,也没出现任何的异常,数据库中的数据也还在
    但是我们到底执行了什么样的SQL, 查看MySQL的日志.
    这里简单提下MySQL的日志,可以在my.ini下配置

    Sql代码


    [mysqld]
    log=MySQL_Log    # 在这里加上日志名称

    这时候会在MySQL/MySQL Server 5.1/Data/目录下生成MySQL_Log文件, 里面记录的就是日志了.

    好了,回到正题,看一下刚刚PreparedStatementTest执行的SQL,在MySQL_Log中查看

    Sql代码




    120719 15:54:25      23 Connect   root@localhost on java_mysql
               23 Query /* mysql-connector-java-5.1.20-SNAPSHOT ( Revision: ${bzr.revision-id} ) ...
               23 Query SHOW WARNINGS
               23 Query /* mysql-connector-java-5.1.20-SNAPSHOT ( Revision: ${bzr.revision-id} ) */SELECT @@session.auto_increment_increment
               23 Query SHOW COLLATION
               23 Query SET character_set_results = NULL
               23 Query SET autocommit=1
               23 Query select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom'
               23 Quit

    重点是最后一条SQL.

    因为数据库中的数据都还在,我们就直接执行这条SQL
    Sql代码


    select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom'
    查看MySQL的客户端
    Sql代码

    mysql> select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom';
       Empty set (0.00 sec)                     -- 就执行了这一条查询的SQL语句
    可以看到什么的字符串已经被转义了.
    让我们来看一下转义字符:
    Sql代码

    mysql> select 'Tom';delete';
    +-------------+
    | Tom';delete |
    +-------------+
    | Tom';delete |
    +-------------+
    1 row in set (0.00 sec)
    总结:其实从上面的测试中已经看出了。的确Statement是不安全的, 可以进行SQL注入, 而PreparedStatement可以防止SQL注入。就好比上面我们想在做查询的时候将pstest中的全部数据都删除掉一样. 前面已经说过这是在理想的环境下做的测试. 在真正的环境中,就想这么简单的实现SQL注入, 基本上是不可能的。而且Statemenet,让它执行execute(String sql)的时候同时执行多条SQL, 基本上不可能会去这么做的.
    其实,关于Statement的execute(String sql)语句能够同时执行多条SQL语句, 可以看MySQL自带的测试例子:
    可查看testsuite.regression包下的ResultSetRegressionTest类:
    Java代码




    public class ResultSetRegressionTest extends BaseTestCase {
        public void testBug33678() throws Exception {
            if (!versionMeetsMinimum(4, 1)) {
                return;
            }
            createTable("testBug33678", "(field1 INT)");
            // allowMultiQueries=true设置
            Connection multiConn = getConnectionWithProps("allowMultiQueries=true");
            Statement multiStmt = multiConn.createStatement();

            try {
                multiStmt.setFetchSize(Integer.MIN_VALUE);

                // 一次性执行多条SQL语句
                multiStmt
                        .execute("SELECT 1 UNION SELECT 2; INSERT INTO testBug33678 VALUES (1); UPDATE testBug33678 set field1=2; INSERT INTO testBug33678 VALUES(3); UPDATE testBug33678 set field1=2 WHERE field1=3; UPDATE testBug33678 set field1=2; SELECT 1");

        // 以下代码省略...
        }
    }



    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-25 20:57 , Processed in 0.065016 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表