0

我有一个需要创建的应用程序,给定一些 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 中没有给出数据类型,因此应用程序也必须确定这一点。

4

3 回答 3

1

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

我们经常听到的基本字符转义:

  • ' -> ''撇号之类的东西很明显并且有记录的令人作呕
  • ;一个语句中的多个命令 - 数据库并不总是允许,但很危险

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

  • 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

于 2012-04-25T11:24:30.730 回答
1

很有可能,如果您不想使用 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]) + ");";

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

于 2012-04-25T12:24:45.843 回答
1

因为我不知道您是如何存储变量的,所以我将使用您的示例数据向您展示一个完整的、可能的实现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 没错

这是不可能的,而且是矛盾的,你不能使用Stringwith SqlParameters。因此,如果您稍后运行这些插入,恐怕您对 Sql-Injection 持开放态度。我建议您在实际运行语句时使用上面的代码。

于 2012-04-25T11:03:51.043 回答