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
  • Что такое Origin и Site? Как их различать?
  • Подробное изучение Same-origin
  • Подробное изучение Same-Site
  • Same-Origin и Same-Site
  • Магический document.domain
  • Сценарий 1: Одностороннее изменение
  • Сценарий 2: Исчезающий порт
  • Сценарий 3: Я не тот, кем был раньше
  • The fading and exit of document.domain
  • Заключение
  1. Глава 4 - Межсайтовые атаки

Same-origin Policy и Same-Site

В предыдущих статьях мы несколько раз упоминали термин "Same-Origin". Это крайне важный термин как в мире фронтенда, так и в кибербезопасности. Same-Origin Policy в браузерах имеет важное значение как для разработки, так и для защиты от атак.

Кроме того, есть несколько терминов, которые часто путают, таких как Host и Site. Например, XS в XSS означает "Cross-Site", и CS в CSRF также означает "Cross-Site". Так в чем же разница между Origin и Site? Чем они отличаются от Host?

Что такое Origin и Site? Как их различать?

Начнем с простого и несколько неточного объяснения, а затем мы шаг за шагом уточним детали.

Origin состоит из схемы, порта и хоста. В совокупности они формируют Origin.

Например, если у нас есть URL, такой как https://example.com/example_path, его компоненты:

  • Схема: https

  • Порт: 443 (порт по умолчанию для https)

  • Хост: example.com

Таким образом, его Origin — это https://example.com. Как видно, часть пути /example_path не влияет на Origin, а порт в данном случае подразумевается по умолчанию как 443.

Same-Origin означает, что происхождение двух URL должно быть одинаковыми. Например:

  1. https://example.com/example_path и https://example.com/path/example имеют одинаковый Origin, потому что схема, порт и хост одинаковы, а путь не влияет на результат.

  2. https://example.com и http://example.com не являются Same-Origin, потому что схемы различаются.

  3. http://example.com и http://example.com:8080 не являются Same-Origin, потому что порты различаются.

  4. https://example и https://sub-domain.example.com также не являются Same-Origin, потому что хосты различаются.

Из приведенных выше примеров видно, что условия для Same-Origin довольно строгие. В основном, за исключением пути, все остальные части должны быть одинаковыми, чтобы считаться Same-Origin.

Теперь давайте посмотрим, что такое Site. Site учитывает меньше элементов по сравнению с Origin.

Он рассматривает только схему и хост, игнорируя порт. Определение того, что два URL-адреса являются Same-Site, более простое, поскольку хост не обязательно должен быть абсолютно одинаковым.

Если это поддомен, он все равно считается Same-Site.

Например:

  1. https://example.com/example_path и https://example.com/path/example являются Same-Site, потому что схема и хост одинаковы.

  2. https://example.com и http://example.com не являются Same-Site, потому что схемы различаются.

  3. http://example.com и http://example.com:8080 являются Same-Site, потому что порт не влияет на результат.

  4. https://example.com и https://sub-domain.example.com являются Same-Site, потому что оба домена - example.com и sub-domain.example.com находятся под одним родительским доменом, example.com.

  5. https://sub-domain-1.example.com и https://sub-domain-2.example.com также являются Same-Site, потому что оба домена - sub-domain-1.example.com и sub-domain-2.example.comнаходятся под одним родительским доменом -example.com.

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

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

Подробное изучение Same-origin

Origins are the fundamental currency of the web's security model. Two actors in the web platform that share an origin are assumed to trust each other and to have the same authority. Actors with differing origins are considered potentially hostile versus each other, and are isolated from each other to varying degrees.

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

Далее спецификация делит Origins на два типа: Непрозрачное происхождение (opaque origin) и Кортеж происхождения (tuple origin)

Непрозрачное происхождение можно рассматривать как происхождение, которое появляется только в особых случаях. Например, когда я открываю веб-страницу на своей локальной машине, URL будет иметь вид «file:///...». В этом случае при отправке запроса внутри веб-страницы в качестве Origin будет использоваться непрозрачный Origin, то есть «null».

