动态创建不对 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
我编写了这段 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