.NET / C#检查SQL查询是否修改数据库,如果不执行

时间:2011-08-22 15:19:41

标签: c# .net sql code-injection

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

问题是我想以任何方式禁止所有修改数据库的查询。我还没有找到办法做到这一点,我用谷歌做到了最好。

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

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

9 个答案:

答案 0 :(得分:12)

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

如果你不采用这种方式,复杂性就会变得非常庞大,因为你基本上必须准备解析一个任意的SQL语句,如果你允许的话,更不用说SQL语句的任意序列存储过程将被运行。

即便如此,请注意确保您不会通过查询泄露敏感数据。如果您不小心,直接从站点用户输入查询可能会很危险。即使你是这样,允许这些查询除了专门构建的沙盒数据库之外的任何东西都是“哎呀,我不小心改变了用户的权限”而不是成为安全噩梦。

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

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

答案 1 :(得分:4)

如何在仅具有select(只读)权限的数据库服务器上创建用户帐户?

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

您无法可靠地解析SQL。

使用权限

  1. 仅对表和视图允许SELECT
  2. 对更改数据的存储过程没有任何权限(默认情况下,最终用户无法查看存储过程定义)

答案 4 :(得分:1)

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

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

<强>提防
要执行存储过程,用户必须具有执行权限...如果存储过程修改数据,那么即使使用受限用户也不会出现错误消息,因为修改权限被授予存储过程!

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

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

修改

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

实例:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

我认为SQL Trigger是您想要做的最佳方式。

答案 7 :(得分:0)

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

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

也许您使用UI编程的语言,您可以尝试在线查找允许对数据库进行过滤的自定义控件。 Google它......

答案 8 :(得分:0)

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

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
请参阅正则表达式备忘单:cheatsheet1cheatsheet2

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

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

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

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