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

21 августа, 2008
mysql vulnerabilities

Эта заметка – вольный перевод статьи Stefan Esser MySQL and SQL Column Truncation Vulnerabilities.

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

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

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

В MySQL существует параметр, который называется max_packet_size. По-умолчанию ему присвоено значение 1МБ. Он контролирует максимальный размер пакетов, которые передаются между SQL клиентом и сервером. Если запросы или результат их выполнения не укладывается в размер пакета – возникает ошибка. Это означает, что слишком длинные SQL запросы никогда не отправляются серверу и, таким образом, никогда не выполняются.

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

Например, представьте запросы авторизации, которые объединяют информацию из HTTP заголовков, id сессий и логов. В результате получается большой запрос, который не укладывается в размер пакета.

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

Таким образом, 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-ти символам.

Хакер может попытаться зарегистрироваться под именем «admin » (с пробелом в конце), но это ему не удастся, если выполняется проверка на существование такого имени.

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

Т.е. запрос

  1. SELECT * FROM user WHERE username='admin '

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

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

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

Вот тут и возникает уязвимость. Например, для авторизации пользователя используется следующий код.

  1. $userdata = null;
  2. if (isPasswordCorrect($username, $password)) {
  3.    $userdata = getUserDataByLogin($username);
  4.    …
  5. }

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

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

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

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

  1. SELECT * FROM users WHERE username = ?

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

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

Заключение.

Обойти эти проблемы не сложно. Достаточно просто проверять длину запросов и длину данных, полученных от пользователя.

А что вы думаете об этих проблемах?

Понравилась статья? Подписывайтесь на продолжение rss link !

Или на мой твиттер twitter link

]]>

Добавьте эту страницу в google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

]]>

Опубликовано в MySQL, PHP, Web разработка, Безопасность Комментарии (14) »

]]>

Комментарии (14)

Вы можете отслеживать обсуждение записи с помощью RSS 2.0 rss link

Вы также можете оставить комментарий, или трекбек с Вашего сайта.

]]>
  1. dizek

    Спасибо, полезно )

  2. Taral

    Спасибо большое за статью. Я много читал статей про инфекцию SQL запросов. Но все они были про скобки ' или ". И ничего нового не говорили.. А Вы описали правда гибкий взлом базы. Теперь буду осторожнее :)

    • Честно говоря, большинство "дыр" как раз и возникает из-за кавычек и спецсимволов SQL.

      • Taral

        совершенно согласен. Но почти любая библиотека для работы с базой сводит на нет все эти "дыры". И потому особого интереса на представляет.

        • сводит на нет все эти "дыры"

          Только если их правильно использовать. Практически все библиотеки позволяют выполнить "сырой" запрос.

  3. Владимир

    Отличная статейка

  4. Maxim

    Хорошая статья.
    Меня интересует такой вопрос (немного не по теме)
    каким образом можно установить длинну имя поля
    т.е мне надо реализовать довольно сложные таблицы имина полей которых я не занаю(будет добавлять админ) при этом имя проеобразуется

    function code_hex($strim)
    {
    $tmp=$strim ; $tmp=bin2hex($tmp); return $tmp;}
    function decode_hex($strim)
    {
    $tmp=$strim ; $tmp=pack("H*",$tmp); return $tmp;}

    т.е имя возрастает в два раза но позволяет использовать любые символы.
    ?

    • Если честно, я не совсем понял, зачем нужно так преобразовывать строки.
      А длину любой строки можно проверить так:
      if (strlen($strim) > $max) {
      //ошибка
      }
      else {
      //создаём поле
      }

  5. А задуматься стоит!
    Спасибо!

  6. Taral

    Например? Я использую стандартную библиотеку CodeIgniter. И как же так прокинуть скобки внутрь?

  7. Стандартный пример

    $this->db->query('SELECT * FROM users WHERE name='.$_POST['name'].' AND pass='.$_POST['pass']);

    В таком запросе нужно использовать функции
    $this->db->escape()
    $this->db->escape_str()

    Но если вы перепишите запрос так:
    $sql = "SELECT * FROM users WHERE name = ? AND pass = ?;
    $this->db->query($sql, array($_POST['name'], $_POST['pass']));
    то CI автоматически экранирует все элементы массива.

    Тоже самое происходит и при использовании Active Record Class.

    Вместо $_POST['name'] лучше использовать $this->input->post('name');

  8. Taral

    Ну если постаратся то можно) Только если так писать запросы то зачем вообще использовать библиотеку?)
    Я использую связывание методов. $this->db->select('fff')->from('dddd')…
    Очень удобно и гибко. Ладно что тут спорить. Я говорил не про то как можно сделать ошибку используя библиотеку, а насколько важно знать про инфекцию через скобки используя библиотеки (используя их нормально)) ).

  9. ну, вы спросили как, я привел пример :-)
    А вообще-то вы правы, врядли кто-то будет писать запросы вручную, после того как познакомится с библиотекой.
    Как минимум будет использовать подстановку через знаки вопроса.

]]>

Оставить комментарий

* - обязательные для заполнения поля

]]>