Действительно ли это достаточно для предотвращения инжекции запроса при использовании SQL Server?

Я недавно взял проект, в котором я должен интегрироваться с Сервером PHP/SQL. Я ищу самую быструю и самую легкую функцию для предотвращения Внедрения 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);
}

В противном случае, какие дополнительные шаги я должен сделать?


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

5
задан Derek Adair 10 April 2010 в 18:47
поделиться

4 ответа

Лучший вариант: не используйте 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-инъекций - используйте его!

UPDATE: как использовать параметризованные запросы из 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 также может помочь - в ней есть похожий пример вставки данных с помощью параметризованных запросов.

UPDATE: после того, как вы узнали, что работаете на 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();
11
ответ дан 18 December 2019 в 09:06
поделиться

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

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

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

2
ответ дан 18 December 2019 в 09:06
поделиться

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

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

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

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

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

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

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

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

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

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

3
ответ дан 18 December 2019 в 09:06
поделиться

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

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

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

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

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

String myquery="select name from customer where customercode='"+custcode+"'";

Безопасный способ:

String myquery="select name from customer where customercode="+q(custcode);

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

2
ответ дан 18 December 2019 в 09:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: