Причины для подготовленных выражений с параметрами связывания над интерполированными утверждениями с исключенными / заключенными в кавычки параметрами - PullRequest
4 голосов
/ 24 февраля 2020

Для защиты от SQL инъекции рекомендуется использовать подготовленные операторы с значениями связывания . Это гарантирует, что база данных может различать guish между фактическими логами c в SQL (которые должны быть проанализированы, интерпретированы и оптимизированы) и данными (которые не требуют интерпретации) и, следовательно, не будет интерпретировать и выполнять команды, найденные в данных.

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

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

Мой вопрос: есть ли какая-либо причина безопасности, которую вы предпочитаете подготовленные операторы с значениями связывания более экранирование ? И если да, то каковы точные причины?

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

Ответы [ 3 ]

6 голосов
/ 24 февраля 2020

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

$escapedName = EscapeString("O'Reilly")

$sql = "SELECT * FROM MyTable WHERE name = '$escapedName'"

В приведенном выше примере апостроф должен быть экранирован, поэтому он станет WHERE name = 'O\'Reilly' и, следовательно, будет безопасно интерполировать в запрос SQL, не вызывая ошибок.

Однако числа не нужно заключать в кавычки в SQL, и экранирование строки, содержащей апостроф, не будет работать правильно :

$escapedId = EscapeString("123'456")

$sql = "SELECT * FROM MyTable WHERE id = $escapedId"

Это приведет к WHERE id = 123\'456, что по-прежнему является ошибкой.

Вы можете сказать: «Хорошо, поместите число в одинарные кавычки», но это не всегда возможно Например, для предложения LIMIT в MySQL требуются действительные целые числа, а не строка в кавычках, содержащая цифры.

Помимо вышеуказанной проблемы, просто проще писать код с использованием параметров вместо используя escape-код!

Например, вы можете написать код, подобный следующему:

$sql = "INSERT INTO mytable (col1, col2, col3, col4, col5, col6) 
  VALUES ('" . mysqli_real_escape_string($_POST['col1']) . "', " 
  . $mysqli->real_escape_string($_POST['col2']) . "', '" 
  . $mysqli->real_escape_string($_POST['col3']) . "', '" 
  . $mysqli->real_escape_string($_POST['col4']) . ", '" 
  . $mysqli->real_escape_string($_POST['col5']) . "', '" 
  . $mysqli->real_escape_string($_POST['col6']) . "')";

Можете ли вы обнаружить ошибки? С достаточным количеством времени я уверен, что вы можете. Но это замедлит ваше кодирование и может привести к потере зрения при поиске пропущенных символов кавычек и других ошибок.

Но это гораздо проще написать, а потом легче читать:

$sql = "INSERT INTO mytable (col1, col2, col3, col4, col5, col6) 
  VALUES (?, ?, ?, ?, ?, ?)";

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

2 голосов
/ 24 февраля 2020

Целое изложение вопроса приходит для одного древнего серьезного заблуждения

, которое спасает существенные признаки в данных

Честно говоря, бессмыслица.

  • Нет всеобъемлющих "значимых персонажей". Символ, который может оказать разрушительное воздействие на одну часть запроса, если его освободить, может быть столь же безобидным, как и ягненок в другой. И наоборот.
  • Не существует абстрактных всеобъемлющих «данных». Все части запроса различны, но экранирование работает только для одной части.
  • И не существует такой практики , как "использование экранирования для защиты".

Escape предназначен для экранирования специальных символов в SQL строк . И никогда не был предназначен для какой-либо защиты. Это просто технологическая мера, которую ужасно неправильно поняли и с которой плохо обращались. Это все равно что утверждать, что мы используем правильный синтаксис в наших программах исключительно для защиты. Мы следуем правильному синтаксису, чтобы интерпретатор / компилятор понимал наш код. Тоже самое. Экранирование используется для получения синтаксически правильных строк SQL. Которые, конечно, являются инъекционными в качестве побочного эффекта. Но опять же - миссия побега - это не защита.

И здесь возникает проблема Escapeing # 1: строки - не единственные типы данных, которые используются в запросе. В то время как использование экранирования строк в любом другом литерале данных является прямой дорогой к катастрофе.

Более того, даже для строк экранирование является по существу отсоединяемой мерой, которая сама по себе составляет целую банку червей создание вашего кода подвержено всевозможным человеческим ошибкам и создает проблему Escapeing # 2:

Ссылаясь на мою статью по этому вопросу, Почему я должен использовать подготовленные операторы, если экранирование безопасно? :

Как видите, форматирование значения для базы данных фактически разделено на две части, экранирование переменных и цитирование значений в запросе. И именно здесь все маги c случаются является причиной неисчислимых случаев из реальной жизни SQL инъекций.

В вашем упрощенном примере, где весь код связан вместе, трудно пропустить правильную процедуру. Но в реальной жизни код гораздо сложнее и состоит из больших отдельных модулей. И экранирование выполняется в одном модуле, а в другом - цитирование. Или нет. Никто не может сказать на самом деле. Я бы просто поверил, что это значение уже сбежало. Или я просто избежу этого и добавлю дополнительные экранирующие символы в данные. Или я новый разработчик, который не понимает пример, который вы разместили здесь, и я смотрел видео на YouTube, в котором говорилось, что экранирование предотвращает SQL инъекцию. Я знаю, что значение уже было экранировано, поэтому я могу безопасно ввести запрос. И поскольку это целое число, зачем мне тратить на него кавычки?

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

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

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

0 голосов
/ 24 февраля 2020

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

--we could infer that name will vary and type will not
--but we'd have to analyze all queries sent to work this out
SELECT * FROM person WHERE type = 1 AND name = 'john'
SELECT * FROM person WHERE type = 1 AND name = 'mark'
SELECT * FROM person WHERE type = 1 AND name = 'luke'


--we can easily say that type will vary and name will too
--the previously seen queries would infer differently
SELECT * FROM person WHERE type = @t AND name = @n

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

Конечно; это может укрепить вашу решимость отказаться от параметров или использовать подсказки, чтобы БД каждый раз перепланировала запрос, но было бы лучше работать с сервером, а не против него, и использовать методы, чтобы планировать его на основе общих или оптимальные возможные значения


Даже если нам не нравятся планы по настройке в соответствии с тем, что мы знаем о переменных и константах, использование подготовленного выражения должно, по крайней мере, позволять БД компилировать и затем повторно использовать это усилие по компиляции, а не переделывать его, уменьшая количество ресурсов, которые должны go подготовить оператор для выполнения.

Подумайте о своем предложении в терминах языка интерфейса:

Чтобы изменить значение действия переменной, вы можете изменить значение времени выполнения с помощью простого присвоения значения в памяти:

sayHello(string name){
  console.print("hello " + name);
}

var name = console.readString(),
sayHello(name);

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

main(){
  disk.write("sayHello(string name){console.print(\"hello \"" + name +");}", "c:\\temp\\new.lang");
  launchExe("langcompiler.exe", "c:\\temp\\new.lang");
  launchExe("c:\\temp\\new.exe");
}

Смешно самостоятельно изменять программу и перекомпилировать только для изменения значения, используемого в вызове функции, верно?

За исключением того, что сервер db делает с каждым непараметризованным SQL, который он получает, если только требуется определенное усилие, чтобы выяснить, является ли только что полученный запрос в основном таким же, как тот, который он получил X минут за go, за исключением некоторой части данных, извлеките эти данные, подключите их к процессу компиляции за 5 минут go ..

...