题目
如何防止用户登录功能中的SQL注入攻击?
信息
- 类型:问答
- 难度:⭐
考点
SQL注入原理,参数化查询,输入验证
快速回答
防止SQL注入的核心方法是:
- 使用参数化查询(预编译语句)替代字符串拼接
- 对用户输入进行严格验证和过滤
- 遵循最小权限原则配置数据库账户
- 避免直接显示数据库错误信息给用户
1. 原理说明
SQL注入是攻击者通过在用户输入中插入恶意SQL代码,篡改原始SQL语句逻辑的攻击方式。例如在登录场景中:
-- 原始语句
SELECT * FROM users WHERE username = '[user_input]' AND password = '[user_input]';
-- 攻击者输入用户名:admin' --
-- 最终执行语句:
SELECT * FROM users WHERE username = 'admin' -- ' AND password = '';
-- 注释符(--)使密码验证失效,直接以admin身份登录2. 危险代码示例
以下Java代码存在严重注入漏洞:
// 危险!字符串拼接方式
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql); // 攻击者可操控完整SQL结构3. 修复方案(参数化查询)
使用PreparedStatement进行参数绑定:
// 安全:参数化查询
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username); // 参数1绑定用户名
pstmt.setString(2, password); // 参数2绑定密码
ResultSet rs = pstmt.executeQuery(); // 输入值会被当作纯数据处理4. 最佳实践
- 强制使用参数化查询:所有数据库操作均通过PreparedStatement或ORM框架的参数绑定机制
- 输入验证:对用户名/密码格式进行正则校验(如长度、字符类型)
if (!username.matches("[a-zA-Z0-9_]{4,20}")) { throw new IllegalArgumentException("Invalid username"); } - 错误处理:捕获数据库异常时返回通用错误提示,避免泄露表结构等敏感信息
- 深度防御:数据库账户使用最小权限(禁止DROP/ALTER等权限)
5. 常见错误
- 认为过滤单引号就安全(存在编码绕过和数字型注入)
- 依赖前端验证(攻击者可绕过浏览器直接调用API)
- 手动拼接参数(如
String.format()),未使用真正的预编译
6. 扩展知识
- ORM框架:MyBatis中应使用
#{param}而非${param} - 二次注入:即使经过转义存储的数据,在读取后再次拼接SQL仍可能触发注入
- 自动化工具:使用SQLMap等工具定期进行安全扫描