将参数作为字符串的 Sql命令

本文关键字:Sql 命令 字符串 参数 | 更新日期: 2023-09-27 18:32:07

我需要创建一个应用程序,给定一些用户输入的CSV形式,需要将此CSV解析并生成为多种格式。其中一种格式是针对每行 CSV 的一系列 SQL INSERT 语句(作为字符串)。

(在这一点上,您可以假设我已经将CSV解析为值列表或其他内容,因此这不是问题的重点)

鉴于此输入可能包含漏洞,我希望生成已经过验证和清理的 INSERT 语句。

我熟悉创建 SqlCommand 对象并将值添加到其参数列表中,但查看类似的问题,它似乎没有按照我希望的方式工作。

那么有没有办法以我需要的方式生成经过净化的 SQL 语句,作为字符串?

编辑:这是我想做的一个例子。

.CSV:

id,name,country
1,Alice,France
2,Bob,Austria
3,Carol,Germany

.SQL:

...
INSERT INTO Users (id, name, country) VALUES (1, 'Alice', 'France');
INSERT INTO Users (id, name, country) VALUES (2, 'Bob', 'Austria');
INSERT INTO Users (id, name, country) VALUES (3, 'Carol', 'Germany');
...

由于CSV中没有给出数据类型,因此应用程序也必须确定。

将参数作为字符串的 Sql命令

与其说是一个答案,不如说是一个警告。如果您最终需要走"经典"逃逸路线来执行此操作,并且确实需要安全(即数据来自不受信任的来源),请不要忘记您需要担心的不仅仅是简单的逃逸。

我们经常听到的基本角色转义:

  • ' -> ''撇号之类的东西非常明显,并且记录在
  • 在一个语句中;多个命令 - DB并不总是允许,但很危险

但是,如果您要解析"恶意行为",您是否考虑过:

  • SELECT/*not important*/1/*really...*/FROM/*im serious*/users
  • SELECT%09FIELDS%09FROM%0dTABLE_NAME
  • WHERE username=CONCAT(CHAR(97),CHAR(100),CHAR(109),CHAR(105),CHAR(110))
  • SELECT passwd FROM users WHERE username=0x61646d696e

总结:这里是龙。

http://www.ihteam.net/papers/blind-sqli-regexp-attack.pdf

http://ferruh.mavituna.com/sql-injection-cheatsheet-oku/#HexbasedSamples

如果您不想使用 SQL 对象,那么您将不得不自己清理条目。 我不知道SQL的任何推荐格式,但是对于MySQL,以下内容将起作用。 我已经将其更改为与SQL一起使用,但是我不能保证它涵盖了所有可能的注入攻击。

public string sqlEscape(string VAL)
{
    if (VAL == null)
    {
        return null;
    }
    return "'"" + Regex.Replace(VAL, @"['r'n'x00'x1a'''""]", @"'$0") + "'"";
}

然后使用你会做(假设你的CSV行存储在一个名为CSV的数组中):

string query = @"INSERT INTO Users (id, name, country) VALUES (" + sqlEscape(csv[0]) + ", " + sqlEscape(csv[1]) + ", " + sqlEscape(csv[2]) + ");";

如果有人可以增强这一点,请告诉我!

因为我不知道

你是如何存储变量的,所以我将使用List<Dictionary<String, Object>>()向你展示一个完整的、可能的示例数据实现:

添加您的示例数据:

var tableName = "Users";
var records = new List<Dictionary<String, Object>>();
var recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 1);
recordFields.Add("name", "Alice");
recordFields.Add("country", "France");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 2);
recordFields.Add("name", "Bob");
recordFields.Add("country", "Austria");
records.Add(recordFields);
recordFields = new Dictionary<String, Object>();
recordFields.Add("id", 3);
recordFields.Add("name", "Carol");
recordFields.Add("country", "Germany");
records.Add(recordFields);

生成参数化的插入语句:

using (var con = new SqlConnection(Settings.Default.ConnectionString))
{
    con.Open();
    foreach (var record in records)
    {
        String insertSql = String.Format("INSERT INTO {0} ({1}) VALUES ({2});"
            , tableName
            , String.Join(",", record.Select(r => r.Key))
            , String.Join(",", record.Select(r => "@" + r.Key)));
        using (var insertCommand = new SqlCommand(insertSql, con))
        {
            foreach (var field in record)
            {
                var param = new SqlParameter("@" + field.Key, field.Value);
                insertCommand.Parameters.Add(param);
            }
            insertCommand.ExecuteNonQuery();
        }
    }
}

请注意,这并没有真正经过测试(它编译并且看起来不错),但它应该对您有所帮助。

编辑

@Oded:我认为问题是丹尼尔不想运行 插入,但显示/输出/保存它们。因此,使用 SqlCommand 参数 不好,因为您看不到生成的 SQL。

@Cylindric 没错

这是不可能的,而且是一个矛盾,你不能用String SqlParameters.因此,如果您稍后运行这些插入,恐怕您对SQL注入持开放态度。我建议在实际运行状态时使用上面的代码。