10+ способов обрушить mysql-сервер

Иногда у меня спрашивают об ошибках MySQL, (например таких), которые могут привести к падению mysql-сервера, вызванного пользователем с обычными привилегиями. Потом звучит вопрос: “Что же делать в таких случаях? Как защититься от подобных ситуаций?”

Мой ответ “Сядьте и расслабьтесь :)”

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

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

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

Такое положение вещей будет сохраняться все время, пока MySQL использует Глобальное Управление Ресурсами, и вы реально не можете контролировать количество ресурсов, доступных вашим пользователям.

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

Дам вам несколько советов:
Временные таблицы вы можете использовать запросы с производными таблицами и создавать в памяти неограниченное количество временных таблиц с неограниченным размером.

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

MyISAM Sort Buffer - обычно его размер устанавливают довольно большим, но он может одновременно работать только с несколькими таблицами. А что будет, если пользователь использует все его дозволеные 100 подключений для инструкции ALTER для 100 разных таблиц? Можно искусственно ограничить его занижением значения параметра myisam_sort_buffer_size, но это отразится на производительности.

Количество подготовленных инструкций (Prepared Statements) - к счастью теперь есть лимит на максимальное количество подготовленных инструкций (параметр max_stmt_count), который задается сервером. Это лучше, чем было раньше, когда можно было заставить сервер использовать всю доступную память, просто забыв закрыть подготовленные инструкции. Однако ограничения для пользователя отсутствуют и один пользователь может исчерпать весь лимит, заблокировав доступ к использованию подготовленных инструкций для остальных. Кроме того, не все инструкции занимают одинаковое количество памяти и подготовка сложных инструкций может отнять бoльшую часть ресурсов. Решить проблему можно, ограничив использование подготовленных инструкций и установив параметр max_prepared_stmt_count в очень низкое значение.

Подготовленные инструкции и BLOB-данные - если вы хотите получить память, занятую одной подготовленной инструкцией, вы можете создать инструкцию с тысячей меток (placeholders) и посылать данные каждой из них, используя вызов mysql_stmt_send_long_data. Буферы сервера будут принимать данные до тех пор, пока инструкция не будет выполнена.

Утечки кэша таблиц Innodb - InnoDB никогда не ограничивает размер внутренних таблиц в кеше (словарь данных). Так что открывая большие таблицы и работая с ними вы можете исчерпать всю доступную память. Обычно размер 4-8К на таблицу, но сложные таблицы могут занимать и больше. В основном это проблема небольших серверов.

Кэш Слитых таблиц - кэш таблиц обычно распределен и каждая запись обычно соответствует не более, чем паре файловых дескрипторов. Но не в случае Слитых таблиц - создание и доступ к нескольким слитым таблицам с, например, тысячей подтаблиц не лучшим образом скажется на вашем сервере. Тоже самое относится и к Разделенным талицам из MySQL 5.1

Место на диске - для MyISAM-таблиц хостинг-провайдеры обычно используют дисковые квоты. Вы можете использовать похожий подход при помощи innodb_file_per_table. Однако вы не можете контролировать рост системных таблиц InnoDB, которые используются для хранения данных об отменах и которые могут увеличиваться с каждой открытой транзакцией и делать множество обновлений. Или просто сохранять транзакцию открытой и позволять другим пользователям делать обновления - InnoDB может только очистить данные после старых транзакций, нуждающихся в мгновенном коммите. Вы можете решить этот вопрос путем уничтожения слишком старых транзакций, но более верным решением будет установление некоего лимита на объем хранимой информации о отменах. Еще один неплохой способ - это использовать запросы с большими временными таблицами или сортировкой файлов. Даже если временные таблицы будут храниться на другом разделе, при его переполнении другие пользователи не смогут нормально работать с сервером.

Хранимые процедуры - сколько памяти можно выделить для хранимой процедуры? Можно создать 1000 переменных в хранимой процедуре и зарезервировать для каждой их них по 1М памяти. Я не проводил целенаправленных экспериментов, но думаю, что жесткой политики выделения памяти для хранимых процедур нет.

