【问题标题】:.NET/C# check if SQL query modifies database and if not execute.NET/C# 检查 SQL 查询是否修改数据库,如果不执行
【发布时间】:2011-08-22 15:19:41
【问题描述】:

我知道如何从 C# 执行查询,但我想提供一个下拉列表,人们可以在其中编写查询,它将执行并填充列表。

一个问题是我想禁止所有以任何方式修改数据库的查询。我还没有设法做到这一点,我在谷歌上尽了最大努力。

我能想到的解决方案是,我将扫描查询以查找 INSERT、DELETE、UPDATE 并且只允许 SELECT 语句。但是,我也希望能够允许用户调用存储过程。这意味着我需要获取存储过程的主体并在执行之前对其进行扫描。那么如何下载存储过程呢?

如果有人知道一种只执行只读查询的方法,请分享!我感觉扫描文本中的 INSERT、DELETE、UPDATE 并不能阻止 SQL 注入。

【问题讨论】:

  • 这是一个等待发生的 SQL 注入攻击。您最好/唯一的安全选择是按照@dlev 的建议进行操作,并通过许可加以限制。这可能是对 INSERT/UPDATE/DELETE 扫描的补充,但该测试不够可靠。只有权限才能阻止用户执行错误的查询。

标签: c# .net sql code-injection


【解决方案1】:

执行此操作的最简单方法可能是将此作业卸载到数据库。只需确保将运行查询的数据库用户具有读取权限。然后,除SELECT 之外的任何查询都会失败,您可以将失败报告给用户。

如果你不走这条路,复杂度会变得相当大,因为你基本上必须准备好解析任意 SQL 语句,更不用说如果你允许的话,任意的 SQL 语句序列要运行的存储过程。

即便如此,请注意确保您不会通过查询泄露敏感数据。如果您不小心,直接输入来自站点用户的查询可能会很危险。即使您是,允许对除专门构建的沙盒数据库之外的任何内容进行这些查询也是“哎呀,我不小心更改了用户的权限”,以免成为安全噩梦。

另一种选择是编写一个“查询创建者”页面,用户可以在其中选择他们想要查看的表和列。然后,您可以 a) 仅显示适合给定用户的表和列(可能基于用户角色等)和 b) 自己生成 SQL,最好使用参数化查询。

更新:正如 Yahia 指出的那样,如果用户具有执行权限(以便他们可以执行存储的过程),那么程序本身的权限就会得到尊重。鉴于此, 允许任意存储过程执行可能会更好,而是为用户提供一个已知安全的过程列表。不过,这可能难以维护且容易出错,因此最好完全禁止存储过程。

【讨论】:

  • +1 - 数据库权限是要走的路。关键字搜索将失败,因为他仍然允许SELECT INTO 或其中可能包含破坏性代码的存储过程调用。
  • 是的,这正是我的想法。我不想为 SQL 编写一个完整的解析器。我必须检查使用它的用户是否具有我假设他们会的只读权限。我想将查询限制为特定模式,以便它们无法触及数据库的数据敏感部分。
  • @user 如果需要,您可以使用每个表的权限。不过,最好的选择可能是提供“查询创建者”,这样您就可以完全控制可以运行和执行的查询。这只是危险的东西。
  • 执行存储过程需要执行权限...如果过程本身修改了某些内容,则不会检查执行用户的权限,而是检查过程权限 - 至少在 Oracle 中,您授予过程所需的权限...
【解决方案2】:

如何在数据库服务器上创建一个只有选择(只读)权限的用户帐户?

