JDBC防止SQL注入

使用参数化查询(PreparedStatement)

参数化查询是防止SQL注入的最有效方法之一。通过将查询参数化,将用户提供的输入值作为参数传递给查询,而不是将输入值直接拼接到SQL语句中。这样可以确保输入值被正确地转义和处理,减少SQL注入的风险。

例如,以下代码使用Statement执行查询:

1
2
3
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

如果用户输入的username为' or '1'='1,则最终执行的SQL语句将变为:

1
SELECT * FROM users WHERE username = '' or '1'='1' AND password = ''

这将导致所有用户都能登录成功。

而使用参数化查询后,代码可以改为如下:

1
2
3
4
5
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();

这样,用户输入的username将被正确地转义,不会影响SQL语句的执行。

输入验证和过滤

在接受用户输入之前,对输入进行验证和过滤是非常重要的。可以通过以下方法来验证和过滤用户输入:

  • 检查输入的长度,确保其不超过允许的最大长度。
  • 检查输入的格式,确保其符合预期的格式。
  • 过滤掉所有可能用于SQL注入的特殊字符。

例如,可以对username进行如下验证和过滤:

1
2
3
4
5
6
7
8
9
if (username.length() > 20) {
throw new IllegalArgumentException("Username too long");
}

if (!username.matches("[a-zA-Z0-9_]+")) {
throw new IllegalArgumentException("Invalid username");
}

username = username.replace("'", "''");