Содержание статьи
В качестве примера мы будем разбирать CVE-2016-2784 — уязвимость в опенсорсном движке CMS Made Simple. Однако принцип атак на кеш схожий, и изложенное по большей части будет применимо и к другим случаям.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Что такое кеш?
Кеш — это временное место хранения данных, используемых приложениями или операционной системой. Грубо говоря, чтобы не делать какой‑то запрос дважды, мы можем сохранить результат и в следующий раз обратиться сразу к нему.
Есть несколько типов кеша, но нам здесь наиболее важны два из них:
- кеш веб‑браузера. Современные браузеры вроде Chrome и Firefox хранят копии веб‑страниц, изображений и других ресурсов локально на твоем компьютере. Это позволяет быстрее загружать контент при повторном просмотре веб‑страницы, так как данные будут получены с диска, а не из интернета;
- кеш на стороне сервера. Веб‑серверы вместо того, чтобы создавать содержимое для каждого пользовательского запроса с нуля, хранят часто запрашиваемое содержимое непосредственно на сервере и выдают его пользователям. Это избавляет от необходимости повторно обрабатывать одни и те же данные и значительно снижает как нагрузку на сервер, так и время ответа.
Некоторые механизмы кеширования на стороне сервера:
- объектный кеш. Основан на кешировании запросов к базе данных. Вместо того чтобы кешировать все страницы, он хранит результаты запросов к базе в памяти и использует их при сборке страниц;
- кеш Opcode. Opcode (операционный код) — системная операция, которую может выполнять центральный процессор компьютера. Кеширование опкодов предназначено для серверных скриптовых языков, таких как PHP. Когда ты пишешь код на PHP, он изначально находится в форме, доступной для чтения человеком. При выполнении скрипты на PHP компилируются в операционный код. Кеширование опкодов сохраняет скомпилированный код в памяти, чтобы его можно было повторно использовать для последующих запросов и не требовалось каждый раз повторно компилировать скрипт;
- кеш CDN. Сети доставки контента (Content Delivery Network, CDN) — это мощности, расположенные в разных странах. Они кешируют статические активы, такие как изображения, таблицы стилей и файлы JavaScript, на серверах, которые ближе всего к пользователям.
Для демонстрации локального кеша возьмем движок XenForo, который используется для создания форумов. Если ты перезагрузишь сайт на XenForo несколько раз, а затем отключишь интернет, то получишь кешированную страницу с сообщением «Страница не может быть загружена». При этом если ты откроешь инструменты разработчика, то увидишь service worker в разделе «Передано». Сервисный работник действует как прокси‑сервер и позволяет заменять ответы сервера данными закешированными данными, что и произошло в нашем примере.
Чтобы посмотреть на серверное кеширование, нам понадобится ставить на компьютер целую CMS. Но прежде чем это делать, давай разберемся с HTTP-заголовками, которые связаны с кешированием.
Заголовки
Cache-Control
Cache-Control
— самый важный HTTP-заголовок кеширования. Он включает в себя несколько директив для управления поведением кеширования. Первые две из них — самые важные:
-
public
/private
— указывает, может ли ответ быть закеширован любым кешем (public) или только кешем браузера клиента (private). Когда директива указана какpublic
, ответ может быть сохранен в любом кеше на пути запроса‑ответа. Это включает кеш клиентского браузера, промежуточные прокси и даже CDN. Например: открыто закешированное изображение на сайте может быть сохранено в кеше браузера пользователя, кеше прокси‑сервера интернет‑провайдера и на серверах CDN по всему миру. Когда указаноprivate
, ответ может быть закеширован только браузером клиента; -
no-cache
— заставляет кеши отправлять запрос на исходный сервер для проверки перед выдачей закешированной копии. В режиме no-cache при каждом запросе ресурса системы кеширования должны отправлять запрос на исходный сервер, чтобы проверить, актуальна ли копия. Проверка обычно происходит с использованием заголовковETag
илиLast-Modified
. ETag — это уникальный идентификатор, присвоенный сервером конкретной версии ресурса. Клиент при запросе ресурса может отправить значение закешированной копии в заголовкеIf-None-Match
. Сервер сравнивает этот ETag с текущим ETag ресурса. Если они совпадают, это значит, что ресурс не изменился, и сервер отвечает статусом 304 Not Modified.
Пример: мы отправили запрос на получение гифки. Получаем ответ с заголовками ETag
и Last-Modified
.
Полученный нами заголовок ETag
будет отправлен как заголовок If-None-Match
, а заголовок Last-Modified
— как If-Modified-Since
. Здесь сервер сравнивает отправленный нами ETag (значение If-None-Match
) с текущим ETag ресурса, и, как видишь, они совпадают, поэтому статус ответа — 304.
Может возникнуть вопрос: что произойдет, если изображение изменится? Если запрашиваемый нами ресурс был изменен и мы (клиентская сторона) об этом не знаем, запрос будет отправлен со старыми заголовками If-None-Match
и If-Modified-Since
. Если ETag не совпадает, сервер ответит новыми заголовками ETag
и Last-Modified
, которые станут использоваться в будущих запросах.
Пройдемся по остальным возможным директивам заголовка Cache-Control
:
-
no-store
— запрещает кешам сохранять ответ при любых обстоятельствах; -
max-age=[
— определяет максимальное время, в течение которого ресурс считается актуальным;секунды] -
s-maxage=[
— похоже на максимальный возраст (секунды] max-age
), но применимо только к общедоступным кешам (таким как кеши CDN). Предположим, что новостной сайт использует CDN для распространения статей. На таком сайте может быть такой заголовок:Cache-Control:
. Это говорит общедоступным кешам (таким как CDN), что они должны хранить статью десять минут (s-maxage=600, max-age=300 s-maxage=600
), но браузер должен хранить ее только пять минут (максимальный возраст равен 300); -
must-revalidate
— указывает, что кеш должен быть проверен на актуальность. Когда срок годности закешированного ресурса (определяется заголовкамиmax-age
,s-maxage
илиexpires
) истекает, он становится устаревшим. Директиваmust-revalidate
обязывает системы кеширования проверять у исходного сервера (используя ETag илиLast-Modified
), актуален ли еще устаревший ресурс, перед его повторным использованием, чтобы пользователи не видели устаревший контент; -
proxy-revalidate
— похоже наmust-revalidate
, но применимо только к общедоступным кешам; -
no-transform
— запрещает любым промежуточным устройствам (таким как прокси) изменять данные ответа. Например, оператор мобильной сети может снижать качество изображений для экономии передаваемых данных. При использованииno-transform
такие изменения запрещены.
Pragma
Pragma:
— это старый заголовок, существующий со времен HTTP/1.0. Он сообщает системам кеширования, что нужно отправить запрос на исходный сервер для проверки перед выдачей закешированной копии. Сейчас он в основном заменен Cache-Control:
.
Expires
Expires
— это абсолютная временная метка (timestamp), которая задает время, когда закешированный ресурс начнет считаться устаревшим. Это похоже на директиву max-age
в Cache-Control
. Если присутствует и то и другое, то Cache-Control
имеет приоритет.
Другие заголовки
-
Age
— показывает, как долго ресурс хранился в прокси‑кеше (в секундах). -
Vary
— используется, когда в ответ на запросы с разными заголовками запроса предоставляются разные версии ответа.
Представь, что у тебя есть веб‑сайт, который обслуживает изображения в двух форматах: WebP и JPEG. WebP поддерживается не всеми браузерами, но Chrome его поддерживает. Когда браузер запрашивает изображение с сайта, в запросе будет заголовок Accept
. Этот заголовок сообщает серверу, какие типы контента может обрабатывать клиент. Для браузеров, поддерживающих WebP, в заголовке Accept
будет среди прочего указано image/
, то есть готовность принимать WebP.
Наличие заголовка Vary:
гарантирует, что система кеширования будет хранить несколько версий одного и того же ресурса, если он запрашивается с разными заголовками Accept
. Таким образом, если браузер Chrome запрашивает изображение, система кеширования на сервере сохранит эту версию с ключом для заголовка запроса Accept:
. Если позже более старый браузер запросит то же изображение, сервер ответит версией изображения в формате JPEG, и кеш сохранит эту версию отдельно.
Отравление кеша
С точки зрения отравления кеша нас интересует именно серверный кеш. Он может находиться как на самом веб‑сервере, так и на промежуточном кеш‑сервере (например, на обратном прокси или CDN).
При отравлении кеша на стороне сервера атакующий манипулирует процессом хранения и получения кешированного контента. Поскольку последующие запросы к ресурсу будут обслуживаться из кеша с отравленным контентом, такая атака повлияет на других пользователей.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»