Указатели в хранимых процедурах - указатели в хранимых процедурах реализованы в виде временных таблиц. Поэтому используя большое количество указателей можно заполнить доступный объем оперативной памяти.

Рекурсия в хранимых процедурах - На самом деле отличается от “классического” представления рекурсии. Это всего лишь вызовы одной процедуры из другой. Которые так же требуют выделения памяти и, что важно, размещаются в стеке. Есть некоторые способы для защиты от переполнения стека, но они не могут гарантировать 100%-ного результата.

Переменные на стороне сервера - каждый сервер имеет ограничение в виде директивы max_allowed_packet (1M по умолчанию). Но, по-видимому, нет никаких ограничений на количество создаваемых переменных.

Разбор деревьев - внутренне представление запросов в MySQL выглядит как разбор дерева, которое зависит от размера запроса, который, в свою очередь, контролируется директивой max_allowed_packet. Но некоторые способы оптимизации MySQL (такие как equity propagation и range expansion) могут привести к критическим ошибкам при анализе дерева. Для нескольких тривиальных случаев такое поведение было исправлено, но вызывает сомнения, что эти исправления актуальны для всех подобных ситуаций.

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

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

Взаимные исключения - как InnoDB, так и MyISAM имеют “хотспоты” с несколькими соединениями. Используя их можно существенно замедлить работу сервера.

Перегрузка - у MySQL нет достаточного контроля за использованием ресурсов и вы можете “положить” сервер просто выполняя тяжелые запросы. Существующие ограничения не могут полностью контролировать потребление ресурсов пользователем, поскольку не имеют механизма определения “тяжести” запроса. Тяжелые запросы могут сильно нагрузить систему ввода/вывода и заполнить кэш ОС, что заметно снизит скорость работы сервера и запросы других пользователей будут выполняться на порядок медленнее.

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

Как вы видите, MySQL-сервер отнюдь не выглядит “пуленепробиваемым”, если кто-то намеренно будет пытаться его обрушить. Дело в том, что большинство существующих ограничений (таких как max_heap_table_size или max_prepared_stmt_count) реализованы для защиты от типичных ошибок при разработке приложений, а отнюдь не от запланированной атаки

Примечание: я рассмотрел лишь некоторые из объектов сервера, т.е. снижение производительности при некорректной работе с каждым из них. Существует ряд глобальных квот, которые влияют на работу сервера в целом - вы не можете применить их для конкретного пользователя или соединения.

P.S.: вы можете подумать: “как же это все возможно, если существуют тысячи хостинг-провайдеров, предоставляющих доступ к своим серверам БД?” Да, некоторым везт и их пользователи не создают проблем для корректной работы MySQL, но большинству приходится “вручную” постоянно контролировать и ограничивать пользователей, мешающих нормальному функционированию. Не говоря о компаниях, предоставляющих вирутальный хостинг - там, как правило, используются старые версии ПО, имеющие куда больше проблем.

P.P.S.: ничего из вышеописанного не является “новостью” для команды разработчиков MySQL. Эта заметка - лишь попытка осветить некоторые аспекты безопасности и обеспечения корректной работы MySQL-сервера.

Оригинал: http://www.mysqlperformanceblog.com/2007/11/13/10-ways-to-crash-or-overload-mysql/


 

Comments: 6

  1. fuxx November 21st, 2007 at 3:31 pm

    и ощибки эти очевидны.
    не Щ, а Ш

  2. fuxx November 21st, 2007 at 3:36 pm

    полготовленные инструкции
    Л => Д

  3. admin November 21st, 2007 at 3:45 pm

    Спасибо за комментарий, вроде все опечатки поправил

  4. DjSlavian December 22nd, 2007 at 11:20 pm

    “Иногда у меня спрашивают о ошибках MySQL, (например таких), которые могут привести к обрушиванию”

    Об ошибках; и к обрушению скорее всего!

  5. boombick December 23rd, 2007 at 9:40 am

    Спасибо, поправил =)

  6. prist April 11th, 2008 at 4:08 pm

    “По-моему, девиз безопасности MySQL должна выглядеть так”
    девиз должен выглядеть

Add a Comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>