我一直在努力让我的应用程序通过 Checkmarx,但我一直遇到二阶 SQL 注入漏洞。
目前我的代码是这样的:
public static final String SELECT_NEXTVAL_FOR = "SELECT NEXTVAL FOR ";
@Value("#{ systemProperties['sequence.schema']}")
String sequenceSchema;
public Integer getNextContactId() {
if (sequenceSchema.matches("[A-Z]{3}@[A-Z]{3}|[A-Z]\\d[A-Z]{5}")) {
Query getNextContactIdQuery = entityManager.createNativeQuery(
SELECT_NEXTVAL_FOR + sequenceSchema + ".CNTCT_ID FROM SYSIBM.SYSDUMMY1");
return (Integer) getNextContactIdQuery.getSingleResult();
} else {
return null;
}
Checkmarx 将
sequenceSchema
标记为这里的问题。我的想法是 if 语句将确保 sequenceSchema
中存在的任何内容仅匹配我们模式的特定正则表达式。显然这还不够好。我曾尝试使用位置/命名参数,但这些参数在 WHERE 或 HAVING 子句之外不起作用。我已经尝试过 here 中看到的条件查询,但没有任何运气。
我有另一个想法与我在这里有类似的想法,但我可以创建一组有效的模式并将其与
sequenceSchema
进行比较,而不是使用正则表达式:
public static final String SELECT_NEXTVAL_FOR = "SELECT NEXTVAL FOR ";
@Value("#{ systemProperties['sequence.schema']}")
String sequenceSchema;
Set<String> validSchemas = ... //create set of valid schemas
public Integer getNextContactId() {
if (validSchemas.contains(sequenceSchema)) {
Query getNextContactIdQuery = entityManager.createNativeQuery(
SELECT_NEXTVAL_FOR + sequenceSchema + ".CNTCT_ID FROM SYSIBM.SYSDUMMY1");
return (Integer) getNextContactIdQuery.getSingleResult();
} else {
return null;
}
这会防止 SQL 注入吗?如果不是,为什么?我真的很难在这里找到一个可行的解决方案。
那些推荐使用查询参数来防御SQL注入的人只对了一半。在大多数情况下,这是推荐的解决方案。
但是查询参数只能用来代替常量值。不是标识符(在您的情况下是模式标识符)、表达式或 SQL 关键字等
你在使用某种验证的正确轨道上,所以模式标识符被限制为一个已知的安全值。然后您可以将该字符串插入到您的 SQL 查询中。如果你已经做了充分的验证,那么它就可以免受 SQL 注入攻击。
您还应该分隔您的标识符,以防它包含空格、标点符号或与 SQL 保留关键字冲突。在 DB2 中,使用双引号作为标识符分隔符。
然而,即使您所做的验证可能已经足够,它可能不是一种可以被自动 SQL 注入检测工具(如 Checkmarx)识别的防御方法。它可能会看到您正在将变量一起放入一个 SQL 字符串中,并得出结论认为这是一个 SQL 注入风险。它不能说您的验证足以防止出现问题。
SQL 注入检测的自动化解决方案总是有限的。有时您确实必须使用人工分析。
我知道现在这么说已经不合时宜了,因为有一种说法是人工智能会让程序员失业。但是人工智能还不能像人类专家那样可靠地处理边缘情况。我不相信它会是。
我不会为你做你的工作......但是你的问题可以总结为“为什么我会得到 SQL 注入?”
答案是“因为我正在动态创建 SQL 查询”,解决方案是:停止创建动态创建的 SQL 查询。 一旦停止这样做,所有问题都会消失。您如何停止?使用
PreparedStatement()
与变量绑定(参数化查询)。
使用
PreparedStatement()
你的sql访问看起来像:
string what_schema= //! Some value;
String query = SELECT_NEXTVAL_FOR + "?.CNTCT_ID FROM SYSIBM.SYSDUMMY1";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, what_schema);
ResultSet results = pstmt.executeQuery();