本文目录导读:

我来为你提供几个Java中SQL注入的典型案例,以及相应的防护措施。
有SQL注入漏洞的案例
案例1:字符串拼接(最危险)
// 有漏洞的代码
public User getUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 处理结果...
}
// 攻击示例
// 输入: admin' OR '1'='1
// 生成的SQL: SELECT * FROM users WHERE username = 'admin' OR '1'='1'
// 结果:返回所有用户信息
案例2:拼接ORDER BY或LIKE子句
// 有漏洞的ORDER BY
public List<User> getUsersSorted(String sortColumn) {
String sql = "SELECT * FROM users ORDER BY " + sortColumn;
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
}
// 攻击示例
// 输入: id; DROP TABLE users; --
// 生成的SQL: SELECT * FROM users ORDER BY id; DROP TABLE users; --
案例3:拼接LIKE查询
// 有漏洞的LIKE查询
public List<User> searchUsers(String keyword) {
String sql = "SELECT * FROM users WHERE username LIKE '%" + keyword + "%'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
}
存在漏洞的登录验证示例
public boolean login(String username, String password) {
String sql = "SELECT * FROM users " +
"WHERE username = '" + username + "' " +
"AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
return rs.next(); // 只要有结果就登录成功
}
// 攻击示例
// 用户名: admin' --
// 密码: 任意
// 生成的SQL: SELECT * FROM users WHERE username = 'admin' -- ' AND password = '任意密码'
// 结果:成功绕过密码验证
使用PreparedStatement的正确防御
示例1:基本的参数化查询
// 安全的查询方式
public User getUserByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setEmail(rs.getString("email"));
return user;
}
return null;
}
示例2:安全的登录验证
public boolean login(String username, String password) {
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
return rs.next();
}
示例3:安全的LIKE查询
public List<User> searchUsers(String keyword) {
String sql = "SELECT * FROM users WHERE username LIKE ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "%" + keyword + "%");
ResultSet rs = pstmt.executeQuery();
List<User> users = new ArrayList<>();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
users.add(user);
}
return users;
}
示例4:处理排序字段的特殊情况
public List<User> getUsersSorted(String sortColumn) {
// 白名单验证
List<String> allowedColumns = Arrays.asList("id", "username", "email", "created_at");
if (!allowedColumns.contains(sortColumn)) {
throw new IllegalArgumentException("Invalid sort column: " + sortColumn);
}
// 即使使用字符串拼接,由于有白名单验证,也是安全的
String sql = "SELECT * FROM users ORDER BY " + sortColumn;
PreparedStatement pstmt = connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
// 处理结果...
}
MyBatis中的SQL注入预防
错误的写法(有注入风险)
<!-- 有SQL注入风险的写法 -->
<select id="getUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE username LIKE '%${username}%'
</select>
<!-- 有SQL注入风险的ORDER BY -->
<select id="getUsersSorted" resultType="User">
SELECT * FROM users ORDER BY ${sortColumn}
</select>
正确的写法
<!-- 安全的写法 -->
<select id="getUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
<!-- 或者 -->
<select id="getUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE username LIKE '%' || #{username} || '%'
</select>
<!-- 对于ORDER BY,使用白名单 -->
<select id="getUsersSorted" resultType="User">
SELECT * FROM users
<choose>
<when test="sortColumn == 'username'">ORDER BY username</when>
<when test="sortColumn == 'email'">ORDER BY email</when>
<otherwise>ORDER BY id</otherwise>
</choose>
</select>
最佳实践总结
- 始终使用PreparedStatement,避免字符串拼接
- 使用参数占位符 而不是直接拼接变量
- 对于ORDER BY等特殊场景,使用白名单验证
- 在MyBatis中使用 而不是
- 实施输入验证,对特殊字符进行过滤或转义
- 使用ORM框架(如Hibernate、MyBatis)并正确使用参数化查询
- 最小权限原则,数据库连接使用权限最小的账号
- 定期进行安全审计,使用工具扫描SQL注入漏洞
通过遵循这些最佳实践,可以有效防止SQL注入攻击,保护应用程序和数据库的安全。