【讨论】:

    【解决方案3】:

    也许您可以设置一个对数据库具有只读访问权限的 SQL 用户并使用该用户发出命令?然后,您可以在错误发生时/如果发生错误。

    在我看来,尝试解析查询以确定它是否修改了数据库将非常困难且容易出错。

    【讨论】:

      【解决方案4】:

      你不能像那样可靠地解析 SQL。

      使用权限

      1. 仅允许对表和视图进行 SELECT
      2. 对更改数据的存储过程没有权限(最终用户默认看不到存储过程定义)

      【讨论】:

        【解决方案5】:

        最好是不允许用户输入 SQL 并且只使用准备好的/参数化的查询...

        防止这种情况的下一个最佳方法是使用具有纯读取权限的受限用户
        以上两者可以结合...

        注意
        要执行存储过程,用户必须具有执行权限...如果存储过程修改数据,那么即使使用受限用户也会发生这种情况而不会出现错误消息,因为修改权限已授予存储过程!

        如果您绝对必须允许用户输入 SQL 并且不能限制登录,那么您将需要使用 SQL 解析器 - 例如 this...

        关于如何下载存储过程的主体 - 这取决于您使用的数据库(SQL Server、Oracle 等)。

        编辑:

        另一个选项是所谓的“数据库防火墙” - 您连接而不是直接连接到数据库到防火墙...在防火墙中,您配置了几项内容,例如基于时间的限制(当特定用户/语句不是/艺术时允许)、基于 SQL 的语句(允许...)、基于数量的限制(例如您可以获取 100 条记录,但无法下载整个表/数据库...)等。
        那里有商业和开源的数据库防火墙——尽管这些防火墙本质上非常依赖于您使用的数据库等。

        示例:

        • Oracle Firewall - 适用于 Oracle / SQL Server / DB2 等。
        • SecureSphere - 包括 Oracle / SQL Server / DB2 等。
        • GreenSQL - 开源版本支持 Postgres + MySQL,商业 MS SQL Server

        【讨论】:

          【解决方案6】:

          不要忘记比 INSERT、UPDATE 和 DELETE 更糟糕的事情。就像 TRUNCATE...那是一些坏东西。

          【讨论】:

            【解决方案7】:

            我认为SQL Trigger 是你想做的最好的方式。

            【讨论】:

            • 你好 Shree,欢迎来到堆栈溢出。今后,在回答问题时,请尽量把你的回答透彻。什么是 SQL 触发器?这有什么用途?怎么可以在这里使用?代码示例呢?最好是最后一个回答一个很好的答案,而不是第一个回答低质量而被忽视。我建议您编辑答案以符合上述提示(取自堆栈溢出常见问题解答的how to answer 部分。祝你好运!
            【解决方案8】:

            您的第一步应该是为此特定任务创建一个数据库用户,该用户只有所需的权限(基本上仅限 SELECT),并且有权仅查看您需要他们查看的表(因此他们无法选择 sys 表或您的用户表)。

            更一般地说,让用户直接在您的数据库上执行代码似乎是个坏主意。例如,即使您保护它免受数据修改,它们仍然可以进行丑陋的连接以使您的数据库运行缓慢。

            也许无论您使用哪种语言编写 UI,您都可以尝试在线查找允许过滤数据库的自定义控件。 Google它...

            【讨论】:

              【解决方案9】:

              这并不完美,但可能是您想要的,如果关键字是更大的字母数字字符串的一部分,这将允许关键字出现:

              public static bool ValidateQuery(string query)
              {
                  return !ValidateRegex("delete", query) && !ValidateRegex("exec", query) && !ValidateRegex("insert", query) && !ValidateRegex("alter", query) &&
                         !ValidateRegex("create", query) && !ValidateRegex("drop", query) && !ValidateRegex("truncate", query);
              }
              public static bool ValidateRegex(string term, string query)
              {
                  // this regex finds all keywords {0} that are not leading or trailing by alphanumeric 
                  return new Regex(string.Format("([^0-9a-z]{0}[^0-9a-z])|(^{0}[^0-9a-z])", term), RegexOptions.IgnoreCase).IsMatch(query);
              }
              

              你可以在这里看到它是如何工作的:regexstorm
              见正则表达式备忘单:cheatsheet1, cheatsheet2

              请注意,这并不完美,因为它可能会阻止使用关键字之一作为引号的查询,但如果您编写查询并且它只是一种预防措施,那么这可能会奏效。

              您也可以采取不同的方法,尝试查询,如果它影响数据库执行回滚:

              public static bool IsDbAffected(string query, string conn, List<SqlParameter> parameters = null)
              {
                  var response = false;
                  using (var sqlConnection = new SqlConnection(conn))
                  {
                      sqlConnection.Open();
                      using (var transaction = sqlConnection.BeginTransaction("Test Transaction"))
                      using (var command = new SqlCommand(query, sqlConnection, transaction))
                      {
                          command.Connection = sqlConnection;
                          command.CommandType = CommandType.Text;
                          command.CommandText = query;
                          if (parameters != null)
                              command.Parameters.AddRange(parameters.ToArray());
                          // ExecuteNonQuery() does not return data at all: only the number of rows affected by an insert, update, or delete.
                          if (command.ExecuteNonQuery() > 0)
                          {
                              transaction.Rollback("Test Transaction");
                              response = true;
                          }
                          transaction.Dispose();
                          command.Dispose();
                      }
                  }
                  return response;
              }
              

              您也可以将两者结合起来。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2010-09-22
                • 2015-06-18
                • 2018-12-08
                • 2016-07-03
                • 2021-06-30
                • 2012-01-12
                • 2023-03-07
                • 1970-01-01
                相关资源
                最近更新 更多