-1

Известно, что $_GET или $_POST параметры нужно фильтрировать при запросах mysql, иначе это считается уязвимостью sql inj, однако несовсем понятно, почему в условиях можно не фильтрировать глобальные переменные?

Например if(isset($_GET['blabla'])){ // почему это не считается уязвимостью? }

Kromster
  • 13,809
User
  • 1
  • 2
    если используется PDO, а его давно завезли, то никаких sql injection не будет. А фильтровать и валидировать нужно все, что приходит от клиента. – tcpack4 Aug 08 '17 at 06:01
  • А что такого пользователь может запихнуть в blabla, в результате чего в этой строчке случится что-то плохое? – andreymal Aug 08 '17 at 06:02
  • Ну например какой нибудь php код который сначала закрывает условие и выполняет произвольный код, ведь в mysql же можно переделать запросы, но не понятно, почему многие люди не фильтрируют глобальные переменные в условиях. – User Aug 08 '17 at 06:06
  • 1
    Эм, и каким образом этот php код выполнится по-вашему? – andreymal Aug 08 '17 at 06:08

2 Answers2

1

Почему SQL-инъекции существуют

Сразу же отмечу, что их не существует, если используется корректно настроенные mysqli или PDO с подготовленными выражениями, так как там всё экранируется автоматически. (В некорректно настроенные, например, с неправильной кодировкой, инъекция может просочиться; подробнее см. Защищают ли подготовленные выражения/переменные полностью от SQL инъекций?)

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

$user = $_GET['username'];
$sql = "SELECT email FROM users WHERE username = '$user'";

если в параметре username передать 0' UNION SELECT password FROM users;--, то в итоге получается вот такая строка:

SELECT email FROM users WHERE username = '0' UNION SELECT password FROM users;--'

Эта строка как есть передаётся в mysql-сервер. Он эту строку парсит и выполняет всё, что в ней записано: он не знает происхождения этой строки, он не знает, что вы туда подставили параметр от пользователя. Всё, что ему остаётся, — это покорно выполнить всё, что его попросили, а его попросили выдать все пароли (хорошо, если они будут захэшированные с солью). Поэтому здесь получается инъекция.


Почему код вида if(isset($_GET['blabla'])) безопасен и не содержит инъекции

В случае с php-кодом вместо строки у вас есть готовый php-файл. В этот php-файл не подставляется никаких параметров извне: никаких строк, как в случае с SQL-кодом выше, не собирается.

Ещё перед тем, как загрузить $_GET и $_POST, php-движок парсит php-файл и строит примерно такую последовательность инструкций (описываю условно, в реальном байткоде не ковырялся):

  • достать из массива $_GET значение по ключу 'blabla'

  • вызвать функцию isset, передав ей первым аргументом значение из предыдущего шага, и получить возвращённое ей значение

Функция isset просто сравнивает полученное значение с NULL и больше ничего не делает.

Только после того, как php-движок распарсит файл и составит такую инструкцию — только тогда будут загружены пользовательские данные и будет выполнен этот код.

Ни на одном этапе в данной инструкции содержимое $_GET['blabla'] не интерпретируется как php-код, isset тоже не интерпретирует php-код, поэтому независимо от того, что пользователь запихнёт в GET-параметр blabla — этот код всегда будет безопасен.


Когда же возможна php-инъекция?

Когда в php-файле явно прописано интерпретировать значение как php-код. Например, с использованием eval или assert.

В общем, вам нужно знать, интерпретируют ли функции, которые вы вызываете, параметры как какой-то код или нет. Если нет, то, скорее всего, вы в безопасности. Код из переменных никогда не выполнится сам по себе.

andreymal
  • 13,178
-1

php code sniffer для WordPress в phpStorm требует применять к глобальным переменным $_GET или $_POST две функции: одну из семейства sanitize_xxx(), вторую - verify_nonce().

В семейство sanitize_xxx() входят: sanitize_email(), sanitize_key(), sanitize_text_field(), sanitize_title(). Они производят очистку входных данных, полученных по $_POST или $_GET.

verify_nonce производит проверку одноразового кода nonce, чтобы предотвратить подделку запроса.

isset($_GET['blabla']) не является уязвимостью, потому что всего лишь проверяет, установлена глобальная переменная или нет. А вот код

$bla = $_GET['blabla']

будет отмечен php sniffer как недопустимый.

KAGG Design
  • 35,238
  • Что-то я не понял, как отфильтровывание всего кроме a-z0-9_- относится к данному вопросу? – andreymal Aug 08 '17 at 06:52
  • Функция очищает строку, чтобы использовать её как ключ. Ключи используются как разные внутренние ID. Относится к данному вопросу впрямую, потому что code sniffer для phpStorm, полагаю, делали люди более опытные, чем мы тут. – KAGG Design Aug 08 '17 at 06:54
  • 1
    Где в вопросе вообще упоминаются ключи? Я до сих пор не вижу абсолютно никакой связи между вопросом и ответом. – andreymal Aug 08 '17 at 06:59
  • Эту фильтрацию code sniffer требует применять к глобальным переменным, полученным через $_GET или $_POST – KAGG Design Aug 08 '17 at 07:00
  • Под глобальными переменными автор имеет в виду сами $_GET и $_POST, никаких новых глобальных переменных автор не создаёт и не использует. Пример кода же в вопросе есть. Продолжаю не видеть связи между вопросом и ответом – andreymal Aug 08 '17 at 07:03
  • А значения таким образом фильтровать недопустимо, это вы уже должны и так понимать. А ключи в вопросе не упоминаются – andreymal Aug 08 '17 at 07:06
  • Так фильтруют ключи, есть и другие функции для фильтрации. Вопрос называется "Почему в условиях можно не фильтрировать глобальные переменные?". Я отвечаю, что нельзя не фильтровать, по крайней мере, если хочешь, чтобы код соответствовал стандартам. – KAGG Design Aug 08 '17 at 07:34
  • Рекомендую вам читать весь вопрос перед написанием ответа ;) В вопросе написано, цитирую буква в букву: if(isset($_GET['blabla'])){ // почему это не считается уязвимостью? } — в вашем ответе я не вижу ответа на этот вопрос (или опровержения, что это на самом деле уязвимо) – andreymal Aug 08 '17 at 07:40
  • Вы опустили слово "например". Думаю, намеренно. Я дал ответ на весь вопрос, а не на один пример. Рекомендую вам не заниматься схоластикой, а подискутировать с создателями code sniffer. – KAGG Design Aug 08 '17 at 07:46
  • Ну зато вы наконец-то ответили на вопрос хоть как-то :) Я уже почти собрался отправлять тревогу. – andreymal Aug 08 '17 at 07:58
  • @andreymal да отправляйте хоть две. Я уже давно заметил - те, кто не способны ответить на вопрос хоть как-то, юродствуют в комментариях. – KAGG Design Aug 08 '17 at 08:25
  • Это лучше, чем когда те, кто не способны ответить на вопрос, отвечают на вопрос ;) – andreymal Aug 08 '17 at 08:28
  • @andreymal кто из нас способен отвечать на вопросы, показывает репутация – KAGG Design Aug 08 '17 at 08:37
  • 1
    Скорее она просто показывает активность на сайте – andreymal Aug 08 '17 at 08:39
  • @andreymal оставьте себе ваши самооправдания. Ваш удел - троллинг, а за него репутацию не начисляют. – KAGG Design Aug 08 '17 at 08:43