只要你学JDBC,基本上所有的人都会和你说,Statement不能防止SQL注入, PreparedStatement能够防止SQL注入.
基本上参加工作了一段时间之后还是这么认为的, 没错, 这句是没有问题的, 但到底如何进行SQL注入?怎么直观的去了解SQL注入?这还是需要花一定的时间去实验的.
前提:以下的测试都是在一种理想环境下
首先准备好数据库环境, 以下是数据库的schema:
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
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为:
"jdbc:mysql://localhost:3306/java_mysql";
其实上面的意图很简单:
Tom';delete from pstest;select * from pstest where name='Tom
就是想先执行一条SQL查询语句,然后把表的数据删除。
这只是理想环境. 实际上要想传入这么复杂的数据, 真的很难想象
这里将URL单独拎出来是有作用的, 继续看下面
Run一下StatementTest. 会发现报异常了:
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:
jdbc:mysql://localhost:3306/java_mysql?allowMultiQueries=true
重点是allowMultiQueries=true这个参数
再来Run一下StatementTest. OK,没有报任何的异常.打印的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条数据
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 |
+----+------+------+
执行一下
select id, name, age from pstest where name ='Tom';delete from pstest;select * from pstest where name='Tom';
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
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下配置
[mysqld]
log=MySQL_Log # 在这里加上日志名称
这时候会在MySQL/MySQL Server 5.1/Data/目录下生成MySQL_Log文件, 里面记录的就是日志了.
好了,回到正题,看一下刚刚PreparedStatementTest执行的SQL,在MySQL_Log中查看
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
select id, name, age from pstest where name = 'Tom\';delete from pstest;select * from pstest where name=\'Tom'
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语句
可以看到什么的字符串已经被转义了.
让我们来看一下转义字符:
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类:
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");
// 以下代码省略...
}
}
分享到:
相关推荐
Java做的一个SQL注入实验,开发工具mysql数据库,创建表users,字段id,name,paswd;工具myeclipse+tomcat;是一个简单的程序
第2章 SQL注入测试 21 2.1 概述 22 2.2 寻找SQL注入 22 2.2.1 借助推理进行测试 22 2.2.2 数据库错误 29 2.2.3 应用响应 38 2.2.4 SQL盲注 42 2.3 确认SQL注入 45 2.3.1 区分数字和字符串 46 2.3.2 内联SQL注入 46 ...
第2章 SQL注入测试 2.1 概述 2.2 寻找SQL注入 2.2.1 借助推理进行测试 2.2.2 数据库错误 2.2.3 应用响应 2.2.4 SQL盲注 2.3 确认SQL注入 2.3.1 区分数字和字符串 2.3.2 内联SQL注入 ...
SQL注入解析 解决方案 sql injection php+mysql的sql注入。做个好的程序员。写出健壮的程序。可靠的程序。
利用JDBC工具类的方式实现mysql数据库的连接并且完成登录相关功能,并且通过PreparedStatement类实现防sql注入
解决sql注入sql预编译在服务器启动时,mysql client把sql语句模板(变量采用占位符)发给mysql服务器,mysql服务器对sql语句模板进行编
SQL注入经典教程(珍藏版)
本工具是用来进行SQL注入猜解的工具,主要针对一些没有错误返回信息的网站的表结构猜解(当然,如果有错误返回这个工具也适用,只是没必要使用这个工具了,因为有更好更快捷的工具),尤其是Java+Oracle,PHP+MySQL...
apache+mysql+php,Tomcat+mysql+java(部分关卡需要) Sqli-labs安装 将之前下载的源码解压到web目录下,linux的apache为 /var/www/html下,windows下的wamp解压在www目录下。 修改sql-connections/db-creds.inc...
Mabits的动态SQL还允许开发者使用参数化查询,可以防止SQL注入攻击,并增强程序的安全性 学习经常用的几个标签 2.标签 标签是Mabits动态SQL中最常用的一个标签之一,它的作用是根据给定条件包含或排除不同的部分,以...
Sqli-lib是一款由PHP语言编写SQL注入漏洞闯关游戏,包含了六十几个可注入页面,涵盖了几乎所有类型SQL注入漏洞,同时不同的关卡自定义了不同的过滤规则,是SQL注入漏洞学习的首选靶场。图4-1 本地搭建Sqli-lib环境...
java连接mysql,和mysql语句注入检查,简单程序,需要自己提供数据库
10个SQL注入工具 BSQL Hacker是由Portcullis实验室开发的,BSQL Hacker 是一个SQL自动注入工具(支持SQL盲注),其设计的目的是希望能对任何的数据库进行SQL溢出注入。 BSQL Hacker的适用群体是那些对注入有经验的...
自定义注解实现拦截sql,并在sql中增加相应的条件 。 对应博客: https://blog.csdn.net/qq_37716298/article/details/120659139
安全,高新能的PHP mysql-mysqli数据库驱动类,支持主从,分库分表,sql注入防御等,Hi-mydb
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器...
JDBC(powernode CD2206)...7.6 使用PreparedStatement改进代码,解决SQL注入问题 八、编写JDBC工具类 九、CRUD操作 十、事务操作 十一、批处理 11.1 jdbc.properties 11.2 BatchDemo.java 11.3 没有进行批处理的耗时
因此,本模块我们开始学习编写Java应用程序访问数据库的方法技术,内容包含对数据库中数据记录的查询、添加、修改与删除等操作,以及使用预编译SQL语句防止从应用程序外部进行SQL注入。 《Java程序设计案例教程》...
sec-interview 以下为信息安全各个方向涉及的...windows+mysql,存在sql注入,但是机器无外网权限,可以利用吗? (★) 常用的信息收集手段有哪些,除去路径扫描,子域名爆破等常见手段,有什么猥琐的方法收集企业信息?
5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...