В сентябре Почта Mail.Ru включила HTTPS-шифрование для всех пользователей.
Преимущества защищенного соединения очевидны всем разработчикам крупных интернет-проектов. Большинство современных web-серверов (nginx, Apache, etc) и браузеров поддерживают HTTPS. В то же время сайтов, на которых безопасный протокол включен всегда и по умолчанию, не так много. Почему это так? С какими трудностями мы столкнулись при поддержке HTTPS? Читайте под катом.
Особенности работы SSL на высоконагруженных системах
По сути, HTTPS представляет собой обычный протокол HTTP поверх сетевого слоя SSL, который позволяет небезопасный сетевой канал сделать безопасным за счет шифрования.
Что нужно сделать, чтобы поддержать SSL?
Казалось бы, достаточно заказать сертификат, разложить его на серверы, включить опцию в nginx, — и добро пожаловать в мир защищенного трафика. Но в реальности все не так просто. Вернее, не так: все было бы просто, если бы нам зачем-то потребовалось обезопасить статическую страничку с текстом «Hello world». А вот для высоконагруженной системы с большим количеством разнообразного контента, какой является Почта Mail.Ru, все самое интересное начинается как раз после того, как сертификаты уже приобретены и разложены.
В чём отличие Почты Mail.Ru от гипотетической страницы с «Hello world»? По сути, с точки зрения поддержки HTTPS основных отличий два. Во-первых, Почта содержит множество страниц – десятки: список писем, сами письма, настройки, адресную книгу, и т.д., и на каждой странице сотни элементов (картинки, JS-ники, CSS-ы и т.д.). Во-вторых, Почта работает под гигантской нагрузкой, обрабатывая сотни тысяч HTTP-запросов в секунду (включая, разумеется, и запросы на статику). Вот из-за этих двух аспектов реализация поддержки HTTPS и была такой непростой.
Как эти аспекты влияют на поддержку? Первый аспект влияет следующим образом: когда страница одна, у сервера есть сертификат, браузер его понимает, всё работает. Проблемы начинаются в том случае, когда страниц много и, более того, когда на этих страницах есть внешние элементы, которые выполняются в контексте этой страницы. Браузер считает, что соединение безопасное, только если все элементы до последнего на странице были отданы по безопасному протоколу: это касается всех картинок, JS, CSS и т.д. Если хоть один элемент был отдан по небезопасному протоколу, браузер будет считать небезопасной всю страницу целиком и информировать об этом пользователя.
Вторая проблема, которую создает большая нагруженность Почты, состоит в том, что шифрование и дешифрование достаточно дорогая операция — как с точки зрения процессора, так и с точки зрения памяти. Процессорное время тратится на шифрование и дешифрование. Память тратится на SSL-кэш, увеличенные буферы nginx-а, большое количество воркеров веб-серверов, которые дольше висят в памяти из-за более долгой обработки запросов. SSL – это такой зверь, который съест много «камня» и памяти и не подавится при этом.
Как с этим бороться?
Баннерная система
Почта — это гетерогенная конструкция. У нас есть собственная баннерная система. Это не только почтовая система, а общая баннерная система портала Mail.Ru, которая ничего ни о каком HTTPS не слышала. Учитывая, что нам пришлось отдавать по HTTPS вообще все данные со всех страниц, баннерную систему пришлось существенно доработать.
Помимо того, что мы научили веб-серверы отдавать по HTTPS все баннеры, скрипты и статику, нам пришлось иметь дело с партнерскими пикселями. Пиксель — это картинка 1х1, которую предоставляет партнер, и которая дергается в тех случаях, когда показывается баннер. Надо отметить, что далеко не все наши партнеры работают по HTTPS, а, как я уже отмечал, небезопасных элементов на странице быть не должно, даже если они размером 1х1 пиксель. Можно было, конечно, пропускать все пиксели через прокси, но мы пошли сначала более простым путем. Договорились с партнерами, объяснили им, насколько это все замечательно и нужно, и теперь они поставляют нам HTTPS-ные пиксели. Дело это было, понятно, непростое, но это сработало. Правда, теперь необходимо и новых партнеров учить тому, что ссылка на картинку должна быть безопасной.
С баннерами есть еще одна проблема: их могут вводить не только технические специалисты, но другие сотрудники с различным уровнем компьютерной грамотности. Соответственно, существует вероятность, что вместе с баннером на странице вполне могут появиться не SSL-ные картинки, JS-ы, небезопасные счётчики.
Для решения этой проблемы мы единоразово прошлись по всем баннерам и заменили все небезопасные на безопасные. Однако это не даёт гарантии, что в будущем ничего не поменяется. Поэтому мы сделали доработку в баннерной системе: создали бит SSL-ready, который назначается каждому проекту. Пока он установлен только у Почты. Наличие этого бита говорит о том, что на этом проекте не будут показывать баннеры, в которых есть небезопасный контент. Всем баннерам, которые показывались в Почте, также проставлялся бит SSL-ready. И наша система для таких баннеров запрещает на уровне ввода в админку менять безопасный контент на небезопасный или добавлять небезопасный контент. Это полностью устранило человеческий фактор при редактировании баннера: если кто-то создаёт новый баннер с небезопасным контентом, он не сможет его показать в Почте, потому что он не сможет пометить его как безопасный. А если человек меняет старый баннер, который уже показывается в Почте, админка не даст сделать его небезопасным.
Повсеместный HTTPS
Далее нам пришлось поддержать HTTPS везде, где это возможно. Контента, который должен отдаваться по HTTPS, очень много, и он разнороден: это все картинки, статика, аватарки, аттачи. Одно дело — поддержать SSL на серверах, которые эти картинки отдают, другое дело — обеспечить корректность и протоколонезависимость ссылок (режим HTTPS является опциональным, поэтому ссылка должна быть правильной вне зависимости от того, является текущий протокол безопасным или нет). Соответственно, нам пришлось менять значительное количество не заточенных под это шаблонов. Мы работали полуавтоматическим способом: сначала через скрипты, потом всё зачищалось руками. Естественно, существенным изменениям подвергся тест-план: теперь наши тестировщики все это проверяют, правят, прогоняют автотесты и так далее.
Помимо всего этого мы разработали прокси для картинок. Дело в том, что большинство картинок, приходящих пользователю в письмах, внешние, а заставить весь интернет работать по HTTPS мы пока не в силах. Поэтому была создана быстрая, лёгкая, однопоточная прокси, которая пропускает через себя все внешние ссылки и выдает пользователю в браузер всегда HTTPS-ный контент.
Как это работает. Прокси получает HTTP-шный URL и получает по нему или контент, или редирект. Во втором случае прокси уходит по редиректам до какого-то значения, прописанного в конфиге. Если она в итоге получает HTTPS-редирект, она выдаёт его клиенту, то есть в браузер. Браузер показывает картинку оттуда напрямую. Если в конце выдаётся контент, то весь контент картинки мы стягиваем к себе, оборачиваем его в SSL и отдаём браузеру. Таким образом, все картинки показываются абсолютно безопасно, даже если они из небезопасного места. Отмечу, что мы сделали антибрутфорс и защиту от злоумышленного использования прокси.
Кроме того, нам пришлось перевести на SSL часть проекта Мой Мир, с которого Почта получает аватарки, а также серверы, на которых развернута система логирования. Также на SSL перешел Веб-Агент и серверы, с которых подгружаются аттачи и их превью.
Еще из того, что интересно отметить, мы сделали умную поддержку SSL на мобильных устройствах. Не все мобильные устройства корректно работают с SSL, и мы разработали систему, определяющую тип устройства из кода на стороне сервера, и действующую в зависимости от результата.
В настоящий момент поддержка включена на iPhone и на iPad, у других мобильных устройств есть некоторые проблемы, над которыми мы работаем. Недалек тот час, когда на всех современных мобильных устройствах Почта Mail.Ru будет работать по умолчанию через HTTPS.
Оптимизация
Ну, и последнее, про что я хотел рассказать — оптимизация. Можно было бы, конечно, накупить железа и покрыть возросшие затраты по процессорному времени и памяти, но это не наш метод.
Оптимизировали, как обычно, начиная с самых узких мест. Какое самое узкое место в SSL? Это коннект, потому что при коннекте происходит хендшейк. После установления хендшейка у клиента и сервера появляются некоторые данные, которые могут использоваться для шифрования и дешифрования в рамках именно этого соединения. Собственно, поскольку хендшейк очень дорогой, каждый коннект обходится системе недешево. Чтобы сократить число коннектов, мы выставили keepalive в 2 минуты (вместо одной секунды, как было ранее).
Дальше мы создали SSL-кэш, чтобы сократить время самого хендшейка путем повторного использования ключевых данных для одного IP-адреса в течение не слишком продолжительного времени.
И напоследок расскажу о небольшом хаке, который тоже существенно увеличил производительность. Мы сделали линк с /dev/random на /dev/urandom. Работает быстрее, потому что /dev/random блокирующий, /dev/urandom не блокирующий. Следовательно, I/O wait сокращается. А поскольку на наших веб-серверах диски достаточно загружены (из-за логирования, приезжающей статики, сохранения атачей и т.д.), то дополнительный I/O wait оказывает существенное влияние на работу сервера в целом.
Заключение
SSL на крупном, высоконагруженном проекте с большим количеством компонентов связи— это действительно rocket science, и это не так просто, как кажется вначале. Это не просто разложить сертификаты. Это много-много таких вот маленьких, казалось бы, задач. Чем-то похоже на сбор паззла; различие в том, что единожды собранный паззл уже не разломается, а в нашем случае даже изменение одного компонента может привести к неожиданным последствиям. Поэтому важно выделять время и ресурсы на дополнительный мониторинг, проводить большое количество тестов, в том числе и автоматических, чтобы система продолжала радовать пользователя зеленым замочком безопасного соединения.
Если у вас остались вопросы по реализации HTTPS в условиях высоких нагрузок, задавайте их в комментариях, с удовольствием отвечу.
Денис Аникин,
технический директор Почты Mail.ru
Преимущества защищенного соединения очевидны всем разработчикам крупных интернет-проектов. Большинство современных web-серверов (nginx, Apache, etc) и браузеров поддерживают HTTPS. В то же время сайтов, на которых безопасный протокол включен всегда и по умолчанию, не так много. Почему это так? С какими трудностями мы столкнулись при поддержке HTTPS? Читайте под катом.
Особенности работы SSL на высоконагруженных системах
По сути, HTTPS представляет собой обычный протокол HTTP поверх сетевого слоя SSL, который позволяет небезопасный сетевой канал сделать безопасным за счет шифрования.
Что нужно сделать, чтобы поддержать SSL?
Казалось бы, достаточно заказать сертификат, разложить его на серверы, включить опцию в nginx, — и добро пожаловать в мир защищенного трафика. Но в реальности все не так просто. Вернее, не так: все было бы просто, если бы нам зачем-то потребовалось обезопасить статическую страничку с текстом «Hello world». А вот для высоконагруженной системы с большим количеством разнообразного контента, какой является Почта Mail.Ru, все самое интересное начинается как раз после того, как сертификаты уже приобретены и разложены.
В чём отличие Почты Mail.Ru от гипотетической страницы с «Hello world»? По сути, с точки зрения поддержки HTTPS основных отличий два. Во-первых, Почта содержит множество страниц – десятки: список писем, сами письма, настройки, адресную книгу, и т.д., и на каждой странице сотни элементов (картинки, JS-ники, CSS-ы и т.д.). Во-вторых, Почта работает под гигантской нагрузкой, обрабатывая сотни тысяч HTTP-запросов в секунду (включая, разумеется, и запросы на статику). Вот из-за этих двух аспектов реализация поддержки HTTPS и была такой непростой.
Как эти аспекты влияют на поддержку? Первый аспект влияет следующим образом: когда страница одна, у сервера есть сертификат, браузер его понимает, всё работает. Проблемы начинаются в том случае, когда страниц много и, более того, когда на этих страницах есть внешние элементы, которые выполняются в контексте этой страницы. Браузер считает, что соединение безопасное, только если все элементы до последнего на странице были отданы по безопасному протоколу: это касается всех картинок, JS, CSS и т.д. Если хоть один элемент был отдан по небезопасному протоколу, браузер будет считать небезопасной всю страницу целиком и информировать об этом пользователя.
Вторая проблема, которую создает большая нагруженность Почты, состоит в том, что шифрование и дешифрование достаточно дорогая операция — как с точки зрения процессора, так и с точки зрения памяти. Процессорное время тратится на шифрование и дешифрование. Память тратится на SSL-кэш, увеличенные буферы nginx-а, большое количество воркеров веб-серверов, которые дольше висят в памяти из-за более долгой обработки запросов. SSL – это такой зверь, который съест много «камня» и памяти и не подавится при этом.
Как с этим бороться?
Баннерная система
Почта — это гетерогенная конструкция. У нас есть собственная баннерная система. Это не только почтовая система, а общая баннерная система портала Mail.Ru, которая ничего ни о каком HTTPS не слышала. Учитывая, что нам пришлось отдавать по HTTPS вообще все данные со всех страниц, баннерную систему пришлось существенно доработать.
Помимо того, что мы научили веб-серверы отдавать по HTTPS все баннеры, скрипты и статику, нам пришлось иметь дело с партнерскими пикселями. Пиксель — это картинка 1х1, которую предоставляет партнер, и которая дергается в тех случаях, когда показывается баннер. Надо отметить, что далеко не все наши партнеры работают по HTTPS, а, как я уже отмечал, небезопасных элементов на странице быть не должно, даже если они размером 1х1 пиксель. Можно было, конечно, пропускать все пиксели через прокси, но мы пошли сначала более простым путем. Договорились с партнерами, объяснили им, насколько это все замечательно и нужно, и теперь они поставляют нам HTTPS-ные пиксели. Дело это было, понятно, непростое, но это сработало. Правда, теперь необходимо и новых партнеров учить тому, что ссылка на картинку должна быть безопасной.
С баннерами есть еще одна проблема: их могут вводить не только технические специалисты, но другие сотрудники с различным уровнем компьютерной грамотности. Соответственно, существует вероятность, что вместе с баннером на странице вполне могут появиться не SSL-ные картинки, JS-ы, небезопасные счётчики.
Для решения этой проблемы мы единоразово прошлись по всем баннерам и заменили все небезопасные на безопасные. Однако это не даёт гарантии, что в будущем ничего не поменяется. Поэтому мы сделали доработку в баннерной системе: создали бит SSL-ready, который назначается каждому проекту. Пока он установлен только у Почты. Наличие этого бита говорит о том, что на этом проекте не будут показывать баннеры, в которых есть небезопасный контент. Всем баннерам, которые показывались в Почте, также проставлялся бит SSL-ready. И наша система для таких баннеров запрещает на уровне ввода в админку менять безопасный контент на небезопасный или добавлять небезопасный контент. Это полностью устранило человеческий фактор при редактировании баннера: если кто-то создаёт новый баннер с небезопасным контентом, он не сможет его показать в Почте, потому что он не сможет пометить его как безопасный. А если человек меняет старый баннер, который уже показывается в Почте, админка не даст сделать его небезопасным.
Повсеместный HTTPS
Далее нам пришлось поддержать HTTPS везде, где это возможно. Контента, который должен отдаваться по HTTPS, очень много, и он разнороден: это все картинки, статика, аватарки, аттачи. Одно дело — поддержать SSL на серверах, которые эти картинки отдают, другое дело — обеспечить корректность и протоколонезависимость ссылок (режим HTTPS является опциональным, поэтому ссылка должна быть правильной вне зависимости от того, является текущий протокол безопасным или нет). Соответственно, нам пришлось менять значительное количество не заточенных под это шаблонов. Мы работали полуавтоматическим способом: сначала через скрипты, потом всё зачищалось руками. Естественно, существенным изменениям подвергся тест-план: теперь наши тестировщики все это проверяют, правят, прогоняют автотесты и так далее.
Помимо всего этого мы разработали прокси для картинок. Дело в том, что большинство картинок, приходящих пользователю в письмах, внешние, а заставить весь интернет работать по HTTPS мы пока не в силах. Поэтому была создана быстрая, лёгкая, однопоточная прокси, которая пропускает через себя все внешние ссылки и выдает пользователю в браузер всегда HTTPS-ный контент.
Как это работает. Прокси получает HTTP-шный URL и получает по нему или контент, или редирект. Во втором случае прокси уходит по редиректам до какого-то значения, прописанного в конфиге. Если она в итоге получает HTTPS-редирект, она выдаёт его клиенту, то есть в браузер. Браузер показывает картинку оттуда напрямую. Если в конце выдаётся контент, то весь контент картинки мы стягиваем к себе, оборачиваем его в SSL и отдаём браузеру. Таким образом, все картинки показываются абсолютно безопасно, даже если они из небезопасного места. Отмечу, что мы сделали антибрутфорс и защиту от злоумышленного использования прокси.
Кроме того, нам пришлось перевести на SSL часть проекта Мой Мир, с которого Почта получает аватарки, а также серверы, на которых развернута система логирования. Также на SSL перешел Веб-Агент и серверы, с которых подгружаются аттачи и их превью.
Еще из того, что интересно отметить, мы сделали умную поддержку SSL на мобильных устройствах. Не все мобильные устройства корректно работают с SSL, и мы разработали систему, определяющую тип устройства из кода на стороне сервера, и действующую в зависимости от результата.
В настоящий момент поддержка включена на iPhone и на iPad, у других мобильных устройств есть некоторые проблемы, над которыми мы работаем. Недалек тот час, когда на всех современных мобильных устройствах Почта Mail.Ru будет работать по умолчанию через HTTPS.
Оптимизация
Ну, и последнее, про что я хотел рассказать — оптимизация. Можно было бы, конечно, накупить железа и покрыть возросшие затраты по процессорному времени и памяти, но это не наш метод.
Оптимизировали, как обычно, начиная с самых узких мест. Какое самое узкое место в SSL? Это коннект, потому что при коннекте происходит хендшейк. После установления хендшейка у клиента и сервера появляются некоторые данные, которые могут использоваться для шифрования и дешифрования в рамках именно этого соединения. Собственно, поскольку хендшейк очень дорогой, каждый коннект обходится системе недешево. Чтобы сократить число коннектов, мы выставили keepalive в 2 минуты (вместо одной секунды, как было ранее).
Дальше мы создали SSL-кэш, чтобы сократить время самого хендшейка путем повторного использования ключевых данных для одного IP-адреса в течение не слишком продолжительного времени.
И напоследок расскажу о небольшом хаке, который тоже существенно увеличил производительность. Мы сделали линк с /dev/random на /dev/urandom. Работает быстрее, потому что /dev/random блокирующий, /dev/urandom не блокирующий. Следовательно, I/O wait сокращается. А поскольку на наших веб-серверах диски достаточно загружены (из-за логирования, приезжающей статики, сохранения атачей и т.д.), то дополнительный I/O wait оказывает существенное влияние на работу сервера в целом.
Заключение
SSL на крупном, высоконагруженном проекте с большим количеством компонентов связи— это действительно rocket science, и это не так просто, как кажется вначале. Это не просто разложить сертификаты. Это много-много таких вот маленьких, казалось бы, задач. Чем-то похоже на сбор паззла; различие в том, что единожды собранный паззл уже не разломается, а в нашем случае даже изменение одного компонента может привести к неожиданным последствиям. Поэтому важно выделять время и ресурсы на дополнительный мониторинг, проводить большое количество тестов, в том числе и автоматических, чтобы система продолжала радовать пользователя зеленым замочком безопасного соединения.
Если у вас остались вопросы по реализации HTTPS в условиях высоких нагрузок, задавайте их в комментариях, с удовольствием отвечу.
Денис Аникин,
технический директор Почты Mail.ru