动态创建不对 SQL 注入开放的 SQL Where 子句

Creating SQL Where clause dynamically that is not open to SQL Injection

我编写了这段 VBA 代码,它根据用户选择的字段数和从 XL 电子表格中读取的值动态创建 SQL 查询。它基本上只是将 "FIELD_VARIABLE=VALUE_VARIABLE OR" 添加到 where 子句,然后在循环结束后删除最后的 OR 。

它适用于像我希望的那样添加的 N 个字段,但我担心的是安全性,因为我认为我可以像 ';DROP TABLE 项目或其他一些恶意代码一样放入电子表格中程序正在读取 FIELD_VARIABLES。在较小程度上,因为每次执行路径都不同时查询不同,这可能会减慢执行时间。

我正在考虑研究参数化查询或 T-SQL 来改进这一点。希望你们中的一个聪明人能在我浪费太多时间之前为我指明正确的方向。这是相关的 VBA 代码:

 '---loop through array of search fields and search values using the same index
 '---since the arrays sizes will always be the same and create where filters dynamically
          i = 1
          For i = LBound(sLookupFields) To UBound(sLookupFields)
                Set rngLookup = wsLookupSrc.cells(counter, lLookupCols(i))
            '---clear where from last iteration through loop
                SQLWhereDynamic = ""

                SQLWhereDynamic = SQLWhereDynamic & " p." & sLookupFields(i) & " = '" + CStr(rngLookup.Value) & "' OR"
          Next i
        '---remove extra ' OR'
          SQLWhereDynamic = Left(SQLWhereDynamic, (Len(SQLWhereDynamic) - 3))

          SQLValue = wsLookupSrc.cells(counter, lLookupCols(1)).Value

          SQLWhereDefault = "WHERE p.ClientId = " + CStr(iClientId) + ""
          SQLQuery = SQLSelect + SQLWhereDefault + " AND (" + SQLWhereDynamic + ");"

将 WHERE 子句中的字段名称作为参数(因此是动态的并且可以安全地注入)就像 WHERE 子句中的值一样是不可能的,我相信。然而...

以下是我的做法。假设您有一个 Excel 范围,其中包含所有可能的字段、为您要搜索的字段填充的搜索值以及数据类型(稍后在代码中使用)。下面的示例显示正在搜索的两个字段

Field           Value      DataType
Sequence                     131
CustomerID                   200
InvoiceNumber                200
OrderNumber                  200
InvoiceDate     8/14/2015      7
Item            DS2          200
Location                     200
ExportFile                   200
DateImported                   7
OnHold                        11

用户填写第 2 列。代码构建 sql 字符串

Sub MakeSQL()

    Dim aSql(1 To 4) As String
    Dim aWhere() As String
    Dim vaFields As Variant
    Dim lWhereCnt As Long
    Dim lCnt As Long, i As Long
    Dim cn As ADODB.Connection
    Dim cmd As ADODB.Command
    Dim pm As ADODB.Parameter

    'Skip number three until later
    aSql(1) = "SELECT *"
    aSql(2) = "FROM dbo.InvoiceLine"
    aSql(4) = "ORDER BY InvoiceNumber DESC;"

    'Grab all the search criteria
    vaFields = Sheet1.Range("A2:C11").Value
    'Set up the connection
    Set cn = New ADODB.Connection
    cn.Open sConn
    Set cmd = New ADODB.Command
    Set cmd.ActiveConnection = cn

    'Count how many criteria where filled in
    'You could Redim Preserve your aWhere() also
    On Error Resume Next
        lWhereCnt = Sheet1.Range("B2:B11").SpecialCells(xlCellTypeConstants).Count
    On Error GoTo 0

    'If there's at least one
    If lWhereCnt >= 1 Then
        ReDim aWhere(1 To lWhereCnt)

        'Fill in an array and create parameters
        For i = LBound(vaFields, 1) To UBound(vaFields, 1)
            If Len(vaFields(i, 2)) > 0 Then
                lCnt = lCnt + 1
                'Put in the place holder
                aWhere(lCnt) = vaFields(i, 1) & "=?"
                'column 3 holds the data type
                Set pm = cmd.CreateParameter(vaFields(i, 1) & "_p", vaFields(i, 3), adParamInput)
                pm.Value = vaFields(i, 2)
                'Variable length data types (I only use varchar, you may use more)
                'must have a size specified
                If vaFields(i, 3) = adVarChar Then pm.Size = Len(vaFields(i, 2))
                cmd.Parameters.Append pm
            End If
        Next i
        'Fill in the "where" section of your sql statement
        aSql(3) = "WHERE " & Join(aWhere, " OR ")
    End If

    cmd.CommandText = Join(aSql, Space(1))

    'Change this line to actually execute something
    Debug.Print cmd.CommandText
    For i = 0 To cmd.Parameters.Count - 1
        Debug.Print , cmd.Parameters(i).Name, cmd.Parameters(i).Value
    Next i


    cn.Close
    Set cn = Nothing

End Sub

对于此示例,字符串显示为

SELECT * FROM dbo.InvoiceLine WHERE InvoiceDate=? OR Item=? ORDER BY InvoiceNumber DESC;
          InvoiceDate_p 8/14/2015 
          Item_p        DS2