ResultSet更新{ColumnType}方法是否容易受到SQL注入攻击?

时间:2017-01-10 18:02:22

标签: java database jdbc sql-injection bluemix-app-scan

由AppScan源标记进行的安全扫描,必须在下面的代码中的$this->aURI = explode('/', $_SERVER['SCRIPT_NAME']); $sRequest = preg_replace('!'.$this->sURI.'(.*)$!i', '$1', $_SERVER['REQUEST_URI']); if(substr($sRequest, -1)!='/') $sRequest .= '/'; $sGets = $this->parseUrl($sRequest); 行上验证输入(Validation.Required):

    private function parseUrl($sRequest){   
    $sVars = null;
    foreach($this->aRoutingParse AS $k => $v){

        if(!is_array($v))
            continue;

        preg_match_all('!\[(.+?)\]!i', $v[0], $aExpression_);
        $sExpression = preg_replace_callback('!\[(.+?)\]!i', function($m) use ($k){ 
            return $this->transformParam($m[1], $k);
        }, $v[0]);


        if(preg_match_all('!'.$sExpression.'!i', $sRequest, $aExpression__)){


            foreach($aExpression__ AS $k_ => $v_){
                foreach($v_ AS $kkk => $vvv){

                    if(!isset($aExpression_[1][$k_-1]))
                        $aExpression_[1][$k_-1] = null;

                    if($kkk>0)
                        $aExpression[] = array($aExpression_[1][$k_-1].'_'.$kkk, $vvv);
                    else
                        $aExpression[] = array($aExpression_[1][$k_-1], $vvv);

                }
            }

            unset($aExpression[0]);
            $iCount = count($aExpression__[0]);
            if($iCount>1){
                for($i=0;$i<$iCount;$i++){
                    if($i>0)
                        $sVars .= '&'.preg_replace_callback('!\[(.+?)\]!i', '[$1_'.$i.']', $v[1]);
                    else
                        $sVars = '&'.$v[1];                        
                }

            }else                
                $sVars = '&'.$v[1];

            foreach($aExpression AS $k => $v_){
                if(!isset($v['_'.$v_[0]]))
                    $v['_'.$v_[0]] = null;

                if(!is_array($v['_'.$v_[0]]))
                    $sVars = str_replace('['.$v_[0].']', $v_[1], $sVars);

                else {
                    $this->aRoutingParse = array($v['_'.$v_[0]]);
                    $sVars = $sVars.$this->parseUrl($v_[1]);

                }
            }                
            break;
        }
    }    

    return $sVars;
}

我认为这背后的意图是避免SQL注入攻击,但我不确定在那种情况下是否可行。

问题:通过这些JDBC方法可以进行SQL注入攻击吗? JDBC如何在幕后实现这一点?这会是AppScan报告的另一个误报吗?

1 个答案:

答案 0 :(得分:3)

我不确定bluemix-app-scan,但我在这里提供了解释。 (这是我基于以下测试和代码粘贴的假设

我运行了一个测试代码来检查(在 H2 DB 中) 值 testName 字符串:(select 'sqlInjection' from dual)

  1. 使用 createStatement (不安全)

    String query = "update TEST_TABLE set TEST_CHAR = " + testName + " where ID = 1";
    Statement statement = connection.createStatement();
    statement.executeUpdate(query);
    

    输出:数据库中的TEST_CHAR为 sqlInjection

  2. 使用 ResultSet createStatement (H2数据库中的安全)

    String query = "select * from TEST_TABLE where ID = 1";
    Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
    ResultSet executeQuery = statement.executeQuery(query);
    executeQuery.next();
    executeQuery.updateString("TEST_CHAR", testName);
    executeQuery.updateRow();
    

    输出:令人惊讶的是,数据库中的TEST_CHAR是(选择&#39; sqlInjection&#39;来自双重)

  3. 使用 PreparedStatement (安全)

    String query = "update TEST_TABLE set TEST_CHAR = ? where ID = 1";
    PreparedStatement statement = connection.prepareStatement(query);
    statement.setString(1, testName);
    statement.executeUpdate();
    

    输出预期 - 数据库中的TEST_CHAR为(选择&#39; sqlInjection&#39;来自双重)

  4. 使用 ResultSet prepareStatement (H2数据库中的安全)

    String query = "select * from TEST_TABLE where ID = 1";
    PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
    ResultSet uprs = statement.executeQuery();
    uprs.next();
    uprs.updateString("TEST_CHAR", testName);
    uprs.updateRow();
    

    输出预期 - 数据库中的TEST_CHAR为(选择&#39; sqlInjection&#39;来自双重)

  5. 返回问题:

      

    通过这些JDBC方法可以进行SQL注入攻击吗?

    可能。这取决于您正在使用的数据库驱动程序 如何?: SQL注入在我的结果集更新中失败的原因是因为H2数据库在调用PreparedStatement时内部使用ResultSet.updateRow()来更新行。

    public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
        StatementBuilder buff = new StatementBuilder("UPDATE ");
        ...
        buff.append(" SET ");
        appendColumnList(buff, true);
        ...
        appendKeyCondition(buff);
        PreparedStatement prep = conn.prepareStatement(buff.toString());
        ...
        for (int i = 0; i < columnCount; i++) {
           ...
            v.set(prep, j++);
        }
        setKey(prep, j, current);
        int count = prep.executeUpdate();
        ...
    }
    

    我不确定java中的所有数据库驱动程序是否使用updateRow()实现了preparedStatement方法。但是很明显,这是留给司机的,如果bluemix建议你在这里添加验证,我建议你遵循:)

      

    JDBC如何在幕后实现这一点?

    好吧,如上所示,这是特定于驱动程序的。但是,PreparedStatementhere的处理方式有一个很好的解释。

      

    这会是AppScan报告的另一个误报吗?

    我不认为这是误报(但在像H2 DB这样的情况下),但你永远不会知道所有数据库驱动程序是否安全地实现了这一点。

    编辑 -
    甚至PostgreSQL和MySQL都使用PreparedStatement来处理这个问题。

    public synchronized void updateRow() throws SQLException
    {
        ...
        updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
        ...  
        updateStatement.executeUpdate();
        ...
    }