java中PreparedStatement和Statement详细讲解
人气:0大家都知道PreparedStatement对象可以防止sql注入,而Statement不能防止sql注入,那么大家知道为什么PreparedStatement对象可以防止sql注入,接下来看我的案例大家就会明白了!
我用的是mysql数据库,以admin表为例子,如下图:
最后面有具体的java代码和sql代码案例
最终执行的sql语句打印出来是SELECT * FROM admin WHERE username = '韦小宝' AND password = '222\' OR \'8\'=\'8'
从以上截图就能看出来,由此可见,prepareStatement对象防止sql注入的方式是把用户非法输入的单引号用\反斜杠做了转义,从而达到了防止sql注入的目的
Statement对象就没那么好心了,它才不会把用户非法输入的单引号用\反斜杠做转义呢!
PreparedStatement可以有效防止sql注入,所以生产环境上一定要使用PreparedStatement,而不能使用Statement
当然啦,你可以仔细研究下PreparedStatement对象是如何防止sql注入的,我自己把最终执行的sql语句打印出来了,看到打印出来的sql语句就明白了,原来是mysql数据库产商,在实现PreparedStatement接口的实现类中的setString(int parameterIndex, String x)函数中做了一些处理,把单引号做了转义(只要用户输入的字符串中有单引号,那mysql数据库产商的setString()这个函数,就会把单引号做转义)
大家有兴趣可以去网上,下载一份mysql数据库的驱动程序的源代码,看看mysql数据库产商的驱动程序的源代码,去源代码中找到setString(int parameterIndex, String x)函数,看看该函数中是怎么写的,我没有下载mysql数据库产商的驱动程序的源代码,而是把mysql数据库的驱动程序jar包解压了,找到了PreparedStatement.class文件,利用反编译工具,反编译了一下,如下:
这下大家应该知道PreparedStatement是如何防止sql注入的了吧
像222' OR '8'='8这样的sql注入还算温柔了,有些更可恶的用户,他们输入的非法的值是delete from tableName或truncate table tableName 这是十分危险的,更有甚者传入drop table tableName;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句执行,所以生产环境上一定要使用PreparedStatement,而不能使用Statement
下面再举几个例子,看截图
最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; DROP TABLE tableName;#'
最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; delete from tableName;#'
最终打印SELECT * FROM admin WHERE username = '韦小宝' AND password = '\'; truncate table tableName;#'
下面是java代码和sql语句,供大家参考,主要是为了测试PreparedStatement对象,所以java代码写的比较粗略,大家凑合着看吧!
package com.test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /* * 研究PreparedStatement是如何防止sql注入的,我分析了一下,原来是mysql数据库产商,在实 * 现PreparedStatement接口的实现类中的setString(int parameterIndex, String x)函 * 数中做了一些处理,把单引号做了转义(只要用户输入的字符串中有单引号,那mysql数据库产商的setString()这个函 * 数,就会把单引号做转义) */ public class TestConnMySql2 { public static void main(String[] args) { String connStr = "jdbc:mysql://localhost:3306/girls"; // String sql = "select * from admin"; String sql = "SELECT * FROM admin WHERE username = ? AND password = ?"; try { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection(connStr, "root", "root"); System.out.println("数据库连接=" + connection); //Statement无法防止sql注入 // Statement stmt = connection.createStatement(); //PreparedStatement可以有效防止sql注入,所以生产环境上一定要使用PreparedStatement,而不能使用Statement PreparedStatement prepareStatement = connection.prepareStatement(sql); prepareStatement.setString(1, "韦小宝"); //模拟用户输入正常的值 // prepareStatement.setString(2, "222"); //测试sql注入(模拟用户输入非法的值) prepareStatement.setString(2, "222' OR '8'='8"); /* *上面那种的sql注入还算温柔了,有些更可恶的用户,他们输入的非 *法的值是delete from tableName或truncate table tableName 这是十分危险的, * 更有甚者传入drop table tableName;有些数据库是不会让你成功的,但也有很多数 * 据库就可以使这些语句执行,所以生产环境上一定要使用PreparedStatement,而不能使用Statement */ //测试sql注入(模拟用户输入非法的值)在mysql中#井号表示单行注释(这是mysql中的基础知识,我就不赘述了) // prepareStatement.setString(2, "'; DROP TABLE tableName;#"); //测试sql注入(模拟用户输入非法的值) // prepareStatement.setString(2, "'; delete from tableName;#"); //测试sql注入(模拟用户输入非法的值) // prepareStatement.setString(2, "'; truncate table tableName;#"); ResultSet rs = prepareStatement.executeQuery(); System.out.println("sql=" + prepareStatement.toString()); int col = rs.getMetaData().getColumnCount(); System.out.println("============================"); while (rs.next()) { for (int i = 1; i <= col; i++) { System.out.print(rs.getString(i) + "\t"); if ((i == 2) && (rs.getString(i).length() < 8)) { System.out.print("\t"); } } System.out.println(""); } System.out.println("============================"); rs.close(); prepareStatement.close(); connection.close(); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } } }
#用户输入正常合法的值 SELECT * FROM admin WHERE username = '韦小宝' AND `password` = '222'; #用户输入正常合法的值 SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222'; #sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(会查询出表的所有数据) SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222' OR '8'='8' #sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入 SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '222\' OR \'8\'=\'8' #sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(DROP操作很危险) SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; DROP TABLE tableName;#' #sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入 SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; DROP TABLE tableName;#' #sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(TRUNCATE操作很危险) SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; TRUNCATE TABLE tableName;#' #sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入 SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; truncate table tableName;#' #sql注入(用户输入非法的值)使用Statement对象,无法防止sql注入(DELETE操作很危险) SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = ''; DELETE FROM tableName;#' #sql注入(用户输入非法的值)使用PreparedStatement对象,可以有效防止sql注入 SELECT * FROM admin WHERE username = '韦小宝' AND PASSWORD = '\'; delete from tableName;#' #所以生产环境上一定要使用PreparedStatement,而不能使用Statement /* 顺便复习一下mysql中的3种注释,我是多行注释 */ #我是单行注释 -- 我也是单行注释(注意:-- 这种注释,后面必须要加一个空格,否则语法报错)
您可能感兴趣的文章:
加载全部内容