Кортежное происхождение встречается чаще всего, и именно этот тип происхождения нас больше всего волнует. В документации говорится, что кортеж происхождения включает в себя:

  1. схему (ASCII-строка).

  2. хост (хост).

  3. порт (null или 16-битное целое число без знака).

  4. домен (null или домен). Null, если не указано иное.

Вы можете задаться вопросом, зачем нужны хост и домен. Мы обсудим это позже.

Кроме того, в спецификации также описан алгоритм определения того, являются ли два Origins A и B Same-Origin:

  1. Если A и B — одно и то же непрозрачное происхождение, то возвращаем true.

  2. Если A и B являются кортежами и их схемы, хосты и порты идентичны, то возвращается true.

  3. Вернуть false.

Либо оба Origins являются одним и тем же непрозрачным Origin, либо их схемы, хосты и порты идентичны, чтобы считаться Same-Origin. Помимо одинакового происхождения, в спецификации вы также встретите еще один термин, называемый "Same Origin-Domain", который будет объяснен позже.

Как я уже упоминал ранее, Same-Origin — это строгое ограничение. Например, для URL "https://example.com/api" поскольку при определении происхождения не учитывается путь, его Origin будет "https://example.com". Это означает, что любой сайт с таким же происхождением должен иметь URL, начинающийся с "https://example.com/*", чтобы считаться таким же по происхождению.

Хотя "https://example.com" и "https://sub-domain.example.com" связаны с одним доменом, они не являются Same-Origin, потому что хосты различаются.

Запомните это, так как это важно.

Подробное изучение Origin и Same-Origin в спецификации, по сравнению с "неточным утверждением", упомянутым в начале, добавляет Opaque Origin, Same Origin-Domain и Tuple Origin.

Наконец, позвольте мне упомянуть еще одну вещь. Когда я говорю, что Origin "https://example.com/api" равен "https://example.com", более точное утверждение будет: "The serialized form of the origin of 'https://example.com/api' is 'https://example.com'".

Это потому, что ранее упоминалось, что происхождение на самом деле является кортежем, представленным как (https, example.com, null, null), и оно становится https://example.com при сериализации в строку. Мне кажется, что сериализованная форма кортежа легче читается по сравнению с его представлением в виде кортежа. Поэтому, когда оба варианта могут передать схожую информацию, я предпочитаю последний подход.

Подробное изучение Same-Site

В той же спецификации есть определение Site, которое гласит:

A site is an opaque origin or a scheme-and-host.

Итак, сайт может быть непрозрачным источником или схемой и хостом.

В спецификации, помимо термина "Same-Site", можно найти еще один термин, называемый "Schemelessly Same-Site". Разница между ними также очевидна: Same-Site учитывает схему, а Schemelessly Same-Site не учитывает схему.

Поэтому при определении того, являются ли два Origins, A и B - Same-Site, алгоритм выглядит следующим образом:

Two origins, A and B, are said to be same site if both of the following statements are true:

  • A and B are schemelessly same site

  • A and B are either both opaque origins, or both tuple origins with the same scheme

Если A и B являются Same-Site, то либо они оба Opaque Origins, либо у них одинаковая схема и они являются Schemelessly Same-Site.

Таким образом, понятие "Same-Site" зависит от схемы. URL с разными схемами, например http и https, никогда не считаются Same-Site, но они могут быть Schemelessly Same-Site.

Здесь есть немного истории. Когда Same-Site был впервые введен, он не учитывал схему. Позже схема была принята во внимание.

В RFC 2016: Same-Site Cookies можно увидеть, что определение Same-Site не включало схему. Таким образом, в то время https://example.com и http://example.com считались Same-Site.

Затем, через два месяца, соответствующая спецификация была перенесена из URL в HTML. Вы можете ознакомиться с этими двумя PR:

Спецификации — это одно, но иногда браузеры не сразу успевают за изменениями. Так какова текущая реализация в браузерах?

После изучения истории давайте посмотрим, как определяется Schemelessly Same-Site:

Ключевой момент выше — это новый термин: "Registrable Domain", который используется для сравнения двух хостов, чтобы определить, являются ли они Same-Site.

A host's registrable domain is a domain formed by the most specific public suffix, along with the domain label immediately preceding it, if any.

