保护 Java 应用程序免受 SQL 注入

Securing Java Applications from SQL injection

如果我们在 SQL 查询中传递 1=1,它将 return 所有数据,因为它在所有条件下都为真。

String query =  "select * from users where userId= 'abcd' or '1'='1'";

我正在查看 PreparedStatement 接口,发现了 setString 方法,该方法将列的值附加到准备语句中。我想知道内部如何防止 sql 注入?如果我通过这样的东西 -

Connection con=DriverManager.getConnection(url,username,password);  
PreparedStatement query = con.prepareStatement("select * from users where userId=?");  
query.setString(1,userId);'

最终这取决于数据库及其 JDBC 驱动程序。

例如,MySQL 服务器本身支持准备好的语句(ServerPreparedStatement,这意味着当您执行 query.executeQuery() 时,驱动程序会向服务器发送以下数据:

  1. 准备好的SQL语句(select * from users where userId=?)
  2. 第一个参数的值("abcd' or '1'='1")

MySQL 服务器不需要解析完整的 SQL 语句(select * 来自 userId= 'abcd' 的用户)并提取来自那个的比较值(“abcd”)。

相反,它可以解析准备好的语句(“select * from users where userId=?”)并从第二个数据元素中获取比较值。

警告:您需要通过设置 useServerPrepStmts=true 连接 属性 来通知 MySQL 连接器您想要使用服务器端准备好的语句,请参阅 MySQL Connector documentation


如果 SQL 服务器实现不支持准备语句(或未配置为使用服务器端准备语句),JDBC 驱动程序的工作是更改

  1. 准备好的SQL语句(select * from users where userId=?)
  2. 第一个参数的值("abcd' or '1'='1")

进入安全的SQL查询

select * from users where userId='abcd'' or ''1''=''1'

在这个简单查询的情况下,很容易转义值中的单引号,但对于更复杂的值,这就复杂得多(请参阅 https://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/ 以了解 [=58] 中可能使用的示例=]注入攻击)。

如果您尝试实现自己的转义,您很可能会错过一些特殊情况。