如何生成一个安全的SQL命令字符串,防止SQL注入?

How to generate a SQL Command string that is safe from SQL Injection?

提问人:RHarris 提问时间:1/20/2023 最后编辑:RHarris 更新时间:2/7/2023 访问量:602

问:

我有一种情况,我必须从用户那里获取输入,创建一个 SQL 命令并将该命令发送到将执行 SQL 的服务。该服务只允许 SQL 字符串 -- 不允许其他参数;所以我被迫在我这边创建整个 SQL 语句。

我对数据库本身没有任何访问权限 - 只有位于它之上的服务。

我意识到以下情况不安全:

var sql = $"SELECT * FROM tablename WHERE name = '{incomingdata.searchName}'";

但是,如果我使用参数生成 SQL,这不会受到 SQL 注入的影响?

var sql = $@"
DECLARE @Name varchar(50);
SET @Name = '{incomingdata.searchName}';
SELECT * FROM tablename WHERE name = @Name";
C# SQL-Server SQL注入

评论

3赞 Thom A 1/20/2023
不,不会。为什么不能参数化?C# 完全支持参数化查询。这个“服务”是什么?
3赞 Jonesopolis 1/20/2023
可能值得与拥有该服务的人交谈
1赞 Ralf 1/20/2023
修复服务。不要试图用魔法来处理损坏的服务。
1赞 Jeroen Mostert 1/20/2023
如果你绝对别无选择,那就去做吧。麻烦的是,你必须确保在任何地方都做这种事情,并且转义和格式可能会有所不同,这取决于你是否传递字符串、一个、一个标识符、一个......一次失误,你就遇到了安全问题。参数的存在是有充分理由的。'{incomingdata.searchName.Replace("'", "''")}'nullDateTime
1赞 David Browne - Microsoft 1/20/2023
此外,还必须通过类型安全或其他方式保证在单引号外注入的任何字符串都是安全的。因此,标识符必须按照 QUOTENAME 进行转义,并将数字键入为代码中的数字,或验证为仅包含数字和 '.'。

答:

0赞 sheldonm301 1/20/2023 #1

披露:我的背景是C++,Java和TypeScript / JavaScript,而不是C#

这是否更合适:

SqlCommand sql = new SqlCommand("SELECT * FROM tablename WHERE name = @Name");
sql.Parameters.Add("@Name", SqlDbType.VarChar, 50).Value = incomingdata.searchName);

在用户数据进入此阶段之前,使用受信任的包清理用户数据也可能有所帮助。

评论

0赞 Thom A 1/20/2023
这是参数化,OP 说他们不能使用,出于某种原因。
0赞 RHarris 1/20/2023
如果我真的能够连接到有问题的数据库,这将非常有用。但是,正如我所指出的,所有者没有提供连接到数据库的方法。
-2赞 Sarah 1/20/2023 #2

你走在正确的路线上。您永远不想使用可以更改的变量。相反,您需要使用 SQL 参数。您可以添加如下所示的 SQL 参数:

command.Parameters.Add(new SqlParameter("@Name", "incomingdata.searchName"));

然后在查询中引用它,如下所示:

SELECT * 
FROM tablename 
WHERE name = @Name";

完成此操作后,当用户尝试更改变量的值时,查询将不返回任何结果。应该说,这种方式确实会导致 SQL 属性假定 C# 变量的类型。如果要指定属性的类型与变量类型不同,还有其他方法可以执行此操作。这是一个有用的资源 https://jonathancrozier.com/blog/preventing-sql-injection-in-c-sharp-applications

评论

0赞 Dale K 1/20/2023
OP 已经说过他们不能使用参数,另一个答案已经引用了参数。
1赞 Hogan 1/20/2023 #3

这不是一个理想的情况。我会尝试寻找一种参数化方法来解决这个问题,否则我将测试输入,并且在测试失败的任何情况下根本不允许查询并要求用户重新输入。

执行以下测试:

  • 输入长度小于最大名称大小(25 个字符?
  • 所有输入字符均按字母表排列
  • 没有保留的 SQL 字(很容易通过谷歌搜索找到)

如果输入没有通过这些测试中的任何一个,你应该没问题。 不要试图清理输入——这对于国际字符集来说可能很难/不可能做到。

评论

0赞 Thom A 1/20/2023
哎呀,如果他们有国际字符集,这就会成为一场噩梦,以确保符号字符只在需要时用于列。 对于性能来说会很糟糕。WHERE VarcharColumn = N'{Injected String}'
0赞 Hogan 1/20/2023
@Larnu -- 我不明白 -- 我说过要确保字母表的一部分 -- 这在国际上是可以兼容的。
0赞 Thom A 1/20/2023
WHERE VarcharColumn = N'{Injected String}'@Hogan,无法SARGable。这将导致列的隐式转换。因此,如果 OP 具有国际字符,则需要注入,但不能总是注入 an,因为这种假设可能对性能非常不利。nvarcharnvarchar
0赞 Hogan 1/20/2023
@Larnu -- 好吧,你说的是索引 -- 这个问题是关于注入的,所以你正在改变主题 -- 但无论如何,在您的示例中,VarcharColumn 将是 NVARCHAR 和 SARGable 就好了。
0赞 Hogan 1/20/2023
@Larnu -- 一种常见的注入攻击是使用 i18n 字符集来绕过扫描程序 -- 进入 SQL 语句的数据无关紧要(因此速度不是问题),因为它们已经破坏了数据库。