SqlCommand с параметрами в виде строки

У меня есть приложение, которое мне нужно создать, которое, учитывая некоторый ввод данных пользователем в форме CSV, должно анализировать и генерировать этот CSV в нескольких форматах. Один из этих форматов представляет собой серию операторов SQL INSERT (в виде строки) для каждой строки CSV.

(На этом этапе вы можете предположить, что я уже проанализировал 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 не указаны типы данных, приложение также должно определить это.


person daniel    schedule 25.04.2012    source источник
comment
Что не так с ответами на этот вопрос? Почему они вам не подходят?   -  person Oded    schedule 25.04.2012
comment
Покажи нам: что ты делаешь, что у тебя есть, что ты хочешь получить   -  person Likurg    schedule 25.04.2012
comment
Вы должны показать нам пример Insert с именем / типом поля и значением.   -  person Tim Schmelter    schedule 25.04.2012
comment
Я включил правку. Это имеет смысл? Также должен быть оператор CREATE TABLE, но я не стал его включать.   -  person daniel    schedule 25.04.2012
comment
@Oded: Я думаю, проблема в том, что Дэниел не хочет запускать вставки, а показывает / выводит / сохраняет их. Поэтому использование параметров SqlCommand бесполезно, потому что вы не видите сгенерированный SQL.   -  person Cylindric    schedule 25.04.2012


Ответы (3)


Не столько ответ, сколько предупреждение. Если для этого вам нужно пойти «классическим» путем экранирования и действительно нужна безопасность (т. Е. Данные поступают из ненадежного источника), не забывайте, что это не просто побег, о котором вам нужно беспокоиться.

Мы постоянно слышим о побеге основного персонажа:

  • ' -> '' апострофы и прочее довольно очевидны и задокументированы 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

person Cylindric    schedule 25.04.2012

Скорее всего, если вы не хотите использовать объекты 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]) + ");";

если кто-то может улучшить это, дайте мне знать!

person Neo    schedule 25.04.2012

Поскольку я не знаю, как вы сохранили свои переменные, я покажу вам полную возможную реализацию с вашими образцами данных с использованием 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-Injection, если запустите эти вставки позже. Я бы посоветовал использовать приведенный выше код, когда вы действительно запускаете statemenets.

person Tim Schmelter    schedule 25.04.2012
comment
Дэниел специально хочет, чтобы операторы SQL были строками, я предполагаю, что для вывода / сохранения / отображения, и ваш пример выполняет запрос, но вы никогда не видите финальные команды. - person Cylindric; 25.04.2012
comment
@Cylindric: Да, я только что это понял. Я отредактировал свой ответ. - person Tim Schmelter; 25.04.2012