Здесь упоминается новый термин: "Public Suffix".

Начнем с примера, чтобы лучше это понять.

Registrable domain sub-domain.example.com будет example.com, а Registrable domain example.com также будет example.com.

Однако Registrable domain bob.github.io не github.io, а скорее bob.github.io.

Почему так? Позвольте мне кратко объяснить.

Если бы у нас не было понятий "Registrable Domain" и "Public Suffix", то определение Same-Site было бы таким, как упоминалось ранее: example.com и sub-domain.example.com считались Same-Site, и в этом не было бы проблем.

Но если бы это было так, то bob.github.io и alice.github.io также считались Same-Site.

Подождите, это ведь проблема?

Да, это проблема. github.io — это сервис, предоставляемый GitHub, и у каждого пользователя GitHub есть свой собственный поддомен. Однако GitHub не хочет, чтобы bob.github.io мешал alice.github.io, потому что это на самом деле два совершенно независимых веб-сайта, в отличие от example.com и sub-domain.example.com, которые принадлежат мне.

Поэтому возникла концепция Public Suffix. Это список, который ведется вручную и содержит "list of domains that should not be considered as the same websit". Позвольте привести несколько примеров:

  1. github.io

  2. com.tw

  3. s3.amazonaws.com

  4. azurestaticapps.net

  5. herokuapp.com

Как упоминалось выше, поскольку github.io включен в список Public Suffix, Registrable Domain bob.github.io — это bob.github.io, а Registrable Domain alice.github.io — это alice.github.io.

Таким образом, определение Same-Site, которое мы изначально упоминали, неверно. Два хоста могут казаться принадлежащими одному и тому же родительскому домену, но это не обязательно означает, что они являются Same-Site. Это также зависит от того, включены ли они в список Public Suffix.

bob.github.io и alice.github.io не являются Same-Site, потому что их Registrable Domains различны.

sub-domain-1.example.com, example.com и sub-domain-2.example.com — все они Same-Site, потому что их Registrable Domain — example.com.

Спецификация включает более ясную таблицу для справки. Пожалуйста, обратите на нее внимание:

Наконец, давайте подведем итоги по поводу Same-Site:

  1. Существуют Same-Site и Schemelessly Same-Site, при этом первый используется чаще.

  2. Чтобы определить, являются ли два хоста Same-Site, необходимо рассмотреть Registrable Domain.

  3. Для определения Registrable Domain нужно обратиться к списку Public Suffix.

  4. Даже если два хоста кажутся принадлежащими одному и тому же родительскому домену, они могут не быть Same-Site из-за наличия Public Suffix.

  5. Same-Site не учитывает порт, поэтому http://sub-domain.example.com:8888 и http://example.com являются Same-Sate.

Same-Origin и Same-Site

Same-Origin определяется по:

  1. Scheme

  2. Port

  3. Host

А Same-Site определяется по:

  1. Scheme

  2. Host (Registrable Domain)

Если два веб-сайта имеют Same-Origin, то они обязательно должны быть Same-Site, поскольку критерии для определения Same-Origin строже.

Основные различия между ними:

  1. Same-Origin учитывает порт, в то время как Same-Site — нет.

  2. Same-Origin учитывает хост, в то время как Same-Site учитывает Registrable Domain.

Магический document.domain

При изучении спецификации источника упоминается магический атрибут "domain", назначение которого не совсем ясно. В спецификации источника также упоминается нечто под названием "одинаковый источник-домен" (same origin-domain), и есть зеленая заметка, которая касается этого:

(document.domain) can be set to a value that removes subdomains, to change the origin's domain to allow pages on other subdomains of the same domain (if they do the same thing) to access each other. This enables pages on different hosts of a domain to synchronously access each other's DOMs.

Чтобы помочь всем понять, давайте проведем демонстрацию. Я изменил файл /etc/hosts на своем локальном компьютере, добавив следующее содержимое:

127.0.0.1   alice.example.com
127.0.0.1   bob.example.com

