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

Posted

技术标签:

【中文标题】准备好的语句中的动态 where 条件的 SQL 注入【英文标题】:SQL injection for Dynamic where conditions in prepared statement 【发布时间】:2019-04-28 17:04:48 【问题描述】:

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

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

我的应用程序有四个输入参数,

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

基于这些输入值,我正在为准备好的语句构建动态“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();

【问题讨论】:

尝试使用 CriteriaBuilder - objectdb.com/java/jpa/query/criteria的示例 什么是Criteria Builders?请指点 你看链接了吗 是什么让您认为您面临 SQL 注入的风险?您正在使用 PreparedStatement 和有界参数,您应该是安全的。 但是当veeracode扫描运行时(veera code是一种扫描应用程序代码缺陷的工具),表明这段代码存在sql注入缺陷。 【参考方案1】:

您处理strSerialNumberLogicstrCreationDateLogic 的方式确实允许SQL 注入攻击。而不是直接将它们的值附加到 where 子句,您应该使用条件逻辑来确定要使用的正确条件:

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]Logic 的值在附加之前有效以避免注入攻击,但您的代码检查器可能仍会抛出错误,因此最好附加字符串文字而不是变量。

【讨论】:

以上是关于准备好的语句中的动态 where 条件的 SQL 注入的主要内容,如果未能解决你的问题,请参考以下文章

带有 IN() 条件的 WordPress 准备好的语句

--------------------------------MaBatis动态sql--------------------------

sql语句 根据条件的不同让where后的赛选条件动态改变

在sql的where条件下怎么加判断语句?

在 Java 中将动态 SQL 查询转换为准备好的语句

java代码中组装where条件然后拼接到mybatis xml中的sql后面