如何在 C# 中为 SqlServer 转义简单的 SQL 查询

Posted

技术标签:

【中文标题】如何在 C# 中为 SqlServer 转义简单的 SQL 查询【英文标题】:How to escape simple SQL queries in C# for SqlServer 【发布时间】:2011-01-25 02:39:41 【问题描述】:

我使用的 API 需要一个 SQL 字符串。我接受用户输入,对其进行转义并将其传递给 API。用户输入非常简单。它要求列值。像这样:

string name = userInput.Value;

然后我构造一个SQL查询:

string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '0'",
                           name.replace("'", "''"));

这足够安全吗?如果不是,是否有一个简单的库函数可以使列值安全:

string sql = string.Format("SELECT * FROM SOME_TABLE WHERE Name = '0'",
                           SqlSafeColumnValue(name));

API 使用 SQLServer 作为数据库。

【问题讨论】:

参数化它!用于“SQL 注入”的 SO 的 Google search 编辑:响应 Seva Alekseyev,SO answer with character 8 injection 需要使用参数。 @SLaks,显然 API 不允许这样做。也许他需要一个新的 API。 @sri 如果你解释为什么你不能/不会使用参数,你会得到更好的答案。 有没有办法用列表参数化查询? SELECT [Name], [Value] FROM [SomeTable] WHERE [Name] IN (@ListOfNames) 我的意思是 var listParNames = listNames.Select(name => var pname = string.Format("@n0", cmd.Parameters.Count); cmd.Parameters.AddWithValue(pname, name); return pname; ); cmd.CommandText = string.Format("SELECT [Name], [Value] FROM [SomeTable] WHERE [Name] in (0)", string.Join(",", listParNames.ToArray())); 更像是直接 cmd.Parameters.AddWithValue("@ListOfNames", listNames.ToArray()); 【参考方案1】:

由于不能使用 SqlParameter,只需将字符串文字中的 ' 替换为 '' (这是两个单引号,而不是一个双引号)。就是这样。

对于可能投反对票的人:重新阅读问题的第一行。 “使用参数”也是我的直觉。

编辑:是的,我知道 SQL 注入攻击。如果您认为此引用容易受到这些人的影响,请提供一个有效的反例。我认为不是。

【讨论】:

罗伯特后面有一点''。这就是如何。如果引用得当,鲍比的非正统名字将完整地存储在数据库中。 当然...字符 8 注入:***.com/questions/1800013/… Seva Alekseyev 非常感谢您阅读我的问题并尝试回答它。感谢您没有向我宣讲“最佳实践”。谢谢你没有试图“教育我”真的,谢谢! 嗯,作为记录,我仍然认为参数是要走的路:) 但是,我从事这项业务的时间已经足够长,可以了解您的设计存在限制。去过那里,做到了。 @Seva 虽然每个人都知道上面的代码会受到注入,但有时您会被绑定到一个不允许您遵循最佳实践的 API。程序员经常担心不在特定任务关注范围内的事情。对具体问题的好回答。【参考方案2】:

我使用动态 sql(我可以听到行刑队装载步枪的声音)来实现搜索功能,但每当用户搜索姓氏为“O'Reilly”的人时,它就会中断。

我设法找到了解决方法(阅读“hack”):

在sql中创建了一个标量值函数,将一个单引号替换为两个单引号,有效地转义了有问题的单引号,所以 “......姓氏像'%O'Reilly%'并且......” 变成 “...姓氏像 '%O''Reilly%' 和...”

每当我怀疑字段可能包含单引号字符时,都会从 sql 中调用此函数,即:名字、姓氏。

CREATE FUNCTION [dbo].[fnEscapeSingleQuote]
    (@StringToCheck NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
    DECLARE @Result NVARCHAR(MAX)
    SELECT @Result = REPLACE(@StringToCheck, CHAR(39), CHAR(39) + CHAR(39))
    RETURN @Result
END

不是很优雅也不是很高效,但它在你紧要关头时可以工作。

【讨论】:

【参考方案3】:

当需要在短时间内解决大量 ad hoc sql 中的问题时,可能希望将 ' 替换为 ' 而不是参数化。

【讨论】:

【参考方案4】:

SqlCommand 和实体框架使用exec sp_executesql...

因此,大概可以使用您自己的转义模式来替代原始字符串。使用 SqlCommand,您在技术上使用参数化查询,但您绕过了底层 SQL 代码的 ADO.Net 抽象。

因此,虽然您的代码不会阻止 SQL 注入,但最终的答案是 sp_executesql 而不是 SqlCommand。

话虽如此,我确信生成一个使用 sp_executesql 的防 SQL 注入字符串有特殊的处理要求。

见:How to return values from a dynamic SQL Stored Procedure to the Entity Framework?

【讨论】:

【参考方案5】:

简单:

const string sql = "SELECT * FROM SOME_TABLE WHERE Name = @name";

并添加带有值的@name 参数:

cmd.CommandText = sql;
cmd.Parameters.AddWithValue("@name", name);

【讨论】:

他仍然需要从命令对象中获取实际的 SQL 语句以传递给 API。 他明确声明他正在使用一个接受 SQL 字符串的 API。他没有使用 ADO.Net。 @MHollis 完全正确;这是他们需要解决的第一个问题。抱歉,没有参数没有可取的方法。人们对参数大喊大叫是有原因的。【参考方案6】:

如果您需要为 MSSQL 查询转义字符串,请尝试以下操作:

System.Security.SecurityElement.Escape(Value)

【讨论】:

抱歉,Kevin 但 Escape 函数只转义 XML 而不是 SQL (msdn.microsoft.com/en-us/library/…)

以上是关于如何在 C# 中为 SqlServer 转义简单的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# (.NET 4.5) 中为 HttpClient.GetAsync(URI) 创建回调?

如何在 php 中为 tsql 字符串转义单引号(ms sql)

在 C# 中为 WCF 服务反序列化简单的 XML 数组对象

C# 如何去掉string中所有转义字符(特殊符号)?

如何在 EF6.1 中为一个简单的操作阻塞一个表而没有死锁?

如何使用 C# 转义 XPath 引号“”