Допустим, у меня есть такой код:
$dbh = new PDO("blahblah");
$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );
Документация PDO гласит:
Параметры для подготовленных утверждений не должны быть указаны; водитель справится с этим за вас.
Это действительно все, что мне нужно сделать, чтобы избежать SQL-инъекций? Это действительно так просто?
Вы можете принять MySQL, если это имеет значение. Кроме того, мне действительно любопытно использовать подготовленные операторы против SQL-инъекций. В этом контексте меня не волнует XSS или другие возможные уязвимости.
php
security
pdo
sql-injection
Марк Бик
источник
источник
Ответы:
Краткий ответ - НЕТ , PDO не защитит вас от всех возможных атак SQL-инъекций. Для некоторых неясных крайностей.
Я адаптирую этот ответ, чтобы поговорить о PDO ...
Длинный ответ не так прост. Он основан на атаке, продемонстрированной здесь .
Атака
Итак, начнем с показа атаки ...
При определенных обстоятельствах это вернет более 1 строки. Давайте рассмотрим, что здесь происходит:
Выбор набора символов
Для этой атаки на работу, нам нужна кодировка , что сервер ожидают на связи как для кодирования ,
'
как в ASCII , т.е.0x27
и иметь некоторый символ , чьи окончательный байт является ASCII\
т.е.0x5c
. Как выясняется, есть 5 таких кодировок , поддерживаемых в MySQL 5.6 по умолчанию:big5
,cp932
,gb2312
,gbk
иsjis
. Мы выберемgbk
здесь.Теперь очень важно отметить использование
SET NAMES
здесь. Это устанавливает набор символов на сервере . Есть еще один способ сделать это, но мы скоро туда доберемся.Полезная нагрузка
Полезная нагрузка, которую мы собираемся использовать для этой инъекции, начинается с последовательности байтов
0xbf27
. Во-gbk
первых, это недопустимый многобайтовый символ; вlatin1
это строка¿'
. Обратите внимание, что вlatin1
иgbk
,0x27
само по себе является буквальным'
символом.Мы выбрали эту полезную нагрузку, потому что, если бы мы ее вызвали
addslashes()
, мы вставили бы ASCII,\
т.е.0x5c
перед'
символом. Таким образом, мы получим0xbf5c27
, чтоgbk
представляет собой последовательность из двух символов: с0xbf5c
последующим0x27
. Или, другими словами, действительный символ, за которым следует неоткрытый'
. Но мы не используемaddslashes()
. Итак, к следующему шагу ...$ Stmt-> Execute ()
Здесь важно понять, что PDO по умолчанию НЕ делает правильно подготовленные операторы. Он имитирует их (для MySQL). Поэтому PDO внутренне строит строку запроса, вызывая
mysql_real_escape_string()
(функцию MySQL C API) для каждого значения связанной строки.Вызов C API
mysql_real_escape_string()
отличается от того,addslashes()
что он знает набор символов соединения. Таким образом, он может выполнить экранирование правильно для набора символов, который ожидает сервер. Однако до этого момента клиент думал, что мы все еще используемlatin1
для соединения, потому что мы никогда не говорили об этом иначе. Мы сказали серверу, который используемgbk
, но клиент все еще думает, что это такlatin1
.Следовательно, вызов
mysql_real_escape_string()
вставляет обратную косую черту, и у нас есть свободный'
символ зависания в нашем «экранированном» контенте! В самом деле, если бы мы должны были смотреть на$var
вgbk
наборе символов, мы видим:Что именно то, что требуется для атаки.
Запрос
Эта часть просто формальность, но вот обработанный запрос:
Поздравляем, вы только что успешно атаковали программу, используя подготовленные операторы PDO ...
Простое исправление
Теперь стоит отметить, что вы можете предотвратить это, отключив эмулированные подготовленные операторы:
Это обычно приводит к истинно подготовленному утверждению (т. Е. Данные отправляются в отдельном пакете от запроса). Тем не менее, следует помнить , что PDO будет молча запасным вариантом , чтобы эмулировать заявления , что MySQL не может подготовить изначально: те , которые можно будут перечислены в руководстве, но будьте осторожны , чтобы выбрать подходящую версию сервера).
Правильное Исправление
Проблема здесь в том, что мы не вызывали C API
mysql_set_charset()
вместоSET NAMES
. Если бы мы это сделали, мы были бы в порядке, если бы использовали версию MySQL с 2006 года.Если вы используете более раннюю версию MySQL, затем ошибку в
mysql_real_escape_string()
виде , что недопустимые символы многобайтовых , такие как в наших полезных нагрузках рассматривались как отдельные байты для побега целей , даже если клиент был правильно информирован о кодировании соединения и поэтому эта атака будет все еще удастся Ошибка была исправлена в MySQL 4.1.20 , 5.0.22 и 5.1.11 .Но хуже всего то,
PDO
что не раскрывал C APImysql_set_charset()
до 5.3.6, поэтому в предыдущих версиях он не мог предотвратить эту атаку для всех возможных команд! Это теперь выставлено как параметр DSN , который должен использоваться вместоSET NAMES
...Спасительная Грация
Как мы уже говорили, чтобы атака работала, соединение с базой данных должно быть закодировано с использованием уязвимого набора символов.
utf8mb4
это не уязвима , и все же может поддерживать каждый символ Unicode: чтобы вы могли выбрать для использования , что вместо, но он был доступен только начиная с MySQL 5.5.3. Альтернативаutf8
, которая также не уязвима и может поддерживать всю базовую многоязычную плоскость Unicode .Кроме того, вы можете включить
NO_BACKSLASH_ESCAPES
режим SQL, который (среди прочего) изменяет работуmysql_real_escape_string()
. Если этот режим включен,0x27
он будет заменен на,0x2727
а не,0x5c27
и, таким образом, процесс выхода не может создавать допустимые символы в любой из уязвимых кодировок, где они ранее не существовали (то0xbf27
есть все еще и0xbf27
т. Д.), Поэтому сервер все равно отклонит строку как недопустимую. , Однако см . Ответ @ eggyal о другой уязвимости, которая может возникнуть при использовании этого режима SQL (хотя и не с PDO).Безопасные Примеры
Следующие примеры безопасны:
Потому что сервер ожидает
utf8
...Потому что мы правильно установили набор символов, чтобы клиент и сервер совпадали.
Потому что мы отключили эмулированные подготовленные заявления.
Потому что мы установили правильный набор символов.
Потому что MySQLi постоянно делает действительно подготовленные операторы.
Завершение
Если ты:
ИЛИ
utf8
/latin1
/ascii
/ etc)ИЛИ
NO_BACKSLASH_ESCAPES
режим SQLВы на 100% в безопасности.
В противном случае вы уязвимы, даже если вы используете подготовленные операторы PDO ...
добавление
Я медленно работал над патчем, чтобы изменить настройки по умолчанию, чтобы они не эмулировали подготовку к будущей версии PHP. Проблема, с которой я сталкиваюсь, состоит в том, что МНОГО тестов ломаются, когда я делаю это. Одна из проблем заключается в том, что эмулированная подготовка будет генерировать только синтаксические ошибки при выполнении, но истинная подготовка будет вызывать ошибки при подготовке. Так что это может вызвать проблемы (и это одна из причин, по которой тесты не работают).
источник
mysql_real_escape_string
неправильно обрабатывать случаи, когда соединение было правильно установлено на BIG5 / GBK. Таким образом, даже вызовmysql_set_charset()
mysql <5.0.22 будет уязвим для этой ошибки! Так что нет, этот пост все еще применим к 5.0.22 (потому что mysql_real_escape_string является только кодировкой от вызововmysql_set_charset()
, о чем этот пост говорит об обходе) ...NO_BACKSLASH_ESCAPES
также могутПодготовленных операторов / параметризованных запросов обычно достаточно, чтобы предотвратить внедрение 1-го порядка в этот оператор * . Если вы используете непроверенный динамический sql где-либо еще в вашем приложении, вы по-прежнему уязвимы для внедрения 2-го порядка .
Внедрение 2-го порядка означает, что данные были циклически пройдены по базе данных, прежде чем они были включены в запрос, и их намного сложнее выполнить. AFAIK, вы почти никогда не видите настоящих искусных атак 2-го порядка, так как злоумышленникам обычно легче внедрить социальный инжиниринг, но иногда у вас появляются ошибки 2-го порядка из-за дополнительных доброжелательных
'
персонажей или подобных им.Вы можете выполнить атаку с внедрением 2-го порядка, когда можете сохранить значение в базе данных, которое впоследствии будет использоваться как литерал в запросе. Например, предположим, что вы вводите следующую информацию в качестве нового имени пользователя при создании учетной записи на веб-сайте (при условии использования MySQL DB для этого вопроса):
Если нет других ограничений на имя пользователя, подготовленный оператор все равно будет следить за тем, чтобы вышеуказанный встроенный запрос не выполнялся во время вставки, и правильно сохранит значение в базе данных. Однако представьте, что позже приложение извлекает ваше имя пользователя из базы данных и использует конкатенацию строк, чтобы включить это значение в новый запрос. Вы можете увидеть чужой пароль. Поскольку первые несколько имен в таблице пользователей, как правило, относятся к администраторам, возможно, вы также только что раздали ферму. (Также обратите внимание: это еще одна причина не хранить пароли в виде обычного текста!)
Таким образом, мы видим, что подготовленных операторов достаточно для одного запроса, но самих по себе они не достаточны для защиты от атак SQL-инъекций во всем приложении, поскольку у них нет механизма, обеспечивающего полный доступ к базе данных в приложении. код. Тем не менее, используется как часть хорошего дизайна приложения - который может включать в себя такие практики, как анализ кода или статический анализ, или использование ORM, уровня данных или уровня обслуживания, который ограничивает динамические SQL- подготовленные операторы. являются основным инструментом для решения Sql Injection проблема.Если вы следуете хорошим принципам разработки приложений, таким образом, ваш доступ к данным отделен от остальной части вашей программы, становится легко обеспечить или проверить, что каждый запрос правильно использует параметризацию. В этом случае инъекция sql (как первого, так и второго порядка) полностью предотвращается.
* Оказывается, что MySql / PHP (хорошо, были) просто глупы в обработке параметров, когда задействованы широкие символы, и все еще есть редкий случай, описанный в другом ответе с высоким числом голосов, который может позволить инъекции проскользнуть через параметризованный запрос.
источник
Нет, они не всегда.
Это зависит от того, разрешаете ли вы ввод данных пользователя в самом запросе. Например:
будет уязвимым для SQL-инъекций, и использование подготовленных операторов в этом примере не будет работать, потому что пользовательский ввод используется как идентификатор, а не как данные. Правильный ответ здесь будет использовать какую-то фильтрацию / проверку, например:
Примечание: вы не можете использовать PDO для привязки данных, которые выходят за пределы DDL (языка определения данных), то есть это не работает:
Причина , по которой выше делает работу не потому , что
DESC
иASC
не данные . PDO может сбежать только для данных . Во-вторых, вы не можете даже поставить'
кавычки вокруг него. Единственный способ , чтобы позволить пользователю выбрана сортировка в ручном режиме фильтра и убедитесь , что это либоDESC
илиASC
.источник
SELECT * FROM 'table'
может быть не так, как должно быть,SELECT * FROM `table`
или без каких-либо помех. Тогда некоторые вещи, например,ORDER BY DESC
откудаDESC
исходит пользователь, не могут быть просто исключены. Таким образом, практические сценарии довольно безграничны.switch
для получения имени таблицы.Да, этого достаточно. Способ атаки типа инъекций заключается в том, чтобы каким-то образом заставить интерпретатор (базу данных) оценить что-то, что должно было быть данными, как если бы это был код. Это возможно только в том случае, если вы смешиваете код и данные на одном носителе (например, когда вы строите запрос в виде строки).
Параметризованные запросы работают, отправляя код и данные отдельно, поэтому в этом никогда не будет возможности найти дыру.
Вы все еще можете быть уязвимы для других атак инъекционного типа. Например, если вы используете данные на HTML-странице, вы можете подвергнуться атакам типа XSS.
источник
Нет, этого недостаточно (в некоторых конкретных случаях)! По умолчанию PDO использует эмулированные подготовленные операторы при использовании MySQL в качестве драйвера базы данных. Вы должны всегда отключать эмулированные подготовленные операторы при использовании MySQL и PDO:
Еще одна вещь, которая всегда должна быть сделана, это установить правильную кодировку базы данных:
Также см. Этот связанный вопрос: Как я могу предотвратить внедрение SQL в PHP?
Также обратите внимание, что это касается только стороны базы данных того, что вам все равно придется наблюдать при отображении данных. Например, используя
htmlspecialchars()
снова с правильным стилем кодирования и цитирования.источник
Лично я всегда сначала выполняю какую-либо форму очистки данных, поскольку вы никогда не можете доверять вводу пользователем, однако при использовании привязки заполнителей / параметров введенные данные отправляются на сервер отдельно в оператор sql, а затем связываются вместе. Ключевым моментом здесь является то, что это связывает предоставленные данные с конкретным типом и конкретным использованием и исключает любую возможность изменить логику оператора SQL.
источник
Еще раз, если вы собираетесь предотвратить внедрение SQL-инъекций с помощью html или js-проверок, вам следует учитывать, что проверки переднего плана «обходятся».
Вы можете отключить js или отредактировать шаблон с помощью внешнего инструмента разработки (встроенного в Firefox или Chrome в настоящее время).
Таким образом, чтобы предотвратить внедрение SQL-кода, было бы правильно санировать входные данные внутри вашего контроллера.
Я хотел бы предложить вам использовать встроенную функцию PHP filter_input () для очистки значений GET и INPUT.
Если вы хотите продолжить с безопасностью, для разумных запросов к базе данных, я хотел бы предложить вам использовать регулярное выражение для проверки формата данных. preg_match () поможет вам в этом случае! Но будь осторожен! Двигатель Regex не такой легкий. Используйте его только в случае необходимости, иначе производительность вашего приложения снизится.
Безопасность требует затрат, но не теряйте производительность!
Простой пример:
если вы хотите дважды проверить, является ли значение, полученное из GET, числом, меньше 99, если (! preg_match ('/ [0-9] {1,2} /')) {...} тяжелее
Итак, окончательный ответ: «Нет! Подготовленные операторы PDO не предотвращают все виды внедрения SQL»; Это не предотвращает неожиданные значения, просто неожиданное объединение
источник