SQL注入绕过身份验证是攻击者利用应用对用户输入处理不当的漏洞,通过构造特殊SQL语句绕过登录校验逻辑,直接获取系统访问权限的攻击方式,这类攻击在缺乏安全防护的Web应用中十分常见。
SQL注入绕过身份验证的常见方式
1. 构造永真条件绕过
很多应用的登录逻辑会拼接用户输入的用户名和密码到SQL查询语句中,如果未对输入做过滤,攻击者可以输入特殊字符让查询条件恒成立。比如登录查询语句原本是:
SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';
攻击者在用户名输入框输入admin' OR '1'='1,密码随意输入,拼接后的SQL语句会变成:
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '任意内容';
由于OR '1'='1'是永真条件,只要表中存在admin用户,查询就会返回对应记录,从而绕过密码校验完成登录。
2. 注释符截断后续逻辑
攻击者还可以使用SQL注释符--或者/* */截断原本的查询逻辑,忽略密码校验部分。比如在用户名输入框输入admin'--,拼接后的SQL语句为:
SELECT * FROM users WHERE username = 'admin'--' AND password = '任意内容';
在SQL中--后面的内容会被视为注释,不会被执行,因此密码校验部分会被直接忽略,只要admin用户存在就能登录成功。
3. 绕过空密码校验
部分应用的登录逻辑会先判断密码是否为空,攻击者可以构造输入让密码判断失效。比如在用户名输入admin' AND password IS NULL--,如果admin用户的密码字段为空,或者应用存在逻辑漏洞,就可能绕过校验。
加强认证防护的方法
1. 使用参数化查询
参数化查询是最有效的防SQL注入方式,它会将用户输入作为参数传递,而不是拼接进SQL语句,数据库会把参数内容当作普通数据处理,不会执行其中的SQL逻辑。以下是Java中使用PreparedStatement的示例:
String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); // 设置用户名参数 pstmt.setString(2, password); // 设置密码参数 ResultSet rs = pstmt.executeQuery();
同样,PHP中可以使用PDO参数化查询:
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->execute();
2. 对用户输入做严格过滤
对用户名、密码等登录输入做白名单校验,只允许符合规则的字符通过,比如用户名只允许字母、数字、下划线,长度限制在合理范围。同时过滤常见的SQL注入特殊字符,如单引号、双引号、注释符、OR、AND等关键字,不过过滤只能作为辅助手段,不能完全替代参数化查询。
3. 密码安全存储与校验
不要明文存储用户密码,要使用加盐哈希算法存储密码,比如使用bcrypt、argon2等算法,每个用户的盐值随机生成。登录时先查询用户记录,再对输入的密码做同样的哈希处理,对比哈希值是否一致,避免直接拼接密码到SQL语句中。
4. 增加额外认证校验
可以在身份验证环节增加验证码、登录失败次数限制、异地登录校验等机制,即使攻击者尝试SQL注入,也会因为这些额外校验无法快速完成攻击,降低被绕过的风险。
5. 最小权限原则
应用连接数据库的账号不要使用高权限账号,只授予查询用户表的必要权限,即使发生SQL注入,攻击者也无法通过注入语句修改数据或者获取更高权限。
总结
SQL注入绕过身份验证的核心是应用未正确处理用户输入,将用户输入直接拼接进SQL语句。开发者需要从查询逻辑、输入处理、密码存储、权限控制等多个层面做好防护,其中参数化查询是必须落实的基础措施,配合其他防护手段才能有效提升认证环节的安全性,避免系统被非法入侵。