1. 导语
可能有点标题党 哈哈,现在一般的网站登录,用户名和密码被正则限制的死死的,一般很难有漏洞通过sql语句,登录进去,但是不妨碍我们做一个demo,看看怎么的漏洞能被sql利用,同时学习怎样加强防备使用PreparedStatement来预防。
2. 学习
一般我们登录时,一般会把我们的用户名和密码用来作为sql查询的条件,查询数据库,如果能查到则说明有次用户,如果查不到当然无此用户。查询时我们一般都是在java代码中操作的,所以就用到 JDBC
jdbc
2.1 建表插入数据
如上图,这里准备了一张 student 表,其中的 studentname 、stupsd 分别当作用户名和密码
2.2 使用 JDBC 查询
代码比较简答,直接贴了
private static Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/db4";
return DriverManager.getConnection(url, "root", "root");
}
/**
* login in jdbc
* @param name
* @param psd
*/
private void login(String name,String psd){
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = getConnection();
statement = connection.createStatement();
// select * from student where studentname = "xxx" and stupsd = "xxx"
String sql = "select * from student where studentname = '"+name +"' and stupsd = '"+psd+"'";
resultSet = statement.executeQuery(sql);
if(resultSet.next()){
System.out.println(" --- "+name+" Welcome To Home! ");
}else{
System.out.println(" --- error ---");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
statement.close();
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
特别注意 sql 语句的书写,因为用户名和密码都是字符串类型的,所以需要如上图一样的书写。现在那个账号和密码试一试
]
发现没 ,没问题。再随便修改下账号
从上面比较可以看出这个 jdbc 的查询和 sql 语句没问题。现在用下面的用户名和密码试一试
发现没,简直莫名其妙,我们的表中那有这个用户和密码,但是确成功登录了。这技巧无非利用 or 这个关键字,毕竟 a = a 一定为 true,然后再加上 or 那 and 左边 和 右边都是 true 了,这样子 where 后面的限制条件其实已经不起作用了,我们可以将这个 sql 语句在数据库中运行下。
select * from student where studentname = “a” or “a” = “a” and stupsd = “a” or “a” = “a”;
发现没 上面的语句 其实和下面一样的
select * from student;
2.3 防备措施
- 过滤用户输入的数据中是否包含非法字符
- 分步校验,先使用用户名来查询用户,如果查找到了,再比较密码
- 使用PreparedStatement
前面两种比较好理解,下面看看这个PreparedStatement的使用
PreparedStatement叫预编译声明,是Statement的子接口
使用它的好处
- 防止SQL攻击
- 提高代码的可读性,以可维护性
- 提高效率
PreparedStatement的使用步骤
- 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;
- 调用PreparedStatement的setXXX()系列方法为问号设置值
- 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
现在将代码修改下,如下
String sql = "select * from student where studentname = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
resultSet = preparedStatement.executeQuery();
再用如下代码测试下
嗯哼 没问题,现在再使用 有问题的 用户名测试
发现没,已经登录不进去了
有朋友可能在想 为什么 PreparedStatement 可以提高效率,其实它已经和 sql 语句绑定了,绑定了 某个 sql 语句的模板,然我们可以使用不用的参数来使用此模板,就是复用 sql 模板。
所以,建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement。