Client-Side Fundamental
  • Добро пожаловать
  • Глава 1 - Начало работы с XSS
    • Браузерная модель безопасности
    • Знакомимся с уязвимостью XSS
    • Более глубокое понимание XSS
    • Опасный псевдопротокол javascript
  • Глава 2 - Защита и Обход для XSS
    • Первая линия обороны от XSS - Sanitization
    • Вторая линия обороны от XSS - CSP (Content Security Policy)
    • Третья линия обороны против XSS - сокращение области воздействия
    • Последние методы защиты от XSS - Trusted Types и встроенный Sanitizer API
    • Обход защитных мер - Обычные способы обхода CSP
    • Обход защитных мер - Mutation XSS
    • Самая опасная XSS - Universal XSS
  • Глава 3 - Атаки без JavaScript
    • Кто сказал, что для атаки обязательно выполнять JavaScript?
    • Prototype Pollution - Эксплуатация цепочки прототипов
    • Может ли HTML влиять на JavaScript - Введение в DOM clobbering
    • Template Injection in Frontend - CSTI
    • CSS Injection - Атака с использованием только CSS (Часть 1)
    • CSS Injection - Атака с использованием только CSS (Часть 2)
    • Можно ли атаковать, используя только HTML
  • Глава 4 - Межсайтовые атаки
    • Same-origin Policy и Same-Site
    • Введение в Cross-Origin Resource Sharing (CORS)
    • Проблемы Cross-Origin безопасности
    • Cross-Site Request Forgery (CSRF)
    • Спаситель от CSRF - Same-site cookie
    • От same-site до главного site
    • Интересная и практичная Cookie Bomb
  • Глава 5 - Другие интересные темы
    • То, что вы видите, это не то, что вы получаете - Clickjacking
    • Эксплуатация MIME Sniffing
    • Атаки на цепочку поставок во фронтенде - Attacking Downstream from Upstream
    • Атаки на веб-фронтенд в Web3
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 1)
    • Самая интересная атака на побочные каналы фронтенда - XSLeaks (Часть 2)
Powered by GitBook
On this page
  • Изучение Same-Site Cookie
  • История Same-site cookie
  • Перерыв на размышления
  • CSRF с помощью GET-запросов
  • Скрытые правила использования Same-site Cookies
  • Достаточно ли куки Same-site для предотвращения CSRF?
  • Пример из реальной жизни
  • Заключение
  1. Глава 4 - Межсайтовые атаки

Спаситель от CSRF - Same-site cookie

Когда речь идет о защите от CSRF, независимо от используемого метода, как фронтенд, так и на бэкенд должны реализовать комплексный механизм защиты от него. Ранее, обсуждая XSS, мы упоминали CSP, который может блокировать ресурсы, не соответствующие правилам. Но предоставляет ли браузер аналогичный способ предотвращения CSRF? Есть ли что-то, что мы можем добавить, чтобы предотвратить CSRF?

Да, существует такое понятие, как cookie с атрибутом Same-Site.

Изучение Same-Site Cookie

Как следует из названия, Cookie с атрибутом Same-Site — это Cookie, которые отправляются только при соблюдении условий Same-Site. Он используется путем установки атрибута под названием SameSite, который может иметь три значения:

  1. None

  2. Lax

  3. Strict

None это самое мягкое значение, означающее "я не хочу атрибут SameSite".

С другой стороны, Strict — это самое строгое значение. Когда вы добавляете его, это явно указывает, что "этот cookie может быть отправлен только при условии same-site".

Например, предположим, что cookie установлен с атрибутом SameSite=Strict на https://api.example.com. В этом случае запросы, отправленные с https://randomsite.com на https://api.example.com не будут включать этот cookie, потому что эти два веб-сайта не являются same-site.

Однако, если это https://test.example.com, cookie будет включен, потому что это same-site.

Насколько это строго? Это настолько строго, что даже "клик по ссылке считается". Если я нажимаю на гиперссылку <a href="https://api.example.com"></a> на https://randomsite.com, это эквивалентно отправке Cross-Site запроса с https://randomsite.com to https://api.example.com.

Поэтому в этом случае cookie не будет включен.

Но разве это не неудобно? Давайте возьмем Google в качестве примера. Предположим, Google использует cookie с атрибутом same-site для проверки идентичности пользователя, и в моей статье есть гиперссылка, которая ведет на страницу поиска Google. Когда пользователь нажимает на ссылку, открытая страница Google будет в состоянии выхода из системы, потому что у нее нет токена. Это приводит к плохому пользовательскому опыту.

Существует два решения этой проблемы. Первое похоже на подход Amazon, который включает подготовку двух наборов cookie. Первый набор поддерживает статус входа в систему, в то время как второй набор используется для чувствительных операций (таких как покупка товаров или настройки учетной записи). Первый набор не имеет атрибута SameSite, поэтому он будет поддерживать статус входа в систему независимо от того, откуда поступает запрос. Однако даже если злоумышленник имеет первый набор cookie, он не сможет ничего сделать, потому что не сможет выполнять никакие операции. Второй набор, с другой стороны, полностью избегает CSRF, устанавливая атрибут SameSite.

Однако этот подход может быть немного громоздким. Поэтому вы можете рассмотреть второе решение, которое заключается в переходе на другой режим SameSite: Lax.

Режим Lax ослабляет некоторые ограничения. В основном, если это "навигация верхнего уровня", такая как <a href> или <form method="GET">, cookie все равно будут включены. Однако, если это форма с методом POST, cookie не будет включен.

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

Если Cross-Site запросы не включают cookie, злоумышленники не могут осуществить атаки CSRF.

История Same-site cookie

Однако после того, как пандемия немного утихла в июле, это изменение было постепенно повторно развернуто и полностью внедрено к августу.

Перерыв на размышления

К этому моменту все должны быть в какой-то степени знакомы с принципами и методами защиты от CSRF. Cookie с атрибутом Same-Site, представленный в этой статье, кажется довольно надежным, и браузеры даже автоматически делают его значением по умолчанию, позволяя вам наслаждаться преимуществами без каких-либо настроек.

С настройкой по умолчанию SameSite=Lax, CSRF, похоже, покинул сцену, официально объявленный мертвым, став слезой времени. Не обязательно добавлять CSRF-токен, потому что cookie с атрибутом Same-Site автоматически все обработает.

Однако действительно ли это так?

Действительно ли значение по умолчанию SameSite=Lax настолько мощно? Нужно ли нам все еще добавлять CSRF-токен вместе с ним? Будут ли какие-либо проблемы, если мы его не добавим? Какие ситуации могут вызвать проблемы?

Сначала подумайте над этими вопросами, а затем продолжайте читать.

CSRF с помощью GET-запросов

В предыдущих примерах, когда я вводил CSRF, я всегда использовал POST-запросы. Причина проста: CSRF сосредоточен на выполнении действий, и, как правило, GET-запросы не используются для выполнения действий, поскольку это не соответствует семантике метода GET (или, более профессионально, GET подходит только для идемпотентных операций).

Однако "не подходит" не означает "невозможно сделать".

Как я упоминал в первом примере, говоря о CSRF, некоторые люди могут идти на упрощения и использовать GET для реализации удаления или других функций, например: /delete?id=3.

В этом случае SameSite lax не может обеспечить защиту, потому что lax позволяет следующее поведение:

location = 'https://api.example.com/delete?id=3'

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

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

Однако таких людей, которые пишут это таким образом, должно быть немного, верно? Так что проблема не должна быть значительной?

Для такого рода написания это действительно редко, но есть еще один распространенный механизм, который мы можем использовать: переопределение метода.

Атрибут method в HTML-формах представляет HTTP-метод, используемый при отправке запроса. Он поддерживает только два значения: GET и POST.

Что если мы хотим использовать PUT, PATCH или DELETE? Это невозможно. Либо нам нужно использовать fetch() для отправки запроса, либо реализовать обходной путь на сервере, что поддерживают многие фреймворки.

Для некоторых веб-фреймворков, если запрос имеет заголовок X-HTTP-Method-Override или строка запроса содержит параметр _method, значение внутри будет использоваться как метод запроса вместо оригинального HTTP-метода.

Это изначально использовалось в сценарии, который я только что упомянул, когда вы хотите обновить данные, но можете использовать только POST. Вы можете добавить параметр _method, чтобы сообщить серверу, что это на самом деле запрос PATCH:

<form action="/api/update/1" method="POST">  
 <input type=hidden name=_method value=PATCH>  
 <input name=title value=new_title>
</form>

Но это также может быть использовано в нашей атаке CSRF. Например, GET /api/deleteMyAccount?_method=POST будет рассматриваться сервером как POST-запрос, а не GET.

Скрытые правила использования Same-site Cookies

Итак, если нет поддержки переопределения метода и нет неуместных операций с использованием GET, значит ли это, что все в порядке? Конечно, не так просто.

Куки Same-Site по умолчанию на самом деле имеют скрытое правило, или скорее, менее известное правило, которое было упомянуто Firefox:

For any flows involving POST requests, you should test with and without a long delay. This is because both Firefox and Chrome implement a two-minute threshold that permits newly created cookies without the SameSite attribute to be sent on top-level, cross-site POST requests (a common login flow).

Это означает, что для куки без атрибута SameSite они могут обойти некоторые из ограничений lax в течение первых двух минут после записи, позволяя "верхнеуровневые кросс-сайтовые POST-запросы", другими словами, <form method=POST>.

Поэтому давайте предположим, что пользователь только что вошел на сайт, и куки, используемые для аутентификации, только что были записаны. В это время пользователь открывает веб-страницу, созданную злоумышленником, и содержимое веб-страницы является эксплойтом CSRF:

<form id=f action="https://api.example.com/transfer" method="POST">    
<input type=hidden name=target value=attacker_account>    
<input type=hidden name=amount value=1000>
</form>

<script>f.submit()</script>

Из-за упомянутого ранее исключения атака CSRF будет успешной.

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

Если веб-сайт явно указывает SameSite=Lax, то этой проблемы не будет. Так значит ли это, что он действительно безопасен?

Я думаю, вы знаете, что я собираюсь сказать.

Достаточно ли куки Same-site для предотвращения CSRF?

Хотя CSRF означает Cross-Site, чаще всего это больше похоже на Cross-Origin. Другими словами, если злоумышленник может запустить атаку с assets.example.com на example.com, мы обычно будем считать это CSRF, даже если эти два веб-сайта не являются Cross-Site.

Куки Same-Site только гарантируют, что куки не будут отправлены в Cross-Site сценариях. Но если два веб-сайта являются Same-Site, это не имеет значения.

Продолжая с предыдущего примера, предположим, что основной веб-сайт Facebook — это www.facebook.com, и у него есть тестовая среда под названием sandbox.facebook.com, где была обнаружена уязвимость XSS.

Если веб-сайт полагается только на куки Same-Site для предотвращения CSRF, то в этом сценарии это совершенно бесполезно, потому что www.facebook.com и sandbox.facebook.com явно являются Same-Site. Поэтому мы можем легко запустить атаку CSRF на основной веб-сайт, используя уязвимость XSS, найденную в песочнице.

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

Developers are strongly encouraged to deploy the usual server-side defenses (CSRF tokens, ensuring that "safe" HTTP methods are idempotent, etc) to mitigate the risk more fully.

Настоятельно рекомендуется, чтобы разработчики реализовывали обычные меры защиты, такие как токены CSRF, в дополнение к куки Same-Site.

Таким образом, даже при наличии куки Same-Site это не означает, что предыдущие меры защиты можно удалить. Нам все еще нужны токены CSRF, в сочетании с куки Same-Site, чтобы построить более надежную защитную стену и предотвратить различные сценарии атак.

Пример из реальной жизни

Однако есть причина, по которой Grafana так считает. API Grafana принимает только запросы с типом контента application/json, и этот тип контента нельзя отправить через форму. Можно использовать только fetch, и этот тип контента попадает под категорию непростых запросов, поэтому требуется предварительный запрос (preflight).

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

Но, внимательно прочитав спецификацию CORS и обнаружив небольшую ошибку на сервере, это ограничение было успешно обойдено.

MIME-тип состоит из трех частей: типа, подтипа и параметров. Мы часто видим application/json, где тип — application, подтип — json, и нет параметров.

Однако text/plain; charset=utf-8 имеет тип text, подтип plain и параметр charset=utf-8.

Спецификация CORS требует, чтобы тип и подтип были одним из следующих:

  1. application/x-www-form-urlencoded

  2. multipart/form-data

  3. text/plain

Но она не ограничивает содержание параметров.

Таким образом, этот тип контента может быть простым запросом: text/plain; application/json. application/json является параметром, а text/plain — это тип + подтип, что полностью соответствует спецификации.

Логика обработки на стороне API выглядит следующим образом:

func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
  contentType := ctx.Req.Header.Get("Content-Type")
  if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || len(contentType) > 0 {
    switch {
    case strings.Contains(contentType, "form-urlencoded"):
      ctx.Invoke(Form(obj, ifacePtr...))
    case strings.Contains(contentType, "multipart/form-data"):
      ctx.Invoke(MultipartForm(obj, ifacePtr...))
    case strings.Contains(contentType, "json"):
      ctx.Invoke(Json(obj, ifacePtr...))
    // ...
    }
  } else {
    ctx.Invoke(Form(obj, ifacePtr...))
  }
}

Здесь используется strings.contains для всего типа контента, поэтому, хотя передаваемый нами тип контента по сути text/plain, сервер обрабатывает его как допустимый JSON из-за параметров.

После обхода этого ограничения мы можем использовать fetch для инициации атаки CSRF с сайта того же происхождения.

Предположим, что Grafana размещена по адресу https://grafana.example.com, нам нужно найти хотя бы одну уязвимость XSS или получить контроль над всем доменом *.example.com, чтобы запустить атаку. Хотя это может быть сложно, это не невозможно.

Как я уже упоминал ранее, это атака, инициированная с того же сайта, поэтому куки Same-Site не могут предотвратить ее. Строго говоря, если рассматривать буквальное значение, это нельзя назвать CSRF, потому что это не кросс-сайт. Однако давать этому новое название кажется странным.

Заключение

В этой статье мы представили новые меры, которые недавно внедрили основные браузеры, устанавливая куки по умолчанию на SameSite=Lax. Хотя это действительно увеличивает некоторую безопасность, не думайте, что использование этого одного лишь метода может полностью предотвратить CSRF.

Так же как защита от XSS, защита от CSRF также требует многослойной защиты, чтобы гарантировать, что если одна линия обороны будет нарушена, будут другие защиты, которые смогут помочь. Например, если используются только куки Same-Site, это означает капитуляцию, когда другой сайт того же происхождения будет скомпрометирован. Вместо этого лучше реализовать дополнительные меры защиты, такие как токены CSRF, которые могут по крайней мере смягчить последствия, когда сайт того же происхождения будет скомпрометирован.

Говоря об этом, легко ли получить контроль над другими сайтами того же происхождения? И что можно сделать, получив контроль? Каждый может подумать над этими вопросами, и мы обсудим их в следующей статье.

Дополнительные ссылки:

PreviousCross-Site Request Forgery (CSRF)NextОт same-site до главного site

Last updated 8 months ago

Первый cookie с атрибутом Same-site был опубликован в октябре 2014 года. В то время он назывался "First-Party Cookie" вместо текущего "Same-site cookie". Только в январе 2016 года название было изменено на Same-site cookie.

Google официально представил эту функцию с Chrome 51 в мае 2016 года: . Firefox также добавил поддержку в Firefox 60, выпущенном в мае 2018 года. Safari, с самым медленным прогрессом, полностью поддержал эту функцию только с выходом Safari 15 в сентябре 2021 года.

Из-за повышенной безопасности и защиты конфиденциальности, предоставляемой атрибутом SameSite, в октябре 2019 года Chrome выпустил статью под названием, в которой было объявлено, что начиная с февраля 2020 года, cookie без атрибута SameSite будут по умолчанию иметь значение Lax.

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

Помимо Chrome, Firefox также объявил в августе 2020 года, что они последуют этому примеру. Cookie без атрибута SameSite будут по умолчанию иметь значение Lax. Статья того времени называлась:.

Что касается Safari, они объявили в марте 2020 года, что полностью , но фактическое поведение, похоже, остается черным ящиком.

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

Поэтому полагаться исключительно на куки Same-Site для защиты от CSRF — это небезопасный выбор. подтверждает:

В 2022 году jub0bs и abrahack обнаружили уязвимость CSRF в открытой системе мониторинга Grafana с идентификатором .

Основная причина заключается в том, что Grafana использует только SameSite=Lax в качестве защиты от CSRF, поэтому любой Same-Site запрос может выполнить атаку CSRF. Интересно, что в 2019 году Grafana изначально планировала добавить токен CSRF, но после некоторых изменений они решили, что "наличие куки same-site кажется достаточным" и прекратили разработку. Более подробную информацию можно найти в этом PR: .

Вы можете найти оригинальное описание здесь:

черновой вариант спецификации
SameSite cookie
Developers: Get Ready for New SameSite=None; Secure Cookie Settings
Temporarily rolling back SameSite Cookie Changes
Changes to SameSite Cookie Behavior – A Call to Action for Web Developers
заблокируют сторонние cookie
Bypassing Samesite Cookie Restrictions with Method Override
RFC for Cookies
CVE-2022-21703
WIP: security: csrf protection #20070
CVE-2022-21703: cross-origin request forgery against Grafana
Preventing CSRF with the same-site cookie attribute
再见,CSRF:讲解set-cookie中的SameSite属性
SameSite Cookie,防止 CSRF 攻击
SameSite——防御 CSRF & XSSI 新机制
Cross-Site Request Forgery is dead!