Теперь оба этих URL будут подключаться к локальному компьютеру. Затем я запустил простой HTTP-сервер и создал базовую HTML-страницу, работающую на localhost:5555.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
    <h1></h1>
    <h2></h2>
    <button onclick="load('alice')">load alice iframe</button>
    <button onclick="load('bob')">load bob iframe</button>
    <button onclick="access()">access iframe content</button>
    <button onclick="update()">update domain</button>
    <br>
    <br>
</body>
<script>
    const name = document.domain.replace('.example.com', '');
    document.querySelector('h1').innerText = name;
    document.querySelector('h2').innerText = Math.random();
    
    function load(name) {
        const iframe = document.createElement('iframe');
        iframe.src = 'http://' + name + '.example.com:5555';
        document.body.appendChild(iframe);
    }
    
    function access() {
        const win = document.querySelector('iframe').contentWindow;
        alert('secret:' + win.document.querySelector('h2').innerText);
    }
    
    function update() {
        document.domain = 'example.com';
    }
</script>
</html>

Страница имеет три функции:

  1. Загрузка iframe

  2. Чтение данных из DOM iframe

  3. Изменение document.domain

Давайте начнем с открытия http://alice.example.com:5555 и затем загрузим iframe с http://bob.example.com:5555. Затем нажмите "access iframe content" на странице Алисы.

Вы увидите сообщение об ошибке в консоли, которое гласит:

Вы увидите сообщение об ошибке в консоли, которое гласит:

Uncaught DOMException: Blocked a frame with origin "http://alice.example.com:5555" from accessing a cross-origin frame.

Это происходит потому, что хотя alice и bob имеют Same-Site, они не являются Same-Origin. Чтобы iframe мог получить доступ к содержимому DOM, он должен находиться на одном и том же источнике.

Далее оба, alice и bob, нажимают "update domain" на своих страницах, а затем снова нажимают "access iframe content":

На этот раз вы увидите, что мы успешно получили данные со страницы Боба и изменили http://alice.example.com:5555 и http://bob.example.com:5555 с Cross-Origin на Same-Origin.

Эту технику не могут использовать любые две веб-страницы. В основном, только веб-сайты c Same-Site могут ее использовать, и также существует множество проверок во время настройки:

Принимая github.io в качестве примера, если alice.github.io выполнит document.domain = 'github.io', в консоли будет выдана ошибка:

Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'github.io' is a top-level domain.

Почему изменение document.domain делает две страницы Same-Origin? Строго говоря, это не одинаковый источник, а одинаковый источник-домен. В спецификации, связанной с document, упоминается, что некоторые проверки основаны на одинаковом источник-домене, а не на одинаковом источнике.

Итак, как мы определяем, являются ли два источника одинаковым источник-доменом? Давайте посмотрим, что говорит спецификация:

  1. If A and B are the same opaque origin, then return true.

  1. If A and B are both tuple origins, run these substeps:

  2. If A and B's schemes are identical, and their domains are identical and non-null, then return true.

  3. Otherwise, if A and B are same origin and their domains are identical and null, then return true.

  4. Return false.

Если A и B имеют одинаковую схему, и их свойства домена идентичны и не равны null, то вернуть true. В противном случае, если A и B — это одинаковый источник и их домены идентичны и равны null, то вернуть true. В противном случае вернуть false.

Вот несколько интересных моментов:

  • Обе веб-страницы должны либо не иметь установленного домена, либо иметь установленный одинаковый домен, чтобы потенциально вернуть true (это важно).

  • Если домен установлен, проверка одинакового источник-домена не учитывает порт.

document.domain используется для изменения свойства домена в кортеже источника.

В приведенном выше примере обе наши веб-страницы, http://alice.example.com:5555 и http://bob.example.com:5555, изменяют свой домен на example.com, поэтому они становятся одинаковым источник-доменом.

Теперь давайте рассмотрим три интересных сценария.

Сценарий 1: Одностороннее изменение

Если https://alice.example.com выполнит document.domain = 'example.com', а затем встроит https://example.com в iframe, они все равно не будут Same Origin-Domain, потому что страница Алисы имеет установленное свойство домена, а страница example.com — нет. Страница example.com также должна выполнить document.domain = 'example.com', чтобы они стали Same Origin-Domain.

Сценарий 2: Исчезающий порт

