Достаточно ли этого, чтобы предотвратить внедрение запросов при использовании SQL Server? - PullRequest
5 голосов
/ 09 апреля 2010

Я недавно взялся за проект, в котором мне нужно интегрироваться с PHP / SQL Server. Я ищу самую быструю и простую функцию для предотвращения внедрения SQL на SQL Server, так как я предпочитаю MySQL и не ожидаю много других проектов, связанных с SQL Server.

Достаточно ли этой функции?

$someVal = mssql_escape($_POST['someVal']);

$query = "INSERT INTO tblName SET field = $someVal";

mssql_execute($query);

function mssql_escape($str) {
    return str_replace("'", "''", $str);
}

Если нет, какие дополнительные шаги я должен предпринять?


EDIT: Я работаю на сервере Linux - sqlsrv_query() only works if your hosting environment is windows

Ответы [ 4 ]

11 голосов
/ 09 апреля 2010

Лучший вариант: не использовать операторы SQL, которые объединяются вместе - использовать параметризованные запросы.

например. не создать что-то вроде

string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(" + value1 + ", " + value2 + ")"

или что-то в этом роде, а затем попытайтесь «очистить» его, заменив одинарные кавычки или что-то в этом роде - вы никогда не поймаете все, кто-то всегда найдет способ обойти вашу «безопасную охрану».

Вместо этого используйте:

string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(@value1, @value2)";

и затем установите значения параметров перед выполнением этого оператора INSERT. Это действительно единственный надежный способ избежать внедрения SQL-кода - используйте его!

ОБНОВЛЕНИЕ: как использовать параметризованные запросы из PHP - я нашел что-то здесь - это помогает?

$tsql = "INSERT INTO DateTimeTable (myDate, myTime,
                                    myDateTimeOffset, myDatetime2)
         VALUES (?, ?, ?, ?)";

$params = array(
            date("Y-m-d"), // Current date in Y-m-d format.
            "15:30:41.987", // Time as a string.
            date("c"), // Current date in ISO 8601 format.
            date("Y-m-d H:i:s.u") // Current date and time.
          );

$stmt = sqlsrv_query($conn, $tsql, $params);

Так что кажется, что вы не можете использовать «именованные» параметры, такие как @ value1, @ value2, но вместо этого вы просто используете вопросительные знаки? для каждого параметра, и вы просто создаете массив параметров, который затем передаете в запрос.

Эта статья Доступ к базам данных SQL Server с помощью PHP также может помочь - в ней есть аналогичный пример вставки данных с помощью параметризованных запросов.

ОБНОВЛЕНИЕ: после того, как вы узнали, что работаете в Linux, этот подход больше не работает. Вместо этого вам нужно использовать альтернативную библиотеку в PHP для вызова базы данных - что-то вроде PDO .

PDO должен работать как в любой операционной системе * nix, так и против всех видов баз данных, включая SQL Server, и также поддерживает параметризованные запросы:

$db = new PDO('your-connection-string-here');
$stmt = $db->prepare("SELECT priv FROM testUsers WHERE username=:username AND password=:password");
$stmt->bindParam(':username', $user);
$stmt->bindParam(':password', $pass);
$stmt->execute();
3 голосов
/ 09 апреля 2010

Замена строки для экранирования кавычек достаточна для предотвращения векторов атаки SQL-инъекций.

Это применимо только к SQL Server, когда QUOTED_IDENTIFIER включен, и когда вы не делаете что-то неуклюжее для вашей экранированной строки, например, обрезаете ее или переводите строку Unicode в 8-битную строку после экранирования. В частности, вы должны убедиться, что уверен, что QUOTED_IDENTIFIER установлен на ON. Обычно это значение по умолчанию, но оно может зависеть от библиотеки, которую вы используете в PHP для доступа к MSSQL.

Параметризация - это лучшая практика, но нет ничего небезопасного в том, чтобы экранировать кавычки для предотвращения внедрения SQL с должной осторожностью.

Проблема, связанная с экранированием строк, заключается не в эффективности замены, а в том, что может забыть каждый раз делать замену.

Тем не менее, ваш код экранирует значение, но не заключает его в кавычки. Вам нужно что-то вроде этого:

function mssql_escape($str) {
  return "N'" + str_replace("'", "''", $str) + "'";
}

