在不使用 QUOTENAME 的情况下正确转义 SQL Server 中的分隔标识符
除了将标识符(表、视图、列)用双引号括起来以及标识符名称中出现的“双引号”之外,代码还必须执行其他操作吗?将不胜感激。
我继承了一个具有自定义对象关系映射 (ORM) 系统的代码库。 SQL 无法在应用程序中编写,但 ORM 最终仍必须生成 SQL 并将其发送到 SQL Server。所有标识符都用双引号引起来。
string QuoteName(string identifier)
{
return "\"" + identifier.Replace("\"", "\"\"") + "\"";
}
如果我在 SQL 中构建这个动态 SQL,我会使用内置的 SQL Server QUOTENAME 函数:
declare @identifier nvarchar(128);
set @identifier = N'Client"; DROP TABLE [dbo].Client; --';
declare @delimitedIdentifier nvarchar(258);
set @delimitedIdentifier = QUOTENAME(@identifier, '"');
print @delimitedIdentifier;
-- "Client""; DROP TABLE [dbo].Client; --"
我还没有找到任何关于如何转义带引号的标识符的明确文档SQL 服务器。我找到了分隔标识符(数据库引擎)并且我还看到了这个关于清理的 stackoverflow 问题。
如果必须调用 QUOTENAME 函数只是为了引用标识符,那么 SQL Server 的流量就很大,这是不必要的。
ORM 似乎在 SQL 注入方面经过了深思熟虑。它是用 C# 编写的,早于 nHibernate 端口和实体框架等。所有用户输入都是使用 ADO.NET SqlParameter 对象发送的,它只是我在这个问题中关心的标识符名称。这需要在 SQL Server 2005 和 2008 上运行。
更新于 2010-03-31
虽然应用程序不应该允许用户在查询中输入标识符名称,但 ORM 通过它具有的 ORM 样式的查询语法来实现读取和自定义查询。我试图最终阻止所有可能的 SQL 注入攻击的是 ORM,因为与所有应用程序代码相比,它非常小且易于验证。
查询接口的一个简单示例:
session.Query(new TableReference("Client")
.Restrict(new FieldReference("city") == "Springfield")
.DropAllBut(new FieldReference("first_name"));
ADO.NET 发送此查询:
exec sp_executesql N'SELECT "T1"."first_name"
FROM "dbo"."Client" AS "T1"
WHERE "T1"."city" = @p1;',
N'@p1 nvarchar(30)',
N'Springfield';
也许这有助于思考类似的内容在 nHibernate 查询语言 (HQL) 中的外观:
using (ISession session = NHibernateHelper.OpenSession())
{
Client client = session
.CreateCriteria(typeof(Client)) \\ <-- TableReference in example above
.Add(Restrictions.Eq("city", "Springfield")) \\ <-- FieldReference above
.UniqueResult<Client>();
return client;
}
也许我应该看看 nHibernate 如何保护输入。
Is there anything else that the code must do to sanitize identifiers (table, view, column) other than to wrap them in double quotation marks and "double up" double quotation marks present in the identifier name? References would be appreciated.
I have inherited a code base that has a custom object-relational mapping (ORM) system. SQL cannot be written in the application but the ORM must still eventually generate the SQL to send to the SQL Server. All identifiers are quoted with double quotation marks.
string QuoteName(string identifier)
{
return "\"" + identifier.Replace("\"", "\"\"") + "\"";
}
If I were building this dynamic SQL in SQL, I would use the built-in SQL Server QUOTENAME function:
declare @identifier nvarchar(128);
set @identifier = N'Client"; DROP TABLE [dbo].Client; --';
declare @delimitedIdentifier nvarchar(258);
set @delimitedIdentifier = QUOTENAME(@identifier, '"');
print @delimitedIdentifier;
-- "Client""; DROP TABLE [dbo].Client; --"
I have not found any definitive documentation about how to escape quoted identifiers in SQL Server. I have found Delimited Identifiers (Database Engine) and I also saw this stackoverflow question about sanitizing.
If it were to have to call the QUOTENAME function just to quote the identifiers that is a lot of traffic to SQL Server that should not be needed.
The ORM seems to be pretty well thought out with regards to SQL Injection. It is in C# and predates the nHibernate port and Entity Framework etc. All user input is sent using ADO.NET SqlParameter objects, it is just the identifier names that I am concerned about in this question. This needs to work on SQL Server 2005 and 2008.
Update on 2010-03-31
While the application is not supposed to allow user-input for identifier names in queries, the ORM does via the query syntax that it has for both ORM-style reads and custom queries. It is the ORM that I am trying to ultimately prevent all possible SQL Injection attacks as that is very small and easy to verify as opposed to all the application code.
A simple example of the query interface:
session.Query(new TableReference("Client")
.Restrict(new FieldReference("city") == "Springfield")
.DropAllBut(new FieldReference("first_name"));
ADO.NET sends over this query:
exec sp_executesql N'SELECT "T1"."first_name"
FROM "dbo"."Client" AS "T1"
WHERE "T1"."city" = @p1;',
N'@p1 nvarchar(30)',
N'Springfield';
Perhaps it would help to think about how something similar this might look in nHibernate Query Language (HQL):
using (ISession session = NHibernateHelper.OpenSession())
{
Client client = session
.CreateCriteria(typeof(Client)) \\ <-- TableReference in example above
.Add(Restrictions.Eq("city", "Springfield")) \\ <-- FieldReference above
.UniqueResult<Client>();
return client;
}
Maybe I should look and see how nHibernate protects the input.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的
QuoteName
函数需要检查长度,因为 T-SQL QUOTENAME 函数指定了它返回的最大长度。使用您的示例:如果
QuoteName(identifier)
长度超过 258 个字符,则在分配给@delimitedIdentifier
时,它将被静默截断。发生这种情况时,@delimitedIdentifier
可能会被不正确地转义。有一篇 MSDN 文章 由“Microsoft 安全软件开发人员”Bala Neerumalla 撰写,更深入地解释了该主题。该文章还包含我发现的最接近“有关如何在 SQL Server 中转义带引号的标识符的权威文档”的内容:
这是我当前使用的 C# 代码:
Your
QuoteName
function needs to check the length, because the T-SQL QUOTENAME function specifies the maximum length it returns. Using your example:If
QuoteName(identifier)
is longer than 258 characters, it will be silently truncated when assigned to@delimitedIdentifier
. When that happens, you open up the possibility for@delimitedIdentifier
to be escaped improperly.There is an MSDN article by Bala Neerumalla, a "security software developer at Microsoft", that explains the topic in more depth. The article also contains the closest thing I have found to "definitive documentation about how to escape quoted identifiers in SQL Server":
This is the C# code I am currently using:
不能只使用 [ 和 ] 分隔符而不是引号(单引号或双引号)吗?
标识符永远不应该真正包含任何引号(除非你比现在更不幸),所以你删除了名称等中引号的正常使用因素
编辑:
但是如果对 ORM 的调用已经参数化,你不需要担心它,不是吗?使用 [ 和 ] 消除了 C# 字符串中复杂转义的需要
Can you not just use [ and ] delimiters instead of quotes (single or double)?
Identifiers should never really contain any quotes (unless you're more unlucky than now) so you remove the normal use factor of quotes in names etc
Edit:
But if the calls to the ORM are already parameterised, you don't need to worry about it, no? Using [ and ] removes the need for complex escaping in c# strings