如何在不使用参数的情况下使 sql 字符串在 vb.net 中安全

How to make sql string safe in vb.net without using parameters

提问人:Reza 提问时间:10/21/2020 最后编辑:Reza 更新时间:10/21/2020 访问量:485

问:

我正在做一个有问题的项目,我几乎通过使用修复了所有注入,有一种情况是构建sql字符串,然后加密并传递给,然后它将从中检索和解密以运行,显然不能在这种情况下使用参数,也无法重构为不传递sql, 唯一的选择是在构建 SQL 语句时尽可能避免注入,或者使用参数,然后获取生成的 SQL 语句并将其传递给视图状态。vb.netsql innjectionparametersviewStateviewStateviewState

参数值由字符串和数字混合而成。

例如

 sSQL = sSQL & " WHERE Name LIKE '" & lstBrowse.SelectedItem.Value & "%'"

 sSQL = sSQL & " WHERE State ='" & lstBrowse.SelectedItem.Value & "'"

 sSQL = sSQL & " WHERE OrganizationID=" & lstBrowse.SelectedItem.Value

如何在不使用参数的情况下做到这一点?我应该考虑和避免什么?

C# asp.net .NET vb.net SQL 注入

评论

3赞 Hursey 10/21/2020
那么,是否不能将参数化查询存储在视图状态中,然后在执行它时,在那里设置它的值?
2赞 jmcilhinney 10/21/2020
为什么不能在 SQL 代码中使用参数,同时将参数值添加到 ViewState?然后,解密后两者都有,因此您可以创建命令并添加参数。
0赞 Albert D. Kallal 10/21/2020
我认为发帖人没有意识到你可以构建 sql 字符串,而是动态地使用参数构建它们,从而获得参数安全的 sql。我在下面的帖子中解释了如何。真的公平吗?我认为可以将一个 sql 命令对象推入会话中,因为该对象包含您的 sql,保存您的参数,并且它只有一个对象。您甚至可以在该 sql 命令中保存连接对象。所以它相当不错。
0赞 Muhammad Aftab 10/21/2020
您必须通过 或 来执行此操作。否则,您必须创建自定义函数来删除可疑字符,但是当您已经为您构建了轮子时,不建议再次发明轮子。Stored ProcedureParameterized Query
0赞 Caius Jard 10/22/2020
在这种情况下,显然不能使用参数 - 在 SQL 无法参数化的极少数情况下,我向你保证这不是其中之一

答:

1赞 Albert D. Kallal 10/21/2020 #1

好的,我认为问题在于人们假设你不能有“可选”参数,或者你必须提前设置它们。(你没有)。

你可以列出这个:

dim strWhere  as string
strWhere = "(State =  @State)
cmdSQL.Parmaters.Add("@State",SqlDbType.NVarchar).Value = lstBrowse.SelectedItem.Value 

现在,我可以“即时”向 strWhere 添加更多条件,并且随着时间的推移,还可以将 parmaters 添加到 cmdSQL.Parmaters 集合中。

现在,让我们假设你在一些复杂的网格页面上,用户选择了一些东西,现在你需要把这个说法传递给另一个表单?

Well, it gets a wee bit messy to have:
Keep track of some field names
Keep track of some conditions (=, like, etc.)
Keep track of the parameters

但是你肯定可以把一个参数列表“串”在一起,以及一些也与该参数匹配的 sql。因此,没有必要使用 EXISTING 参数对 sql 进行硬编码。

现在,另一个问题是你建议不仅要构建一些动态 sql,而且你需要在会话中传递它(或者你想要这种能力)。

好吧,我只是构建一个非常短的代码的“团块”,将上述所有内容放入一个非常简短和简单的类中。然后将该类推入会话。

因此,将这部分代码放入项目中的一个标准代码模块中。

公共类 SqlParms

    Public cmdSQL As New SqlCommand("", GetCon)
    Public Where As String
    Private m_SQL As String

    Public Sub Add(FieldName As String, Cond As String, FieldValue As Object, Datatype As SqlDbType)
        Dim mycond As String
        Dim prex As String = "", sufx As String = ""
        If Left(Cond, 1) = "%" Then prex = "'%' + "
        If Right(Cond, 1) = "%" Then sufx = " + '%'"
        mycond = Replace(Cond, "%", "")

        If Where <> "" Then Where += " AND "
        Where += "(" & FieldName & " " & mycond & " " & prex & "@" & FieldName & sufx & ")"
        cmdSQL.Parameters.Add("@" & FieldName, Datatype).Value = FieldValue

    End Sub

    Public Property SQL As String
        Get
            Return m_SQL
        End Get
        Set(value As String)
            m_SQL = value & " WHERE " & Where
            cmdSQL.CommandText = m_SQL
        End Set
    End Property

End Class

那么,以上都是吗?允许您添加“字段”、“条件”,然后添加更多内容。 这很简单。

所以,现在在代码中,我可以去:

Dim MyParms As New SqlParms
Session("MyParms") = MyParms

现在,请注意,一旦我们将会话指向该类?然后我可以添加/修改当前代码位,而不必将 MyParms 汇集/保存/推回 - 它是一个对象,现在在会话中。

