Уязвимости в MySQL и SQL запросах

MySQL

Автор: Александр Степанов

1 нояб. 2011 г., 09:43:57  2244


Эту статью я прочитал на simplecoding.org и призадумался. В большинстве web приложений, которых я видел, не производят проверку на длину входях данных. Слабое место это все, что связано с личными данными пользователя, авторизация, регистрация, восстановление пароля от учетной записи etc.

SQL-инъекции (SQL-injection) на сегодняшний день остаются наиболее обсуждаемыми проблемами безопасности web приложений.


В тоже время, другие уязвимости SQL запросов, например, связанные со слишком длинными входными данными, обычно игнорируются, хотя могут привести ко всем видам проблем безопасности.

Первая уязвимость касается движка MySQL

В MySQL существует параметр, который называется max_packet_size. По-умолчанию ему присвоено значение 1МБ.

В MySQL >= 5 он называется max_allowed_packet и тоже принемает значение 1МБ. Эта директива меняется в зависимости от возможности сервера и зп админов).

Он контролирует максимальный размер пакетов, которые передаются между SQL клиентом и сервером. Если запросы или результат их выполнения не укладывается в размер пакета – возникает ошибка. Это означает, что слишком длинные SQL запросы никогда не отправляются серверу и, таким образом, никогда не выполняются.
Это может привести к проблемам безопасности, если хакер может передать большие блоки данных, которые потом используются в SQL запросах.
Например, представьте запросы авторизации, которые объединяют информацию из HTTP заголовков, id сессий и логов. В результате получается большой запрос, который не укладывается в размер пакета.
Еще один пример – очистка таблицы сессий, при которой сначала создается PHP массив из всех сессий, которые соответствуют определенным параметрам, после этого выполняется очистка, а в конце создается один большой запрос на удаление со всеми id выбранных сессий. Очевидно, что такой запрос может стать слишком длинным.

Тут может быть не только очистка таблиц, но и неграмотно составленные запросы, например предложение IN в SQL или операции с BLOB полями данных.
  • Максимальный размер объекта типа BLOB или TEXT определяется его типом, но наибольшее значение, которое фактически может быть передано между клиентом и сервером, ограничено величиной доступной памяти и размером буферов связи. Можно изменить размер буфера блока передачи, но сделать это необходимо как на стороне сервера, так и на стороне клиента.
Типы данных BLOB и TEXT
Требования к памяти для различных типов столбцов
Настройка параметров сервера

Таким образом, web разработчики должны всегда проверять длину запросов. И тут не играет роли, каким образом выполняется подготовка запроса.

 



Вторая уязвимость касается длины столбцов.


Если не проверять длину данных, полученных от пользователя, может возникнуть уязвимость, связанная с отбрасыванием части данных при вставке в таблицу (в оригинале — SQL Column Truncation Vulnerabilities).

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

В MySQL можно активировать режим sql_mode STRICT_ALL_TABLES чтобы превратить эти предупреждения в ошибки (errors). Но большинство web приложений работают на серверах, запущенных в обычном режиме.

Таким образом, необходима проверка длины данных, полученных от пользователя.

Например, представьте такую ситуацию:

  1. web приложение представляет собой форум;
  2. имя администратора известно, например, «admin»;
  3. MySQL работает в обычном режиме;
  4. в приложении отсутствует ограничение на длину имени пользователя;
  5. длина поля в базе данных равна 16-ти символам.
Последние условие практически не встречается, минимальной длиной записи в БД я видел 25 символов, но это не столь важно. Вообще, разработчики не жалеют места в БД и пишут сразу 255(256) символов. Если же брать MySQL >= 5, то поле varchar увеличено уже до 1024 символов. Что, конечно, не сравниться с 16 символами для примера.

Дальнейшие действия:
Хакер может попытаться зарегистрироваться под именем «admin » (с пробелом в конце), но это ему не удастся, если выполняется проверка на существование такого имени.
Дело в том, что MySQL не сравнивает данные в двоичном режиме. По-умолчанию используется более гибкая система проверок. При которой пробелы в конце строк отбрасываются. Это означает, что строки «admin» «admin » равны.
Т.е. запрос:

SELECT * FROM user WHERE username='admin '

вернет данные реального администратора и в регистрации с таким именем будет отказано.
Но что произойдет, если хакер использует имя «admin х» (одиннадцать пробелов после слова admin и в конце буква «х»)? Такое имя найдено не будет, потому что невозможно найти пользователя с именем из 17 символов, если поле базы данных имеет ограничение в 16 символов.

Т.е. приложение добавит пользователя с таким именем в базу. Но при этом имя будет обрезано до ширины столбца. Т.е. MySQL отбросит последнюю букву «х» и имя превратится в «admin » (одиннадцать пробелов в конце).
Вот тут и возникает уязвимость. Например, для авторизации пользователя используется следующий код.

$userdata = null; if (isPasswordCorrect($username, $password)) { $userdata = getUserDataByLogin($username); … }

Тут не совсем верно представлено получение данных о/от пользователя. Поскольку
  1. принято хранить/использовать данные о пользователе ввиде ID/внешнего ключа на таблицу с профилем.
  2. тут нужно еще учитывать выборку из БД.

Если функция isPasswordCorrect использует запрос вида:

SELECT username FROM users WHERE username = ? AND passhash = ?

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


После этого выполняется функция getUserDataByLogin, которая обычно содержит запрос вроде

SELECT * FROM users WHERE username = ?

Но у нас два имени «admin», которые MySQL считает одинаковыми. Значит в результате выполнения предыдущего запроса, мы получим две записи. И при этом запись настоящего администратора будет первой, т.к. она находится в начале таблицы.


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



Статья не притендует на оригинальность, все лишь перевод оригинала, который писался очень давно, НО не стоит забывать о выше сказанном.


Ссылки

http://www.simplecoding.org/uyazvimosti-v-mysql-i-sql-zaprosax.html