16

我希望允许用户使用一些相当灵活的标准来查询数据库。我可以使用以下内容:

String slqCmdTxt = "SELECT * FROM TheTable WHERE " + userExpression;

但是,我知道这对 SQL 注入很开放。使用参数很好,但我看不到允许非常灵活的查询的方法。

我怎样才能允许灵活的数据库查询而不让自己接受 SQL 注入?


更多细节:

确实有两个表,一个具有属性的主表和一个辅助表。一份主记录可能有许多属性。我们想查询两个表中的值。结果被处理成比简单的表格视图更具可读性的报告。数据由 C# 程序编写,但当前方向是从用 Java 编写的组件中查询表。

所以我需要一种方法来提供用户输入,然后安全地构建查询。对于一组有限的输入,我编写了代码来使用给定的输入和参数值构建查询字符串。然后,我将输入值添加为参数。这导致了复杂的字符串 catination,很难更改/扩展。

现在我正在使用Java,一些搜索已经出现了像jOOQ这样的SQL语句构造库......

4

4 回答 4

8

您可能应该创建一个 UI,用户可以在其中从下拉列表中选择一个表,然后添加过滤器。如果您曾经使用过 TOAD 或 DBVisualizer 甚至 SQLDeveloper,它们在 UI 中都有您可以选择表的部分,然后无需实际编写 SQL,用户就可以从 UI 控件添加过滤器和排序。

然后,当然,在 UI 后面的代码中,您将验证过滤器输入并将它们用作准备好的语句中的参数(取决于您使用的语言)。

例如,当您浏览数据库对象并单击表时,这就是 DBVisualizer(用 Java 编写)在其 UI 中的内容。请注意,您可以从下拉列表中选择任何列,然后从另一个下拉列表中选择运算符(即 =、>、>=、<、<=、LIKE、IN 等),然后您可以输入用户定义的值为过滤器值。

DBVisualizer 用户界面

你可以在你的 UI 中做一些非常相似的事情。

顺便说一句,如果您包含您的应用程序将使用哪种语言编写,这将有所帮助。如果我不得不猜测,我会根据您的字符串声明说 Java 或 C#,但最好确定一下。

于 2012-07-12T23:17:52.953 回答
6

假设一个现代 DBMS(例如 Sql Server 或 Oracle;我不熟悉 MySQL),那么您可以允许用户编写原始 SQL,只要您确保他们登录的帐户具有适当的限制。

在 SQL Server 中,您可以限制用户可以对任何 db 对象执行的操作(SELECT、DELETE、UPDATE、EXECUTE)。我相信这在 Oracle 中是正确的。我认为它甚至可能扩展到列级别,但我不确定。

于 2012-07-12T23:33:10.983 回答
1

准备好的语句和输入清理

1. 准备好的报表

Java 提供PreparedStatement来执行参数化查询。使用 Prepared Statements 构建的查询不太容易受到攻击。

例子:

我们需要做的查询:

String query = "SELECT col1, col2, col3 FROM table1 WHERE " + user_input;

使用带有参数化值的 PreparedStatement:

// write the query with "?" placeholder for the user_input
String query = "SELECT col1, col2, col3 FROM table1 WHERE ?";

// Create database connection
Connection conn = source.getConnection();

// Prepare a statement for the query
PreparedStatement stmt = conn.prepareStatement(query);

// set the placeholder with the actual user_input
stmt.setString(1, user_input);

// execute the query
ResultSet result = stmt.executeQuery(query);

够了吗?不!


即使在使用 PreparedStatement 或 createQuery(JPA 的类似方法)或任何东西之后,攻击者仍有可能通过。所以这把我们带到了这个......

正如@phil 指出的那样进行编辑,使用 PreparedStatement 确实可以阻止非法值的执行。但我仍然强烈建议对输入进行清理,因为当您期待“int”时,用户可能会输入“String”或随机特殊字符。



2. 消毒

假设我们有两组表的列,用户也可以输入列名和值。


而不是像这样未经过滤的输入: String query = "SELECT col1, col2, col3 FROM table1 WHERE ?"; 使用一些过滤器。过滤器可以是任何东西。可能是一些字符串函数或字符串比较或输入变量类型检查或任何东西。


案例1: 假设用户可以使用列“col1”进行过滤,并且它是“整数”或“数字”类型,我们可以使用正则表达式过滤输入以查看其中是否有任何特殊字符:

^[0-9]*$

Case2: 检查输入的列名是否有效。

private static final Set<String> valid_column_names 
= Collections.unmodifiableSet(Stream
.of("col1", "col2", "col3")
.collect(Collectors.toCollection(HashSet::new)));

boolean is_valid = false;

if (valid_column_names.contains(user_column_input)) {
  is_valid = true;
}

if(!is_valid){
  throw new IllegalArgumentException("Invalid Column input");
}
String query = "SELECT col1, col2, col3 FROM table1 WHERE ?";
// prepare statements and execute

最后说明:

那么,在所有这些预防措施之后,您动态生成的查询是否安全?安全得多,但你不能保证。有很多问题使您的数据库容易被注入。

于 2020-09-04T17:44:37.867 回答
0

有很多方法可以保护您的数据库免受 SQL 注入。我将一一列出。

  1. 对 DBMS 提供的用户提供的输入使用字符转义函数,如下所示;还要确保必须在服务器级别设置字符集,并且 mysql_real_escape_string() 函数不会转义 % 和 _ 通配符。

    $input = mysqli_real_escape_string($db_connection, $_POST['username']);
    
  2. 在 SQL 查询中使用 ESCAPE 关键字也可以保护您,如下所示;

    SELECT * FROM table WHERE column LIKE '%%' ESCAPE '!'

  3. 使用Web 应用程序防火墙,例如https://www.cloudflare.com/waf/这将在您的 Web 服务器前面运行,并将监控进出您的 Web 服务器的流量。因此,对 WAF 的每个请求都会根据规则引擎和威胁情报(例如 SQL 注入、XSS、参数篡改等)进行检查。因此,可以立即阻止、质疑或记录任何可疑请求。

  4. 避免管理权限,例如 DB root 访问。至于搜索,您的帐户只需要对那些需要的表和列具有读取权限。

  5. 使用存储过程和特殊的 EXECUTE 权限。

  6. 通过绑定参数使用参数化查询,以便引用输入并且提供的输入不会导致意图的变化。

  7. 输入验证和清理。

  8. 即使您使用诸如 jOOQ 或 JPA 之类的 SQL 语句构造库和其他 ORM 来使您免于创建手工编码的 SQL 语句,它也不会保护您免于编写易受攻击的代码。如果你用 Java 开发。您可以使用 Spring Cloud Vault 等短期凭证。

  9. 如果发生攻击,您可以记录所有内容以防止损坏范围。

  10. 阻止使用 UNION 运算符。它是一种非常常见的基于联合的 SQL 注入攻击。例如:

    GET yourapp.com/store.php?store=-1 UNION SELECT 1,pass,cc FROM users WHERE uname='test' HTTP/1.1 Host: yourapp.com

  11. 阻止使用 DROP 运算符。

    从用户中选择 *;删除用户——

于 2020-09-05T13:13:21.653 回答