CodeQL 文档

使用可能不可信字符串进行连接构建的查询

ID: java/concatenated-sql-query
Kind: problem
Security severity: 8.8
Severity: error
Precision: medium
Tags:
   - security
   - external/cwe/cwe-089
   - external/cwe/cwe-564
Query suites:
   - java-security-extended.qls
   - java-security-and-quality.qls

单击以在 CodeQL 存储库中查看查询

即使 SQL 查询的组件并非完全受用户控制,直接连接这些组件来构建查询也是一个漏洞。也许一个单独的漏洞将允许用户获得组件的控制权。同样,无法完全控制输入的用户可能会对其施加足够的影响,导致 SQL 查询无法运行。

建议

通常,最好使用 SQL 预处理语句,而不是使用字符串连接构建完整的 SQL 查询。预处理语句可以包含一个通配符,写为问号 (?),用于 SQL 查询的每个部分,预期每次运行时都由不同的值填充。稍后执行查询时,必须为查询中的每个通配符提供一个值。

在 Java 持久化查询语言中,最好使用带参数的查询,而不是使用字符串连接构建完整的查询。Java 持久化查询可以包含一个参数占位符,用于查询的每个部分,预期在运行时由不同的值填充。参数占位符可以用冒号 (:) 后跟参数名称,或问号 (?) 后跟整数位置来表示。稍后执行查询时,必须使用 setParameter 方法为查询中的每个参数提供一个值。使用 @NamedQuery 注释指定查询会引入一个额外的安全级别:查询必须是一个常量字符串文字,防止通过字符串连接进行构造,并且为查询部分填充值的唯一方法是设置位置参数。

无论任何参数是否可以直接追溯到用户输入,使用预处理语句(在 SQL 中)或查询参数(在 Java 持久化查询语言中)为查询提供参数值都是一种好习惯。这样做可以避免任何关于引用和转义的担忧。

示例

在以下示例中,代码以两种不同的方式运行一个简单的 SQL 查询。

第一种方法涉及通过将 getCategory 的结果与一些字符串文字连接起来,来构建一个查询 query1getCategory 的结果可能包含特殊字符,或者它可能在稍后进行重构,以便它返回包含特殊字符的内容。

第二种方法展示了良好的实践,它涉及使用包含通配符 (?) 的单个字符串文字来构建一个查询 query2。然后通过调用 setString 为通配符赋予一个值。此版本不受注入攻击的影响,因为 getCategory 的结果中的任何特殊字符都不会得到任何特殊处理。

{
    // BAD: the category might have SQL special characters in it
    String category = getCategory();
    Statement statement = connection.createStatement();
    String query1 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
        + category + "' ORDER BY PRICE";
    ResultSet results = statement.executeQuery(query1);
}

{
    // GOOD: use a prepared query
    String category = getCategory();
    String query2 = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=? ORDER BY PRICE";
    PreparedStatement statement = connection.prepareStatement(query2);
    statement.setString(1, category);
    ResultSet results = statement.executeQuery();
}

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私