文档章节

MySQL for Java的SQL注入测试

zchuanzhao
 zchuanzhao
发布于 2015/09/29 23:12
字数 1588
阅读 89
收藏 0
只要你学JDBC,基本上所有的人都会和你说,Statement不能防止SQL注入, PreparedStatement能够防止SQL注入. 基本上参加工作了一段时间之后还是这么认为的, 没错, 这句是没有问题的, 但到底如何进行SQL注入?怎么直观的去了解SQL注入?这还是需要花一定的时间去实验的. 前提:以下的测试都是在一种理想环境下 首先准备好数据库环境, 以下是数据库的schema:
Sql代码
  1. create database java_mysql;
  2. use java_mysql;
  3. drop table if exists pstest;
  4. create table pstest(
  5.     id int(10) not null primary key auto_increment,
  6.     name varchar(32),
  7.     age int(3)
  8. );
  9. insert into pstest (name, age) values ('Tom', 23);
  10. insert into pstest (name, age) values ('Tom1', 23);
  11. insert into pstest (name, age) values ('Tom2', 23);
  12. insert into pstest (name, age) values ('Tom3', 23);
  13. insert into pstest (name, age) values ('Tom4', 23);
  14. insert into pstest (name, age) values ('Tom5', 23);
 以上就是建立了pstest表, 并插入了一些测试数据.
1. 测试Statement
Java代码
  1. public class StatementTest {
  2.     public static void main(String[] args) throws SQLException {
  3.         Connection con = null;
  4.         Statement stmt = null;
  5.         // name很强大, 传入了这么多东西
  6.         String name = "Tom';delete from pstest;select * from pstest where name='Tom";
  7.         String sql = createSql(name);   // SQL
  8.         System.out.println(sql);
  9.         try {
  10.             con = DBConn.getConnection();
  11.             stmt = con.createStatement();
  12.             stmt.execute(sql);
  13.         } catch(Exception e) {
  14.             e.printStackTrace();
  15.         } finally {
  16.             stmt.close();
  17.             con.close();
  18.         }
  19.     }
  20.     // 根据参数的name参数查询
  21.     private static String createSql(String name) {
  22.         String sql = "select id, name, age from pstest ";
  23.         // 拼接一下SQL
  24.         if(name != null && name.length() != 0) {
  25.             sql += "where name ='" + name + "'";
  26.         }
  27.         return sql;
  28.     }
  29. }
数据库连接的URL为:
Java代码
  1. "jdbc:mysql://localhost:3306/java_mysql";
其实上面的意图很简单:
Tom';delete from pstest;select * from pstest where name='Tom
就是想先执行一条SQL查询语句,然后把表的数据删除。
这只是理想环境. 实际上要想传入这么复杂的数据, 真的很难想象
 这里将URL单独拎出来是有作用的, 继续看下面
Run一下StatementTest. 会发现报异常了:
Java代码
  1. 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代码
  1. jdbc:mysql://localhost:3306/java_mysql?allowMultiQueries=true
 重点是allowMultiQueries=true这个参数
再来Run一下StatementTest. OK,没有报任何的异常.打印的SQL为:
Sql代码
  1. 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代码
  1. mysql> select * from pstest;
  2. +----+------+------+
  3. | id | name | age  |
  4. +----+------+------+
  5. |  1 | Tom  |   23 |
  6. |  2 | Tom1 |   23 |
  7. |  3 | Tom2 |   23 |
  8. |  4 | Tom3 |   23 |
  9. |  5 | Tom4 |   23 |
  10. |  6 | Tom5 |   23 |
  11. +----+------+------+
执行一下
Sql代码
  1. select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';
 这时候可以看到MySQL客户端
Sql代码
  1. mysql> select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';
  2.     +----+------+------+
  3.     | id | name | age  |
  4.     +----+------+------+
  5.     |  1 | Tom  |   23 |
  6.     +----+------+------+
  7.     1 row in set (0.00 sec)                 -- 执行第一条查询SQL
  8.     Query OK, 6 rows affected (0.05 sec)    -- 执行第二条delete语句, Oh, No, 数据库全部的6条数据被删除了
  9.     Empty set (0.00 sec)                     -- 执行第三条SQL查询, 没有查询到任何数据
上面的注释已经写好了,就不多说了。
这时候的确已经实现了SQL注入.
2. 测试PreparedStatemet
Java代码
  1. public class PreparedStatementTest {
  2.     public static void main(String[] args) throws SQLException {
  3.         Connection con = null;
  4.         PreparedStatement ps = null;
  5.         ResultSet rs = null;
  6.         String sql = "select id, name, age from pstest where name = ? ";
  7.         try {
  8.             con = DBConn.getConnection();
  9.             ps = con.prepareStatement(sql);
  10.             ps.setString(1, "Tom';delete from pstest;select * from pstest where name='Tom");
  11.             rs = ps.executeQuery();
  12.         } catch(Exception e) {
  13.             e.printStackTrace();
  14.         } finally {
  15.             rs.close();
  16.             ps.close();
  17.             con.close();
  18.         }
  19.     }
  20. }
