目录
展示SQL攻击
举一个出现sql 攻击的例子
SQL攻击的原因
分析原因
阻止SQL攻击
PreparedStatement的含义
使用PreparedStatement的原因
步骤如下
注意
总结
展示SQL攻击
举一个出现sql 攻击的例子
假设我们在做登录业务时,思路是这样的:
- 首先 收集用户输入的用户名,
- 其次密码和数据库中用户表中的信息逐个比对,如果相同则登录成功;否则登录失败 ,表示用户表中没有该用户信息,需要注册
用户名:tom , 密码 123 。 user 表中已存在该信息
测试代码如下
login("tom", "123");
login方法代码如下
public static void login(String username1, String password1) {String url = "jdbc:mysql://localhost:3306/dj666";String username = "root";String password = "123456";Connection conn = null;Statement statement = null;try {conn = DriverManager.getConnection(url, username, password);statement = conn.createStatement();String sql = "select * from user where pwd='" + password1 + "' and username='" + username1 + "'";ResultSet resultSet = statement.executeQuery(sql);if (resultSet.next()) {System.out.println("登录成功");} else {System.out.println("登录失败");}} catch (SQLException e) {throw new RuntimeException(e);} finally {try {if (statement != null)statement.close();else if (conn != null)conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
因为user表 有这条用户信息,所有肯定是登录成功的
但实际上,用户在设置个人信息,不会这么简单,或许会更加的复杂
如下面所示:
login("a' or '1'='1", "a' or '1'='1");
我们知道,上面的信息在user用户表中,是没有的。也因此会登录失败
但运行后发现,“登录成功” 这就有问题!!!!!!!!!!!!
SQL攻击的原因
分析原因
一个不存在的信息,却可以登录成功这是为什么呢
我们把用户输入的信息和查询的sql语句连起来,发现变成了一条完成的SQL查询语句
select * from user where pwd='a' or '1'='1' and username='a' or '1'='1'
发现 在我们添加 个人信息时,使用or 但在数据库中, or会被视为 关键字,并且or关键字 把用户输入的信息连接为一条完成的sql语句片段 。因此 where条件,一直是true 。即使用户表中,没有这条信息,也会被识别并登录成功。
当然,还不止于此:具体原因如下:
在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!
但我们应该如何避免,既可以使用像or 这样容易被数据库看作关键字的 单词,又不会被数据库识别成关键字
阻止SQL攻击
PreparedStatement的含义
PreparedStatement叫预编译声明!
PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。
使用PreparedStatement的原因
- 防止SQL攻击
- 提高代码的可读性,以可维护性
- PreparedStatement的使用
步骤如下
1 使用Connection 对象调用 PreparedStatement(String sql) 方法 参数为 我们使用的sql语句模板。变量用?表示
String sql = "update student set stuName=?,stuAge=?,stuSex=? where stuId=?";//获取执行sql语句的对象preparedStatement = conn.prepareStatement(sql);
2 调用PreparedStatement的setXXX()系列方法为问号 “?”设置值 。占位符 从1 开始
preparedStatement.setString(1, password1);preparedStatement.setString(2, username1);
3 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
修改上面的代码如下
//sql语句模板String sql = "select * from user where pwd=? and username=?";//获取执行sql语句的对象preparedStatement = conn.prepareStatement(sql);// 1 表示占位符的索引,从1开始preparedStatement.setString(1, password1);preparedStatement.setString(2, username1);// 调用executeQuery方法,执行sql语句ResultSet resultSet = preparedStatement.executeQuery();
注意
- 1 SQL语句中的问号,表示参数,后面使用setxx(占位符索引,传递的实参),为参数赋值,变成一条完整的SQL语句。
- 2 PreparedStatement 的executeQuery()方法 是没有参数的,这是PreparedStatement 独有的;而Statement的executeQuery()是需要参数(SQL语句)的 。
疑问,为什么PreparedStatement 的executeQuery()方法 是没有参数的?
理由如下:
因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了。所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。
修改后login方法代码如下
public static void login(String username1, String password1) {String url = "jdbc:mysql://localhost:3306/dj666";String username = "root";String password = "123456";Connection conn = null;PreparedStatement preparedStatement=null;try {conn = DriverManager.getConnection(url, username, password);//sql语句模板String sql = "select * from user where pwd=? and username=?";//获取执行sql语句的对象preparedStatement = conn.prepareStatement(sql);// 1 表示占位符的索引,从1开始preparedStatement.setString(1, password1);preparedStatement.setString(2, username1);// 调用executeQuery方法,执行sql语句ResultSet resultSet = preparedStatement.executeQuery();if (resultSet.next()) {System.out.println("登录成功");} else {System.out.println("登录失败");}} catch (SQLException e) {throw new RuntimeException(e);} finally {try {if (preparedStatement != null)preparedStatement.close();else if (conn != null)conn.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
运行结果
发现,使用了PreparedStatement后可以处理sql攻击
总结
所以,建议大家在今后的开发中,无论什么情况,都去使用PreparedStatement,而不是使用Statement,这样可以极大的避免因输入的内容(向变量传递值)造成SQL语句和原本我们所想的SQL不同造成的结果错误。