使用动态SQL代码时避免使用SQL注入

时间:2014-09-15 19:18:03

标签: sql-server spring jdbc mybatis code-injection

我正在研究现有Java Web应用程序的安全补救措施。该应用程序有一些由JDBC执行的动态sql代码。但是,我们使用的静态代码分析工具不接受这一点。所以,我正在寻找一种方法来修复问题。基本上,我已经验证了传递给构造查询的代码的所有输入,因此没有SQL注入的可能性。但是,SCA工具仍然不赞成此验证。所以,想知道是否有任何方法可以避免动态查询逻辑。由于查询是根据条件动态构建的,因此无法使用准备语句。

我知道存储过程可以提供帮助。但是,我知道它有自己的问题,团队也没有经验的存储过程。因此,寻找更好的方法来解决这个问题。此外,由于我们使用的是SQL Server,因此我没有在ESAPI工具包中找到任何编码函数来清理仅支持oracle和mysql的查询参数。

想知道是否使用像Mybatis这样的框架卸载构造sql到xml文件的java代码可以解决问题。如果还有其他更好的方法,你能告诉我吗?

2 个答案:

答案 0 :(得分:1)

您可以动态生成SQL并使用预准备语句。

以下是如何做到这一点的想法。 现在你有这样的代码:

StringBuilder whereClause = new StringBuilder();


if (name != null) {
    whereClause.append(String.format("name = '%s'", name));
}

// other similar conditions

String sql = "select * from table" + (whereClause.length() != 0 ? "where " + whereClause.toString() :  "");

Statement stmt = connection.createStatement();

ResultSet rs = stmt.executeQuery(sql);

// use rs to fetch data

您需要将其更改为

StringBuilder whereClause = new StringBuilder();
ArrayList<Object> parameters = new ArrayList<>();

if (name != null) {
    whereClause.append("name = ?");
    parameters.add(name);
}

// other similar conditions

String sql = "select * from table" + (whereClause.length() != 0 ? "where " + whereClause.toString() :  "");

PreparedStatement stmt = connection.prepareStatement();

for (int i = 0; i < parameters.length(); ++i) {
    setParameterValue(stmt, i + 1, parameter.get(i));
}

ResultSet rs = stmt.executeQuery(sql);

// use rs to fetch data

setParameterValue应如下所示:

void setParameterValue(PreparedStatement ps, int index, Object value) {
    if (value instanceof String) {
        ps.setString(index, (String)value);
    } if (value instanceof Integer) {
        ps.setInt(index, (Integer)value);
    } // and more boilerplate code like this for all types you need
}

使用mybatis你can避免编写这样的样板代码来生成动态sql并使这更容易。但我不知道CSA如何处理mybatis生成的SQL。

答案 1 :(得分:0)

我在尝试自己解决类似问题时发现了这个问题。

首先,我们可以将sql代码从java文件中分解出来并将其存储在resources文件夹下的文本文件中。然后,从java代码中,使用classloader的方法将sql作为inputStream读取并将其转换为String。将sql代码存储在单独的文件中将启用静态代码分析。

其次,我们可以在某种形式的sql中使用命名参数,这些参数可以通过正则表达式轻松识别。例如。 $ {namedParam}语法,熟悉不同的表达式语言。然后我们可以编写辅助方法来将这个参数化的sql和Map<String, Object>与查询参数一起使用。此映射中的键应对应于sql参数名称。这个辅助方法将生成带有set参数的PreparedStatement。使用命名参数将使sql代码更具可读性,并将为我们节省一些调试。

第三,最后,我们可以使用sql注释来标记部分sql代码,因为它们依赖于某些参数的存在。并在之前描述的帮助方法中使用它,以在结果语句中仅包括部分,其中存在参数Map中的条目。例如:/*${namedParam}[*/ some sql code /*]${namedParam}*/。这将是一种不显眼的方式将条件插入到我们的动态sql中。

遵循DRY原则,我们也可以尝试使用一些现有的表达式语言引擎,但它会给我们带来一个依赖和处理费用。

一旦我开始使用代码,我会在这里发布解决方案。