http://alice.example.com:1234 и http://alice.example.com:4567 считаются Cross-Origin, потому что у них разные порты. Однако, если обе страницы выполнят document.domain = 'alice.example.com', они станут Same Origin-Domain и смогут получить доступ к DOM друг друга, потому что порт не учитывается.

Сценарий 3: Я не тот, кем был раньше

Предположим, http://alice.example.com встраивает себя в iframe, iframe и оригинальная страница явно являются Same-Origin и могут получить доступ к DOM друг друга. Однако, если я выполню document.domain = 'alice.example.com' на странице, страница установит атрибут домена, но страница внутри iframe не имеет установленного атрибута домена. Поэтому они становятся разными Origin-Domains.

The fading and exit of document.domain

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

Оригинальное поведение можно заменить на postMessage или API Channel Messaging, но это требует написания большего объема кода. В конце концов, это не так удобно, как прямое манипулирование DOM. Если веб-страница хочет продолжать использовать функциональность изменения document.domain, ей необходимо включить в заголовок ответа Origin-Agent-Cluster: ?0.

Заключение

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

После введения важных концепций источника и сайта мы постепенно столкнемся с двумя связанными терминами: CSRF (межсайтовая подделка запросов) и CORS (междоменный доступ к ресурсам).

References:

PreviousМожно ли атаковать, используя только HTMLNextВведение в Cross-Origin Resource Sharing (CORS)

Last updated 8 months ago

В разделе спецификации HTML вы можете найти полное определение. Давайте рассмотрим объяснение Origin в спецификации:

Только в июне 2019 года начались обсуждения о том, следует ли включать схему в рассмотрение. Для получения более подробной информации смотрите: .

В то время спецификация Same-Site не была определена в HTML-спецификации, которую мы видим сегодня, а в другой спецификации URL. Поэтому обсуждение было перенесено туда: . Затем в сентябре 2019 года был представлен этот PR: , который официально включил схему в спецификацию. Same-Site был определен как "onsidering the scheme", и был введен новый термин для обозначения игнорирования схемы: Schemelessly Same-Site.

, .

В ноябре 2020 года Chrome опубликовал статью: , в которой указывалось, что в то время разные схемы все еще считались Same-Site. Но из видно, что Chrome начал учитывать схему с версии 89.

Что касается Firefox, из статуса этого вопроса: кажется, что это поведение еще не является стандартным. Если не настроено специально, разные схемы все еще считаются Same-Site.

Определение Registrable Domain содержится в другой спецификации URL: .

Таким образом, после обращения к этому списку браузер распознает, что bob.github.io и alice.github.io не связаны и не являются Same-Site. Существует также конкретный термин для этого — eTLD (effective Top-Level-Domain). Для получения более подробной информации вы можете обратиться к статье:

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

Однако этот подход явно рискован. Например, если поддомен имеет уязвимость XSS, ее можно использовать для расширения воздействия. В статье 2016 года @fin1te под названием "" эта техника была использована для успешного выполнения XSS с поддомена на , увеличивая воздействие уязвимости.

Из-за проблем с безопасностью Chrome опубликовал 11 января 2022 года статью в своем блоге: "". В статье объясняется, что начиная с версии Chrome 101 поддержка изменения document.domain будет прекращена.

Статья также включает связанную дискуссию по этому изменению:

7.5 Origin
https://github.com/w3c/webappsec-fetch-metadata/issues/34
Consider introducing a "same-site" concept that includes scheme
Tighten 'same site' checks to include 'scheme'
Let HTML handle the "same site" definition #457
Define (schemelessly) same site for origins #5076
Schemeful Same-Site
Chrome platform status: Feature: Schemeful same-site
[meta] Enable cookie sameSite schemeful
спецификация URL
How to determine if two domains have the same owner?
7.5.2 Relaxing the same-origin restriction
An XSS on Facebook via PNGs & Wonky Content Types
www.facebook.com
Chrome will disable modifying document.domain to relax the same-origin policy
Deprecating document.domain setter
HTML spec
URL spec
如何判斷兩個網域的擁有者是否相同?
Chrome will disable modifying document.domain to relax the same-origin policy