【问题标题】:SqlCommand with parameters as a stringSqlCommand 参数为字符串
【发布时间】:2012-05-06 00:55:11
【问题描述】:

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

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

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

我熟悉创建 SqlCommand 对象并向其参数列表添加值,但查看 a similar question 它似乎并没有按我希望的方式工作。

那么有没有一种方法可以按照我需要的方式生成经过清理的 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 中没有给出数据类型,因此应用程序也必须确定这一点。

【问题讨论】:

  • 该问题的答案有什么问题?为什么它们不适合你?
  • 向我们展示:你做了什么,你得到了什么,你想得到什么
  • 您应该向我们展示一个示例 Insert 及其字段名称/类型和值。
  • 我已经包含了一个编辑。这更有意义吗?还应该有一个 CREATE TABLE 语句,但我没有打扰它。
  • @Oded:我认为问题在于丹尼尔不想运行插入,而是显示/输出/保存它们。所以使用 SqlCommand 参数是不好的,因为你没有看到生成的 SQL。

标签: c# sql sql-injection sqlcommand sqlparameter


【解决方案1】:

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

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

  • ' -> '' 撇号和其他东西非常明显并记录在案ad-nauseum
  • ; 一条语句中的多个命令 - 数据库并不总是允许,但很危险

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

  • 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

【讨论】:

    【解决方案2】:

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

    如果有人可以改进这一点,请告诉我!

    【讨论】:

      【解决方案3】:

      因为我不知道您是如何存储变量的,所以我将使用 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:我认为问题在于 daniel 不想运行 插入,但显示/输出/保存它们。所以使用SqlCommand参数 不好,因为你看不到生成的 SQL。

      @Cylindric 没错

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

      【讨论】:

      • Daniel 特别希望 SQL 语句作为字符串,我假设用于输出/保存/显示,并且您的示例执行查询,但您永远看不到最终命令。
      • @Cylindric:是的,我刚刚意识到这一点。我已经编辑了我的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-10
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 1970-01-01
      • 2013-07-26
      • 2012-07-30
      相关资源
      最近更新 更多