Как устроить DoS атаку на сервер баз данных одной строчкой

    40 МБит / сек — неплохой трафик для DoS атаки. Именно на столько внезапно вырос входящий трафик до одного из наших серверов. Сайт мужественно держался. Время начала всплеска аномально высокого трафика подозрительно точно совпадало с временем выкладывания одного крупного релиза, что и навело на мысль о том что мы DoS`им себя сами.

    Ситуацию осложняло то, что в релиз попали изменения порядка сотни разных php-файлов и просмотреть весь список изменений было очень трудоёмко. tcpdump помог выяснить что трафик вырос до сервера баз данных PostgreSQL. Круг сужался.

    Первым предположение было то что в одном из SQL-запросов забыли limit и он выбирает весь набор данных вместо одной строки. Но как узнать что это за запрос? На помощь пришёл pgFouine. Анализ лога работы сервера баз данных за две минуты и вот он — источник зла!

    select oid,typname from pg_type

    Вот он — запрос без limit`а, который выполнялся тысячи раз. Но в наших исходниках не было этого запроса! Нигде, ни в одном файле он не встречался.

    Источник проблемы скрывался в php-функции pg_field_type, которая всего навсего возвращала символьное имя типа колонки результата выборки.

    Проблема уходит своими корнями как минимум в 2005 год но до сих пор в официальной документации не добавлено замечание о то, что библиотека php-pgsql для доступа к PostgreSQL делает дополнительный запрос к СУБД выбирая полный список из более чем 1000 типов данных.

    Возможно на редко посещаемом сайте это совсем не сказывается на быстродействии, но на нагруженном проекте это может создать серьёзную проблему. Мы обошли проблему, использовав вместо функции pg_field_type её аналог pg_field_type_oid, которая возвращает внутренний oid типа данных вместо его символьного имени. Да, это немного ухудшило читабельность кода, но разгрузило канал до сервера баз данных на 40 МБит.

    Возможно если бы мы использовали True FastCGI, то у нас бы не было это проблемы, но php стал готов к этому всего несколько месяцев назад, а проблема существует уже около 6 лет.

    Поищите в ваших исходниках по ключевому слову pg_field_type — возможно вы сможете очень облегчить тяжёлую жизнь вашего сервера PostgreSQL.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 24
    • +2
      > Возможно если бы мы использовали True FastCGI, то у нас бы не было это проблемы, но php стал готов к этому всего несколько месяцев назад, а проблема существует уже около 6 лет.

      Да, хотя бы потому что в phpDaemon есть свой независимый драйвер постгреса, написанный прямыми руками :-) Никаких левых запросов там не делается.
      • +4
        PostgreSQLClient в phpDaemon несколько урезанный, например там не поддерживается биндинг переменных. Меньше функционала — меньше глюков :)
        • 0
          Вы правы. Спасибо что обратили на это внимание. Спецификация протокола реализована полностью. Всё поддерживается, но не всё вынесено в отдельные методы. Если кому-нибудь понадобится биндинг, надеюсь напишут с чем его подают в PHP, я никогда не использовал.
          • +2
            Его подают с PDO, например.

            Без биндинга сразу же возникает куча проблем c SQL injects, поэтому как можно жить без него — вообще не представляю. Workarounds типа mysql_real_escape_string на каждый чих иначе, как извращенством, не назовешь…

            Да и вообще, это же так удобно — скормить переменную запросу, будучи точно уверенным в том, что она всегда останентся «просто данными» и никогда не будет перемешана с самим запросом!
      • +2
        Используйте джангу)
        • НЛО прилетело и опубликовало эту надпись здесь
        • +4
          занятная багофича :)

          насколько я помню, из пхп убрали отдельные библиотеки драйверов мускуля и постгри, заменили их пдошными, а пдо встроили в «ядро»
          • +1
            Вы ошибаетесь — расширения для «простых» mysql и pgsql (php_mysql, php_pgsql) присутствуют в php 5.3.4. Равно как и PDO. Другое дело, что все наперебой кричат «истиный Дао в PDO» — это да…
          • 0
            А еще у кодеров принято делать запросы SELECT… LIKE '%something%'. Особенно замечательно это работает в MySQL при, скажем, 10 миллионах строк, когда вырастает база. Такие конструкции не задействуют индексы.
            • 0
              Даже если в WHERE стоит ограничение по ключевым полям?
              Но в главном вы правы — неразумное увлечение разными вкусными фенечками в запросах может дать весьма неприятный побочный результат в виде приличных тормозов.
              • +2
                Верно. А вот запрос вида SELECT… LIKE 'something%' уже будет работать с индексами.
                Мне для одного из проприетарных продуктов пришлось использовать mysql-proxy и скрипт к нему на lua, чтобы реврайтить запросы, поскольку «чудо индийской школы программирования» умирало уже на 10.000 строках в базе, с прокси же удалось выжать все соки из БД, хотя и немного пожертвовав скоростью обработки.
                • +2
                  Или даже SELECT… LIKE '%something' если индекс построить в обратном порядке, есть такая фича
                  • 0
                    Просто добавь OR и получи индексный LIKE в подарок!
                    • 0
                      Тут вы не правы:
                      field like '%text' or field like 'text%'

                      это не то же самое что и
                      field like '%text%'

                      , например 'маша мыла раму' like '%мыла%'
                      • 0
                        Да это шутка была :)
                        • 0
                          :) но на первый взгляд счастье было так близко!
            • +3
              Время начала всплеска аномально высокого трафика подозрительно точно совпадало с временем выкладывания одного крупного релиза, что и навело на мысль о том что мы DDOS`им себя сами.
              Вы себя DoS'или, а не DDoS'или.
              • 0
                Точно! Спасибо сейчас поправлю.
              • –1
                Теперь знаю как тестировать PostgreSQL на нагрузку :)
                • +3
                  Ух, и все-таки с утра немного тяжело воспринимать DOS как DoS… а то прочитав заголовок задумался о том, как можно из-под DOS-а атаковать сервер баз данных одной строчкой… сразу в голове мысль о
                  :while
                  echo "...some..sql" > mysql
                  goto while

                  но это не одна строчка…

                  Но, возможно, это просто утреннее занудство от недосыпа :)
                  • +1
                    :) спасибо, поправил для тех кто читает хабр по утрам
                  • 0
                    Кстати говоря, с PDO есть такая проблема, что в нем пара prepare()+execute() вызывает 2 запроса к PostgreSQL-базе (первый — PREPARE, второй, соответственно, EXECUTE). Это означает и удвоение времени на мелких запросах (двойной пинг), и «подвисшие в воздухе» PREPARE в случае, если почему-либо до execute() дело не дошло, а соединение не успело открыться. Кроме того, опять же для PostgreSQL, PDO-шная пара prepare()+execute() убивает оптимизацию запроса на основе значений параметров (план запроса строится на этапе PREPARE, когда о реальных значениях параметров еще ничего не известно), так что ее еще и поэтому не стоит применять.

                    Так что для PostgreSQL единственный путь — это руками заменять placeholder-ы на заквоченные значения и посылать запрос в один прием. В этом отношении, получается, PDO теряет всю свою привлекательность по сравнению с pg_* функциями.
                    • 0
                      Отнаследоваться от PDO изменив реализацию prepare/execute?

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