Выше N позволяет передавать более высокие символы Юникода. Если это не проблема (то есть, ваши текстовые поля varchar, а не nvarchar), вы можете удалить N.

Теперь, если вы сделаете это, есть несколько предостережений:

  1. Вам нужно убедиться, что вы уверены, что вызываете mssql_escape для каждого строкового значения. И в этом заключается руб.
  2. Даты и значения GUID также необходимо экранировать таким же образом.
  3. Вы должны проверять числовые значения или, по крайней мере, экранировать их, используя ту же функцию (MSSQL приведёт строку к соответствующему числовому типу).

Опять же, как уже говорили другие, параметризованные запросы безопаснее - не потому, что экранирование кавычек не работает (оно работает, за исключением случаев, указанных выше), а потому, что легче визуально убедиться, что вы не забыли что-то избежать.

2 голосов
/ 09 апреля 2010

Я частично не согласен с другими постерами. Если вы запустите все свои параметры через функцию, которая удваивает кавычки, это должно предотвратить любую возможную атаку инъекцией. На самом деле на практике более частой проблемой является не преднамеренное саботаж, а запросы, которые прерываются, потому что значение законно включает в себя одинарную кавычку, например, клиент с именем «O'Hara» или поле комментария «Не звоните Салли до 9:00». В любом случае, я все время убегаю, и у меня никогда не было проблем.

Одно предостережение: на некоторых движках базы данных могут быть и другие опасные символы, кроме одной кавычки. Единственный пример, который я знаю, это Postgres, где обратный слеш - это магия. В этом случае ваша функция escape также должна удваивать обратную косую черту. Проверьте документацию.

Я не имею ничего против использования подготовленных операторов, и для простых случаев, когда единственное, что меняется, - это значение параметра, они являются отличным решением. Но я обычно нахожу, что мне приходится строить запросы по частям, основываясь на условиях в программе, например, если параметр X не равен нулю, мне нужно не только добавить его в предложение where, но мне также нужно дополнительное соединение, чтобы добраться до значение мне действительно нужно проверить. Подготовленные заявления не могут справиться с этим. Конечно, вы могли бы построить SQL по частям, превратить его в подготовленный оператор и затем предоставить параметры. Но это просто боль без какой-либо явной выгоды.

В настоящее время я в основном пишу на Java, что позволяет перегружать функции, то есть иметь несколько реализаций в зависимости от типа передаваемого параметра. Поэтому я обычно пишу набор функций, которые я обычно называю просто «q» для «quote», которые возвращают данный тип, соответствующим образом заключенный в кавычки. Для строк это удваивает любые кавычки, затем шлепает кавычки вокруг всего этого. Для целых чисел он просто возвращает строковое представление целого числа. Для дат он преобразуется в стандартный формат даты JDBC (Java SQL), который драйвер затем должен преобразовать во все, что необходимо для конкретной используемой базы данных. И т. Д. (В моем текущем проекте я даже включил массив как переданный тип, который я конвертирую в формат, подходящий для использования в предложении IN.) Затем каждый раз, когда я хочу включить поле в оператор SQL, я просто пишу " д (х)». Поскольку это наложение кавычек, когда это необходимо, мне не нужны дополнительные манипуляции со строками для наложения кавычек, так что это, вероятно, так же просто, как не делать escape.

Например, уязвимым способом:

String myquery = "выбрать имя клиента, где customercode = '" + custcode + "'";

Безопасный путь:

String myquery = "выберите имя от клиента, где customercode =" + q (custcode);

Правильный путь - это не просто печатный текст, а неправильный, так что легко привыкнуть к привычке.

2 голосов
/ 09 апреля 2010

Нет, этого недостаточно. Насколько мне известно, замена строк вообще никогда не может быть достаточной (на любой платформе).

Чтобы предотвратить внедрение SQL, все запросы должны быть параметризованы - либо как параметризованные запросы, либо как хранимые процедуры с параметрами.

В этих случаях библиотека вызова базы данных (то есть ADO.NET и команда SQL) отправляет параметры отдельно от запроса, и сервер применяет их, что исключает возможность какого-либо изменения фактического SQL. Это имеет множество преимуществ помимо внедрения, которые включают проблемы кодовой страницы и проблемы преобразования даты - в этом отношении любые преобразования в строку могут быть проблематичными, если сервер не ожидает их выполнения так, как это делает клиент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...