当 table 名称和 where 子句是变量时防止 SQL 注入
Prevent SQL Injection when the table name and where clause are variables
我有件事需要你的帮助。
目前,我已经使用 ado.net 构建了一个 asp.net 应用程序。我正在使用 CommandText 构建动态查询,因此它具有 SQL 注入漏洞。
我的 CommandText 是这样的
String.Format("SELECT COUNT(*) FROM {0} {1}", tableName, whereClause)
TableName和whereClause是开发者传入的。如您所见,我不能在这里使用 SQLParameters,因为我需要传递整个 tableName 和 whereClause 而不仅仅是参数值。
我防止 SQL 注入的解决方案是使用 BlackList 检查 TableName 和 whereClause 来找出恶意字符串,但我不知道在这种情况下这是最好的方法,不是吗。如果有人可以帮助我在哪里可以找到 BlackList 参考资料或图书馆。
在不知道更多细节的情况下,您有几种选择可以避免 SQL 注入攻击或至少将可能造成的损害降至最低:
- 白名单比黑名单更安全:想想你是否真的需要访问除黑名单之外的所有 table。如果有人在稍后添加 tables,他或她可能会忘记将它们添加到备选列表中。
- 也许您可以限制对 table 的特定子集的访问。理想情况下,这些 table 遵循通用的命名方案,因此可以根据该方案验证 table 名称。如果没有命名方案,您还可以在程序或应用程序配置中添加一个可以访问的 table 列表,以便您可以检查此列表中是否包含 table 名称。如果将列表保存在配置文件中,则无需再次编译应用程序即可展开列表。
- 如果您不能将 table 名称列入白名单,您至少可以通过查询
sys.tables
系统 table 检查提供的 table 名称是否存在于数据库中(在 SQL 服务器中,其他 DBMS 可能有类似的 tables)。在此查询中,您可以使用参数以保证安全。
- 对于 SQL 服务器,您应该将 table 名称放在方括号中 (
SELECT COUNT(*) FROM [" + tableName + "]"
)。方括号用于分隔标识符(另见 link)。为了使其工作,您必须检查 tableName
变量不包含右方括号。如果 tableName
变量可能包含模式标识符(例如 dbo.MyTable
,您必须先拆分这些部分,然后添加方括号 ([dbo].[MyTable]
),因为这些是单独的标识符(一个用于架构,一个用于 table 名称)。
- 使用正则表达式或类似检查非常仔细地验证变量的内容。这对于 table 名称很容易,但对于
WHERE
子句非常困难,因为您基本上必须解析 SQL WHERE 子句并断言不包含危险代码。
- 最难的部分是检查
WHERE
子句。同样在这方面,如果您可以限制用户的选项并将可能的 WHERE
子句列入白名单,那将是最好的。这意味着用户可以从程序知道或根据用户输入构建的一系列 WHERE
子句中进行选择。这些已知的 WHERE
子句可以包含参数,因此可以安全地抵御 SQL 注入攻击。如果您不能将 WHERE
子句列入白名单,则必须解析 WHERE
子句才能确定某个请求是否危险。这将需要很大的努力(如果您找不到可以为您做这件事的库),所以我会尝试将尽可能多的动态查询部分列入白名单。
- 为了减少成功攻击的损失,您应该运行在权限非常有限的特定帐户下进行查询。您必须将另一个连接字符串添加到使用此帐户的配置文件中,并使用有限的连接字符串创建连接。在 SQL 服务器中,您可以将此帐户能够访问的 table 移动到特定架构并限制此帐户对此架构的访问。
- 很好地保护您的服务免受未经授权的访问,以便只有受信任的开发人员才能访问它。您可以通过在基础架构中使用一些组件(防火墙、传输级安全性等)以及添加强大的用户身份验证机制来实现这一点。
- 将每个请求记录到服务中,以便识别用户和计算机。通知用户有关此日志记录机制的信息,以便他们知道如果出现任何问题,他们将被识别。
一些最后的想法:即使为开发者提供这样一种查询数据的开放方式看起来很容易,但想想是否真的有必要。一种可能的选择是没有这种开放访问,而是在配置文件中配置其他开发人员需要的查询。每个查询都有一个标识符,查询文本存储在文件中,因此事先已知。尽管如此,您仍然可以在部署服务后添加新查询或更改现有查询。您可以在调用者指定的查询中允许参数(可能是编号参数方案,如 p1、p2 等)。
正如您从上面的列表中看到的那样,一旦您允许此开放访问,就很难(在某些地区几乎不可能)锁定服务并避免各种 SQL 注入攻击.使用上一段中描述的方法,您会失去一些灵活性,但您不必再担心 SQL 注入攻击。
我有件事需要你的帮助。 目前,我已经使用 ado.net 构建了一个 asp.net 应用程序。我正在使用 CommandText 构建动态查询,因此它具有 SQL 注入漏洞。 我的 CommandText 是这样的
String.Format("SELECT COUNT(*) FROM {0} {1}", tableName, whereClause)
TableName和whereClause是开发者传入的。如您所见,我不能在这里使用 SQLParameters,因为我需要传递整个 tableName 和 whereClause 而不仅仅是参数值。
我防止 SQL 注入的解决方案是使用 BlackList 检查 TableName 和 whereClause 来找出恶意字符串,但我不知道在这种情况下这是最好的方法,不是吗。如果有人可以帮助我在哪里可以找到 BlackList 参考资料或图书馆。
在不知道更多细节的情况下,您有几种选择可以避免 SQL 注入攻击或至少将可能造成的损害降至最低:
- 白名单比黑名单更安全:想想你是否真的需要访问除黑名单之外的所有 table。如果有人在稍后添加 tables,他或她可能会忘记将它们添加到备选列表中。
- 也许您可以限制对 table 的特定子集的访问。理想情况下,这些 table 遵循通用的命名方案,因此可以根据该方案验证 table 名称。如果没有命名方案,您还可以在程序或应用程序配置中添加一个可以访问的 table 列表,以便您可以检查此列表中是否包含 table 名称。如果将列表保存在配置文件中,则无需再次编译应用程序即可展开列表。
- 如果您不能将 table 名称列入白名单,您至少可以通过查询
sys.tables
系统 table 检查提供的 table 名称是否存在于数据库中(在 SQL 服务器中,其他 DBMS 可能有类似的 tables)。在此查询中,您可以使用参数以保证安全。 - 对于 SQL 服务器,您应该将 table 名称放在方括号中 (
SELECT COUNT(*) FROM [" + tableName + "]"
)。方括号用于分隔标识符(另见 link)。为了使其工作,您必须检查tableName
变量不包含右方括号。如果tableName
变量可能包含模式标识符(例如dbo.MyTable
,您必须先拆分这些部分,然后添加方括号 ([dbo].[MyTable]
),因为这些是单独的标识符(一个用于架构,一个用于 table 名称)。 - 使用正则表达式或类似检查非常仔细地验证变量的内容。这对于 table 名称很容易,但对于
WHERE
子句非常困难,因为您基本上必须解析 SQL WHERE 子句并断言不包含危险代码。 - 最难的部分是检查
WHERE
子句。同样在这方面,如果您可以限制用户的选项并将可能的WHERE
子句列入白名单,那将是最好的。这意味着用户可以从程序知道或根据用户输入构建的一系列WHERE
子句中进行选择。这些已知的WHERE
子句可以包含参数,因此可以安全地抵御 SQL 注入攻击。如果您不能将WHERE
子句列入白名单,则必须解析WHERE
子句才能确定某个请求是否危险。这将需要很大的努力(如果您找不到可以为您做这件事的库),所以我会尝试将尽可能多的动态查询部分列入白名单。 - 为了减少成功攻击的损失,您应该运行在权限非常有限的特定帐户下进行查询。您必须将另一个连接字符串添加到使用此帐户的配置文件中,并使用有限的连接字符串创建连接。在 SQL 服务器中,您可以将此帐户能够访问的 table 移动到特定架构并限制此帐户对此架构的访问。
- 很好地保护您的服务免受未经授权的访问,以便只有受信任的开发人员才能访问它。您可以通过在基础架构中使用一些组件(防火墙、传输级安全性等)以及添加强大的用户身份验证机制来实现这一点。
- 将每个请求记录到服务中,以便识别用户和计算机。通知用户有关此日志记录机制的信息,以便他们知道如果出现任何问题,他们将被识别。
一些最后的想法:即使为开发者提供这样一种查询数据的开放方式看起来很容易,但想想是否真的有必要。一种可能的选择是没有这种开放访问,而是在配置文件中配置其他开发人员需要的查询。每个查询都有一个标识符,查询文本存储在文件中,因此事先已知。尽管如此,您仍然可以在部署服务后添加新查询或更改现有查询。您可以在调用者指定的查询中允许参数(可能是编号参数方案,如 p1、p2 等)。
正如您从上面的列表中看到的那样,一旦您允许此开放访问,就很难(在某些地区几乎不可能)锁定服务并避免各种 SQL 注入攻击.使用上一段中描述的方法,您会失去一些灵活性,但您不必再担心 SQL 注入攻击。