. net / c#检查SQL查询是否修改了数据库,如果没有执行

本文关键字:数据库 如果没有 执行 修改 是否 检查 SQL 查询 net | 更新日期: 2023-09-27 18:06:15

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

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

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

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

. net / c#检查SQL查询是否修改了数据库,如果没有执行

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

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

即使这样,也要注意确保您不会通过查询泄露敏感数据。如果不小心,直接输入来自站点用户的查询可能是危险的。即使你是,允许这些查询在任何东西上,除了一个特别构建的沙箱数据库是一个"哎呀,我不小心改变了用户的权限"远离成为一个安全噩梦。

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

Update:正如Yahia指出的,如果用户具有执行权限(这样他们就可以执行存储进程),那么过程本身的权限就会受到尊重。考虑到这一点,最好是不允许任意存储过程执行,而是向用户提供已知安全的过程列表。但是,这可能很难维护并且容易出错,因此完全禁止存储过程可能是最好的选择。

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

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

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

您无法可靠地解析SQL。

使用权限

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

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

防止这种情况的下一个最佳方法是使用具有纯读访问权限的受限用户

小心
要执行存储过程,用户必须具有执行权限…如果存储过程修改了数据,那么即使是受限制的用户也不会出现错误消息,因为修改权限被授予了存储过程!

如果你绝对必须允许用户输入SQL而不能限制登录,那么你需要使用SQL解析器-例如这个…

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

编辑:


另一个选项是所谓的"数据库防火墙"-你连接而不是直接到数据库到防火墙…在防火墙中,您可以配置一些东西,如基于时间的限制(当不允许特定用户/语句时),基于sql的语句(允许…),基于数量的限制(例如您可以获得100条记录,但不能下载整个表/DB…)等。
市面上有商业的和开源的数据库防火墙——尽管它们本质上非常依赖于你使用的数据库等。

例子:

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

不要忘记比INSERT、UPDATE和DELETE更糟糕的事情。像截断…这是一些不好的东西。

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

你的第一步应该是为这个特定的任务创建一个DB用户,只有必要的权限(基本上只有SELECT),并且只有你需要他们看到的表的权限(所以他们不能选择sys表或你的用户表)。

一般来说,让用户直接在数据库上执行代码似乎不是一个好主意。例如,即使你保护它不受数据修改的影响,它们仍然可以进行难看的连接,使你的数据库运行缓慢。

也许无论你用哪种语言编程UI,你都可以尝试在网上寻找一个允许在数据库上过滤的自定义控件。谷歌…

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

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);
}

你可以在这里看到它是如何工作的:参见正则表达式小抄表: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;
}