攻击方式
大多数人拥有字母数字式密码,或者有安全意识的人,拥有附带其他键盘符号的字母数字式密码。由于 这种想法,开发人员可能允许输入任何字符作为密码。这通常是没问题的,除非他们忘记清洁或检查输入数据。这种情况比应该的要发生的频繁得多。使用 SQL 数据库的密码系统(在许多网站上非常普遍的场景)可能运行这样的查询:
SELECT * FROM users WHERE 'username' = '$USER' AND 'password'='$PASS';
$USER 和 $PASS 会用用户提供的用户名和密码来代替。那么如果用户输入‘bob’和‘1234’,那么结果的查询是: SELECT * FROM users WHERE 'username' = 'bob' AND 'password' = '1234';
而来自数据库的返回值会是所有用 bob 作为用户名且用 1234 作为密码的数据元组。如果黑客输入 admin 和 <<’hi’ 或 1=1>>–,那么查询是: SELECT * FROM users WHERE 'username' = 'admin' and `password` = 'hi' OR 1=1--'
注意用户输入的引号如何与原始查询中的第三个引号匹配。数据库现在会返回用户名为 admin 的所有元组,并且会取消对密码的检查,因为 ‘password’ = ‘hi’ OR 1=1 命令数据库寻找密码是 hi 的元组或 1=1 的元组,而由于 1 总是 1,所以每行都是候选。– 是 SQL 注释标志,取消查询中原始的其他引号,并且还将取消任何额外的检查,因此如果有额外的凭证(也就是,keyfob 或 captcha)也会被忽略。现在黑客可以以管理员的身份进入系统并且不用不得不给出合法的密码。通过利用越来越复杂的查询,黑客可以变更、添加,或查询数据。对于数据库,这令黑客具有同应用程序相同的特权。
解决方案
a.前端页面需要校验用户的输入数据(限制用户输入的类型、范围、格式、长度),不能只靠后端去校验用户数据。一来可以提高后端处理的效率,二来可以提高后端数据的安全。 b.后端不要动态sql语句,使用存储过程查询语句。限制用户访问数据库权限。后端接受前端的数据时要过滤一些特殊字符(如:“--”等字符) c.后端如果出现异常的话,要使用自定义错误页,防止用户通过服务器默认的错误页面找到服务器漏洞。
安全查询(参数化查询):
//获取参数,拆分参数 String custname = request.getParameter("customerName"); String query = "SELECT account_balance FROM user_data WHERE user_name = ? "; //创建连接 PreparedStatement pstmt = connection.prepareStatement( query ); //将参数格式化 pstmt.setString( 1, custname); //获取查询结果 ResultSet results = pstmt.executeQuery(); 安全查询(存储过程) //获取参数 String custname = request.getParameter("customerName"); try { //调用数据库的存储过程 CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}"); //将参数格式化 cs.setString(1, custname); //获取查询结果 ResultSet results = cs.executeQuery(); } catch (SQLException se) { } 不安全查询 String query = "SELECT account_balance FROM user_data WHERE user_name = " + request.getParameter("customerName"); //动态查询,直接拼接字符串 try { //创建连接 Statement statement = connection.createStatement(); //获取查询结果 ResultSet results = statement.executeQuery( query ); }