如何安全地允许用户定义的SQL查询?

时间:2012-07-12 23:07:06

标签: sql sql-injection sql-parametrized-query

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

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

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

如何在不打开SQL注入的情况下允许灵活的数据库查询?


更多详情:

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

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

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

4 个答案:

答案 0 :(得分:2)

您应该创建一个UI,用户可以从下拉列表中选择一个表,然后添加过滤器。如果您曾经使用过TOAD或DBVisualizer甚至是SQLDeveloper,那么它们都可以在UI中选择一个表,然后在没有实际编写SQL的情况下,用户可以添加过滤器并从UI控件中进行排序。

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

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

DBVisualizer UI

您可以在UI中执行非常类似的操作。

如果你包含你的应用程序将要编写的语言,那将会有所帮助。如果我不得不猜测,我会根据你的字符串声明说Java或C#,但它会对你有好处。知道肯定。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

准备好的语句和输入法消毒

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。消毒

比方说,我们两个表都有两组列,用户也可以输入列名和值。


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


案例1: 假设用户可以使用“ col1”列进行过滤,并且它是“ Integer”或“ Numeric”类型,我们可以使用Regex过滤输入以查看其中是否有特殊字符:

^[0-9]*$

案例2: 检查输入的列名称是否有效。

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

最后的注释:

因此,在采取了所有这些预防措施之后,动态生成的查询是否安全?安全得多,但您不能保证。有很多问题使您的数据库易于注入。

答案 3 :(得分:0)

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

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

    .as-console-wrapper {
      max-height: 100% !important;
    }
  2. 还可以在SQL查询中使用ESCAPE关键字来保护您,如下所示;

    $input = mysqli_real_escape_string($db_connection, $_POST['username']);

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

  4. 避免管理特权,例如数据库根目录访问。至于搜索,您的帐户只需要对所需的那些表和列具有读取权限。

  5. 使用存储过程以及特殊的EXECUTE特权。

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

  7. 输入验证和消毒。

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

  9. 您可以记录所有内容以防止发生攻击时造成损害。

  10. UNION运算符的块用法。这是一种非常常见的基于联合的SQL注入攻击。例如:

    获取yourapp.com/store.php?store=-1 在用户uname ='test'HTTP / 1.1时从用户中选择UNION SELECT 1,pass,cc 主持人:yourapp.com

  11. DROP运算符的块用法。

    SELECT * FROM用户;删除用户-