预准备语句中动态 where 条件的 SQL 注入

SQL injection for Dynamic where conditions in prepared statement

提问人:rz nihar 提问时间:11/27/2018 最后编辑:Scary Wombatrz nihar 更新时间:11/28/2018 访问量:931

问:

在我的应用程序中,我们正在从 UI 收集一些用户输入,并基于这些值生成具有不同“Where”条件的动态 SQL 来查询数据。发现该段代码存在一些 SQL 注入缺陷。

我无法重新排列此代码以防止该 flaq。任何建议都会有所帮助。

My Application 接受四个输入参数,

  1. 序列号 - 可以是一个或无,也可以是两个值 2.创建日期 - 可以是一个或没有,或两个值
  2. ReportTypeNumbers- 这可以是 1 个或 none,也可以是两个以上
  3. reportTitleNames - 这可以是 one 或 none,也可以是 two 以上

基于这些输入值,我正在为准备好的语句构建动态“Where”条件。此 SQL 存在问题。请帮我重写它以修复SQL注入缺陷。

这是构造动态 SQL 的方法。

public void filter(String strSerialNumberLogic, String strSerialNumber1,
        String strSerialNumber2, String strCreationDateLogic,
        long lngCreationDate1, long lngCreationDate2,
        String strTypeNumbers, String strTitles, long lngLoc)
        throws SQLException, ClassNotFoundException {

    StringBuffer strWhere = new StringBuffer();
    List paramList = new ArrayList();
    String arrTypeNumbers[];
    String arrTitles[];
    int i;
    boolean bolHit;

    if (!strTypeNumbers.equals("") || !strTitles.equals("")) {
        arrTypeNumbers = strTypeNumbers.split(",");
        arrTitles = strTitles.split(",");

        bolHit = false;
        strWhere.append("(");

        for (i = 0; i < arrTypeNumbers.length; i++) {
            if (arrTypeNumbers[i].length() > 0) {
                if (bolHit) {
                    strWhere.append(" OR ");
                } else {
                    bolHit = true;
                }

                strWhere.append(" REPORT_NUMBER = ?");
                paramList.add(arrTypeNumbers[i]);
            }
        }

        for (i = 0; i < arrTitles.length; i++) {
            if (arrTitles[i].length() > 0) {
                if (bolHit) {
                    strWhere.append(" OR ");
                } else {
                    bolHit = true;
                }

                strWhere.append(" REPORT_NAME = ?");
                paramList.add(arrTitles[i]);
            }
        }

        strWhere.append(") ");
    }

    if (!strSerialNumber1.equals("")) {

        if (!strWhere.equals("")) {
            strWhere.append(" AND ");
        }
        strWhere.append(" REPORT_FILE_NO " + strSerialNumberLogic + " ? ");
        paramList.add(strSerialNumber1);

        if (strSerialNumberLogic.equals("between")) {
            strWhere.append(" AND ? ");
            paramList.add(strSerialNumber2);
        }
    }

    if (lngCreationDate1 != 0) {


        if (!strWhere.equals("")) {
            strWhere.append(" AND ");
        }

        strWhere.append(" REPORT_CREATION_DATE " + strCreationDateLogic + " ? ");
        paramList.add(Long.toString(lngCreationDate1));

        if (strCreationDateLogic.equals("between")) {
            strWhere.append(" AND ? ");
            paramList.add(Long.toString(lngCreationDate2));
        }
    }

    if (lngLoc != 0) {

        if (!strWhere.equals("")) {
            strWhere.append(" AND ");
        }
        strWhere.append(" REPORT_FILE_LOCATION = ? ");
        paramList.add(Long.toString(lngLoc));
    }
    String finalQuery = "";
    if (!strWhere.equals("")) {
        finalQuery = "WHERE " + strWhere.toString();
    }

    String strSQL = "SELECT * " + "FROM D990800 "
            + "LEFT JOIN D990400 ON REPORT_SYSTEM_ID ||" + " REPORT_NO = REPORT_NUMBER " + finalQuery
            + "ORDER BY REPORT_FILE_NO ASC";


    System.out.println("strSQL:" + strSQL );
    System.out.println("paramList:" + paramList );

    Connection conn = ConnectionFactory.instance().getConnection();
    PreparedStatement preparedStatement = null;
    preparedStatement = conn.prepareStatement(strSQL);

    for (int index = 0; index < paramList.size(); index++) {
        String param = (String) paramList.get(index);

        if (isParsableInt(param)) {
            preparedStatement.setInt(index+1, Integer.parseInt(param));
        } else {
            preparedStatement.setString(index+1, param);
        }
    }

    ResultSet rsReports = preparedStatement.executeQuery();

    buildCollection(rsReports);
    rsReports.close();
    preparedStatement.close();
    conn.close();
}
java jdbc 准备语句 注入 动态 sql

评论

0赞 Scary Wombat 11/27/2018
尝试使用 CriteriaBuilder - objectdb.com/java/jpa/query/criteria 中的示例
0赞 rz nihar 11/27/2018
什么是标准生成器?请给出一些建议
0赞 Scary Wombat 11/27/2018
你看过链接吗
0赞 frenchoverflow 11/27/2018
是什么让您认为自己有 SQL 注入的风险?您正在使用 PreparedStatement 和有界参数,您应该是安全的。
0赞 rz nihar 11/27/2018
但是当 veeracode 扫描运行时(veera 代码是一种扫描应用程序代码缺陷的工具),它表明这段代码存在 sql 注入缺陷。

答:

1赞 Sentinel 11/28/2018 #1

您处理并允许 SQL 注入攻击的方式。与其直接将其值附加到 where 子句,不如使用条件逻辑来确定要使用的正确条件:strSerialNumberLogicstrCreationDateLogic

strWhere.append(" REPORT_FILE_NO ");

switch (strSerialNumberLogic) {
  case "=":
    strWhere.append("= ? ");
    paramList.add(strSerialNumber1);
    break;
  case "!=":
  case "<>":
    strWhere.append("!= ? ");
    paramList.add(strSerialNumber1);
    break;
  case "<":
    strWhere.append("< ? ");
    paramList.add(strSerialNumber1);
    break;
  case "<=":
    strWhere.append("<= ? ");
    paramList.add(strSerialNumber1);
    break;
  case ">":
    strWhere.append("> ? ");
    paramList.add(strSerialNumber1);
    break;
  case ">=":
    strWhere.append(">= ? ");
    paramList.add(strSerialNumber1);
    break;
  case "between":
    strWhere.append("between ? and ? ");
    paramList.add(strSerialNumber1);
    paramList.add(strSerialNumber2);
    break;
  case "not between":
    strWhere.append("not between ? and ? ");
    paramList.add(strSerialNumber1);
    paramList.add(strSerialNumber2);
    break;
  case "is null":
    strWhere.append("is null ");
    break;
  case "is not null":
    strWhere.append("is not null ");
    break;
}

虽然你可以简单地检查以确保 str[SerialNumber|CreationDate]逻辑在附加之前是有效的 为了避免注入攻击,代码检查器可能仍会抛出错误,因此最好附加字符串文字而不是变量。