直接Run一下,OK,也没出现任何的异常,数据库中的数据也还在
但是我们到底执行了什么样的SQL, 查看MySQL的日志.
这里简单提下MySQL的日志,可以在my.ini下配置
Sql代码
  1. [mysqld]
  2. log=MySQL_Log    # 在这里加上日志名称
这时候会在MySQL/MySQL Server 5.1/Data/目录下生成MySQL_Log文件, 里面记录的就是日志了.
好了,回到正题,看一下刚刚PreparedStatementTest执行的SQL,在MySQL_Log中查看
Sql代码
  1. 120719 15:54:25      23 Connect   root@localhost on java_mysql
  2.            23 Query /* mysql-connector-java-5.1.20-SNAPSHOT ( Revision: ${bzr.revision-id} ) ...
  3.            23 Query SHOW WARNINGS
  4.            23 Query /* mysql-connector-java-5.1.20-SNAPSHOT ( Revision: ${bzr.revision-id} ) */SELECT @@session.auto_increment_increment
  5.            23 Query SHOW COLLATION
  6.            23 Query SET character_set_results = NULL
  7.            23 Query SET autocommit=1
  8.            23 Query select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom'
  9.            23 Quit
 重点是最后一条SQL.
因为数据库中的数据都还在,我们就直接执行这条SQL
Sql代码
  1. select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom'
查看MySQL的客户端
Sql代码
  1. mysql> select id, name, age from pstest where name = 'Tom';delete from pstest;select * from pstest where name='Tom';
  2.    Empty set (0.00 sec)                     -- 就执行了这一条查询的SQL语句
可以看到什么的字符串已经被转义了.
让我们来看一下转义字符:
Sql代码
  1. mysql> select 'Tom';delete';
  2. +-------------+
  3. | Tom';delete |
  4. +-------------+
  5. | Tom';delete |
  6. +-------------+
  7. 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代码
  1. public class ResultSetRegressionTest extends BaseTestCase {
  2.     public void testBug33678() throws Exception {
  3.         if (!versionMeetsMinimum(4, 1)) {
  4.             return;
  5.         }
  6.         createTable("testBug33678", "(field1 INT)");
  7.         // allowMultiQueries=true设置
  8.         Connection multiConn = getConnectionWithProps("allowMultiQueries=true");
  9.         Statement multiStmt = multiConn.createStatement();
  10.         try {
  11.             multiStmt.setFetchSize(Integer.MIN_VALUE);
  12.             // 一次性执行多条SQL语句
  13.             multiStmt
  14.                     .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");
  15.     // 以下代码省略...
  16.     }
  17. }

© 著作权归作者所有

zchuanzhao

zchuanzhao

粉丝 51
博文 244
码字总数 145913
作品 1
福州
程序员
私信 提问
mybaties的批量增删改查及普通增删改查

数据库: create table school( id int(11) not null auto_increment comment '主键', name varchar(20) comment '学校名称', address varchar(100) comment '学校地址', create_time datati......

成长中的菜鸟
2015/01/29
997
0
Java开发基础(二)——JDBC的使用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/simonforfuture/article/details/90409979 文章目录 前言 Java开发中...

simon曦
05/22
0
0
java使用原生jdbc连接数据库并操作

使用Java连接数据库需要使用JDBC驱动。JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的...

hasagei
01/23
722
0
Spring之jdbc Template实现CRUD操作

Spring为各种持久化技术都提供了简单操作的模板回调。比如jdbc、hibernate、Mybatis以及JPA等。 这里我们就以JDBC为例,看看JDBC template怎么实现CRUD操作。 JdbcTemplate主要提供以下几类方...

Java攻城玩家
2018/05/31
0
0
java和mysql之间的时间日期类型传递

mysql(版本:5.1.50)的时间日期类型如下: datetime 8bytes xxxx-xx-xx xx:xx:xx 1000-01-01 00:00:00到9999-12-31 23:59:59 timestamp 4bytes xxxx-xx-xx xx:xx:xx 1970-01-01 00:00:01到......

为了美好的明天
2017/10/31
59
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL 11.3 locking

rudi
41分钟前
5
0
Mybatis Plus sql注入器

一、继承AbstractMethod /** * @author beth * @data 2019-10-23 20:39 */public class DeleteAllMethod extends AbstractMethod { @Override public MappedStatement injectMap......

一个yuanbeth
今天
8
1
一次写shell脚本的经历记录——特殊字符惹的祸

本文首发于微信公众号“我的小碗汤”,扫码文末二维码即可关注,欢迎一起交流! redis在容器化的过程中,涉及到纵向扩pod实例cpu、内存以及redis实例的maxmemory值,statefulset管理的pod需要...

码农实战
今天
4
0
为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

之前在阅读《阿里巴巴Java开发手册》时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下: 那么我们首先来用例子来看看在循环体中用 + 或者用 StringBuilder 进行字符串拼接的效率...

武培轩
今天
8
0
队列-链式(c/c++实现)

队列是在线性表功能稍作修改形成的,在生活中排队是不能插队的吧,先排队先得到对待,慢来得排在最后面,这样来就形成了”先进先出“的队列。作用就是通过伟大的程序员来实现算法解决现实生活...

白客C
今天
78
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部