当查询几乎完全可配置时限制 SQL 注入
Limiting SQL Injection when query is almost entirely configurable
我需要从 Web 应用程序执行 SQL 查询的预定转储。最初它是一个完整的 table(只有 table 名称是可配置的),但后来增加了一个可配置的 WHERE 子句,以及列的子集。
现在需要的可配置选项是:
- 列
- table 姓名
- where 子句
此时,它可能只是整个查询,对吧?!
我知道 SQLi 可以通过 java.sql.PreparedStatement
有所缓解,但据我所知,这依赖于在编译时了解列和数据类型。
可配置项将不会暴露给最终用户。它们将位于 WEB-INF/classes
内的属性文件中,因此我在这里捍卫的用户是系统管理员,他们并不像他们认为的那样优秀。
我是不是过于谨慎了?
如果不出意外,如果 WHERE 子句是 Robert'); DROP TABLE students;--
,java.sql.PreparedStatement
可以防止执行多个查询吗?
"It all depends".
如果您有一个应用程序,用户可以在其中输入 where 子句作为自由文本,那么是的,他们可以构造 SQL 注入攻击。他们还可以通过选择巨大的笛卡尔连接来让您的服务器停止运行。
您可以创建一个可视化查询生成器 - 使用模式元数据显示 table 的列表,一旦选择了 table 列,并且对每一列进行有效比较。然后,您可以将查询构造为参数化查询,并将人工输入限制为比较值,然后您可以将这些值用作参数。
虽然工作量很大,而且在任何规模的大多数生产系统中,让用户运行这种查询通常不是特别有用...
准备好的语句不会为您处理这个问题。使用准备好的语句,您只能安全地将 参数 添加到查询中,而不是 table 名称、列名称或整个 where 子句。
特别是后者,如果没有任何约束,几乎不可能防止注入。列和 table 名称参数可以根据静态定义或基于您的数据库结构动态定义的有效值列表进行检查。您可以对 where 参数进行一些基本的正则表达式检查,但这只会真正帮助防止明显的 SQL 注入。
凭借您打算以 SELECT 的形式提供的灵活性,您可以从哪里查询:
SELECT mycolumn FROM mytable WHERE id = 1 AND 'username' in (SELECT username FROM users)
您可以看看 JOOQ 之类的东西来提供安全的动态查询构建,同时仍然能够限制允许用户查询的内容。
以一种或另一种方式限制您的用户是这里的关键。不这样做意味着您不仅要担心 SQL 注入,还要担心性能问题。例如,为他们提供可视化(拖放式)查询构建器。
允许用户执行任意查询是不安全的。这就是您在 Equifax 看到的那种东西。你不想允许它。
准备好的语句无助于使 SQL 表达式安全。在准备好的语句中使用参数有助于使 values 安全。您只能在通常放置常量值的地方使用参数,例如数字、带引号的字符串或带引号的日期。
最简单的解决方案是不允许按需进行任意查询或表达式。
相反,允许用户提交他们的自定义查询以供审核。
查询由人工审核, 可以授权用户(或其他用户)运行 存储查询。如果你认为你可以开发某种自动验证器,欢迎来访,但恕我直言,这肯定比让合格的数据库管理员审查它需要做更多的工作。
随后,允许用户按需运行存储的查询,但只能通过其 ID。
这是另一个替代方案:想要运行 自定义查询的用户可以申请获得数据库的副本,以托管在他们自己的计算机上。他们将获得他们有权查看的数据子集的转储。然后,如果他们 运行 查询垃圾数据,或融化他们的计算机,那是他们的事。
我需要从 Web 应用程序执行 SQL 查询的预定转储。最初它是一个完整的 table(只有 table 名称是可配置的),但后来增加了一个可配置的 WHERE 子句,以及列的子集。
现在需要的可配置选项是:
- 列
- table 姓名
- where 子句
此时,它可能只是整个查询,对吧?!
我知道 SQLi 可以通过 java.sql.PreparedStatement
有所缓解,但据我所知,这依赖于在编译时了解列和数据类型。
可配置项将不会暴露给最终用户。它们将位于 WEB-INF/classes
内的属性文件中,因此我在这里捍卫的用户是系统管理员,他们并不像他们认为的那样优秀。
我是不是过于谨慎了?
如果不出意外,如果 WHERE 子句是 Robert'); DROP TABLE students;--
,java.sql.PreparedStatement
可以防止执行多个查询吗?
"It all depends".
如果您有一个应用程序,用户可以在其中输入 where 子句作为自由文本,那么是的,他们可以构造 SQL 注入攻击。他们还可以通过选择巨大的笛卡尔连接来让您的服务器停止运行。
您可以创建一个可视化查询生成器 - 使用模式元数据显示 table 的列表,一旦选择了 table 列,并且对每一列进行有效比较。然后,您可以将查询构造为参数化查询,并将人工输入限制为比较值,然后您可以将这些值用作参数。
虽然工作量很大,而且在任何规模的大多数生产系统中,让用户运行这种查询通常不是特别有用...
准备好的语句不会为您处理这个问题。使用准备好的语句,您只能安全地将 参数 添加到查询中,而不是 table 名称、列名称或整个 where 子句。
特别是后者,如果没有任何约束,几乎不可能防止注入。列和 table 名称参数可以根据静态定义或基于您的数据库结构动态定义的有效值列表进行检查。您可以对 where 参数进行一些基本的正则表达式检查,但这只会真正帮助防止明显的 SQL 注入。 凭借您打算以 SELECT 的形式提供的灵活性,您可以从哪里查询:
SELECT mycolumn FROM mytable WHERE id = 1 AND 'username' in (SELECT username FROM users)
您可以看看 JOOQ 之类的东西来提供安全的动态查询构建,同时仍然能够限制允许用户查询的内容。 以一种或另一种方式限制您的用户是这里的关键。不这样做意味着您不仅要担心 SQL 注入,还要担心性能问题。例如,为他们提供可视化(拖放式)查询构建器。
允许用户执行任意查询是不安全的。这就是您在 Equifax 看到的那种东西。你不想允许它。
准备好的语句无助于使 SQL 表达式安全。在准备好的语句中使用参数有助于使 values 安全。您只能在通常放置常量值的地方使用参数,例如数字、带引号的字符串或带引号的日期。
最简单的解决方案是不允许按需进行任意查询或表达式。
相反,允许用户提交他们的自定义查询以供审核。
查询由人工审核, 可以授权用户(或其他用户)运行 存储查询。如果你认为你可以开发某种自动验证器,欢迎来访,但恕我直言,这肯定比让合格的数据库管理员审查它需要做更多的工作。
随后,允许用户按需运行存储的查询,但只能通过其 ID。
这是另一个替代方案:想要运行 自定义查询的用户可以申请获得数据库的副本,以托管在他们自己的计算机上。他们将获得他们有权查看的数据子集的转储。然后,如果他们 运行 查询垃圾数据,或融化他们的计算机,那是他们的事。