Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Отравление кеша (cache poisoning) — это вид атаки, при котором атакующий вносит в кеш системы некорректные данные. Когда система пытается их использовать, это приводит к проблемам: от нарушения работы до компрометации данных. В этой статье я покажу отравление кеша на примере реальной уязвимости в движке сайта, а также разберемся c HTTP-заголовками, которые препятствуют этой атаке.
В качестве примера мы будем разбирать CVE-2016-2784 — уязвимость в опенсорсном движке CMS Made Simple. Однако принцип атак на кеш схожий, и изложенное по большей части будет применимо и к другим случаям.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Кеш — это временное место хранения данных, используемых приложениями или операционной системой. Грубо говоря, чтобы не делать какой‑то запрос дважды, мы можем сохранить результат и в следующий раз обратиться сразу к нему.
Есть несколько типов кеша, но нам здесь наиболее важны два из них:
Некоторые механизмы кеширования на стороне сервера:
Для демонстрации локального кеша возьмем движок XenForo, который используется для создания форумов. Если ты перезагрузишь сайт на XenForo несколько раз, а затем отключишь интернет, то получишь кешированную страницу с сообщением «Страница не может быть загружена». При этом если ты откроешь инструменты разработчика, то увидишь service worker в разделе «Передано». Сервисный работник действует как прокси‑сервер и позволяет заменять ответы сервера данными закешированными данными, что и произошло в нашем примере.
Чтобы посмотреть на серверное кеширование, нам понадобится ставить на компьютер целую CMS. Но прежде чем это делать, давай разберемся с HTTP-заголовками, которые связаны с кешированием.
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: s-maxage=600, max-age=300
. Это говорит общедоступным кешам (таким как CDN), что они должны хранить статью десять минут (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: no-cache
— это старый заголовок, существующий со времен HTTP/1.0. Он сообщает системам кеширования, что нужно отправить запрос на исходный сервер для проверки перед выдачей закешированной копии. Сейчас он в основном заменен Cache-Control: no-cache
.
Expires
— это абсолютная временная метка (timestamp), которая задает время, когда закешированный ресурс начнет считаться устаревшим. Это похоже на директиву max-age
в Cache-Control
. Если присутствует и то и другое, то Cache-Control
имеет приоритет.
Age
— показывает, как долго ресурс хранился в прокси‑кеше (в секундах). Vary
— используется, когда в ответ на запросы с разными заголовками запроса предоставляются разные версии ответа. Представь, что у тебя есть веб‑сайт, который обслуживает изображения в двух форматах: WebP и JPEG. WebP поддерживается не всеми браузерами, но Chrome его поддерживает. Когда браузер запрашивает изображение с сайта, в запросе будет заголовок Accept
. Этот заголовок сообщает серверу, какие типы контента может обрабатывать клиент. Для браузеров, поддерживающих WebP, в заголовке Accept
будет среди прочего указано image/webp
, то есть готовность принимать WebP.
Наличие заголовка Vary: Accept
гарантирует, что система кеширования будет хранить несколько версий одного и того же ресурса, если он запрашивается с разными заголовками Accept
. Таким образом, если браузер Chrome запрашивает изображение, система кеширования на сервере сохранит эту версию с ключом для заголовка запроса Accept: image/webp
. Если позже более старый браузер запросит то же изображение, сервер ответит версией изображения в формате JPEG, и кеш сохранит эту версию отдельно.
С точки зрения отравления кеша нас интересует именно серверный кеш. Он может находиться как на самом веб‑сервере, так и на промежуточном кеш‑сервере (например, на обратном прокси или CDN).
При отравлении кеша на стороне сервера атакующий манипулирует процессом хранения и получения кешированного контента. Поскольку последующие запросы к ресурсу будут обслуживаться из кеша с отравленным контентом, такая атака повлияет на других пользователей.
Источник: xakep.ru