因此,让我们添加您的条件。

MyParms.Add("State", "=", lstBrowse.SelectedItem.Value, SqlDbType.NVarChar)

好的,所以上面的内容不仅被添加,而且在我们的会话中也被添加!!

好的,让我们假设我们有两个条件,第二个条件是 ContactID(数据库中的 PK 值 - 因此是整数数据类型)。 好的,我们现在开始:

MyParms.Add("OrganziationID", "=", lstBrowse.SelectedItem.Value, SqlDbType.Int)

或者也许

MyParms.Add("OrganziationID", "=", droplist1.SelectedValue, SqlDbType.Int)

MyParms.SQL = "SELECT * from tblHotels"

再次:注意运行以上 1 个,还是 2 个条件后?它在 session() 中。

好的,假设我们跳到了一个新的不同页面。现在我们需要填充一个网格。

代码将是:

请注意,下面非常非常小心,我们如何不使用新关键字!

    Dim MyParms As SqlParms = Session("MyParms")
    Dim rst As New DataTable
    Using MyParms.cmdSQL
        MyParms.cmdSQL.Connection.Open()
        rst.Load(MyParms.cmdSQL.ExecuteReader)
    End Using

    GridView1.DataSource = rst
    GridView1.DataBind()

因此,这个小小的“帮助程序”例程可以让你构建 where 子句。 它在 session() 中,现在您可以将所有这些东西传递/跳转到另一个页面。

人们经常没有意识到的另一件非常有趣的事情? 您始终可以向 sql 命令对象添加参数,并且尚未设置 sql 集。而且您甚至不需要连接集。

因此,您实际上可以转储上述“帮助程序”例程,然后创建一个 sql 命令对象,并将其推送到会话中。

不管以上什么?您不必对 sql 进行预硬编码。这些值可以而且将会成为参数安全(sql注入安全),如果您要添加“可选”参数,这甚至适用。唯一的诀窍是确保你也使用这些可选参数构建 sql。

所以,如果我这样说:

dim MyParms as new SqlParms
MyParms.Add("City", "Like%", "E", SqlDbType.NVarChar)
MyParms.SQL = "SELECT * from tblHotels"
Dim rst As New DataTable
    Using MyParms.cmdSQL
        MyParms.cmdSQL.Connection.Open()
        rst.Load(MyParms.cmdSQL.ExecuteReader)
    End Using

    GridView1.DataSource = rst
    GridView1.DataBind()

因此,上面的内容将用结果表填充我们的数据网格。

上面的 sql 现在是这样的:

SELECT * from tblHotels WHERE (City Like @City + '%')

现在因为我想要“喜欢”的支持?好吧,我无法在参数名称中添加/包含/包含百分比(我使用与前面带有@的字段名称相同的字段名称。

所以,我使用 %like(用于前缀通配符) 和 Like%(后缀)。然后,代码去除 % 并将其正确添加到 where 子句中,如上所述。

因此,您可以在“添加”更多条件时对字符串进行编码。但在所有情况下,使用参数不仅是一个好主意,而且也不是那么难做到。

我在该类中还使用了一个名为 GetCon 的公共函数,该函数仅返回一个连接对象,但您可以将该类中的“GetCon”替换为您自己的例程,该例程返回一个连接字符串,然后用于创建 sqlconneciton 对象。

此外,相当小,但我确实假设 sql 属性设置为 LAST,因为 sql 字符串随后与 where 子句组合在一起。如果添加更多参数,则需要再次设置 sql 属性。

综上所述: 您可以并且应该添加附加值作为参数。我看不出有很多理由不这样做。

如果您需要在一个页面中设置所有这些内容,然后跳转到另一个页面,则可以将这个可爱的助手例程推入会话。(甚至让当前页面允许一些额外的过滤。

评论

0赞 Reza 10/21/2020
感谢@albert花时间写一个全面的答案,我明白你在说什么,但不幸的是,我不允许重构代码以更改 sql 语句传递给 viewState 的方式
0赞 Albert D. Kallal 10/22/2020
您可以就地使用简单的数组或集合。如果不允许使用连接用户输入的 sql 字符串更改某些字符串会话 var,则无法对可能的 sql 注入方案负责。您可以使用单个 sqlcommand 对象来代替上述帮助程序例程,只需向其添加参数即可。真正好的是,你永远不必知道甚至决定你是否需要在值(对于字符串)、数字(没有引号)周围加引号。为了约会?您不必担心格式和引号(痛苦)。只是一个简单的直接 var 赋值,没有凌乱的串联。
0赞 Albert D. Kallal 10/22/2020
因此,创建一个 sql 命令对象来代替视图状态中的 sql 字符串。它具有可以与现有代码一起使用的 sql 字符串 (commandtext),并且它有一个内置的参数集合。哦,太好了,你不用担心日期格式或任何引号。即使对于日期时间变量中的日期值,您也可以将“赋值”为参数 - 无需担心格式错误,也无需再乱七八糟地考虑我是否有引号或是否添加引号。 它只是使用简单的 var 赋值清理代码。归根结底,上面的内容表明,即使是动态的 sql 代码也是可能的。