Слепые атаки: понимание CSRF (подделка межсайтовых запросов)
2 июня 2022 г.Недавно я занялся исследованием веб-безопасности, когда писал [Понимание асинхронного JavaScript] (https://asyncjs.today) — я хотел убедиться, что мои рекомендации безопасны, и я не окажу своим студентам медвежью услугу своими рекомендациями. .
К сожалению, статьи в области безопасности были довольно сложными для понимания. В статьях было много слов, которые вызывали страх, неуверенность и сомнения. Когда я читаю эти статьи, меня охватывает эмоциональная паника, и я беспокоюсь, что могу в конечном итоге сделать что-то не так, хотя намерение этих статей было хорошим!
Многие статьи также не раскрывают полной информации о CSRF, о том, как настроить CSRF-атаку и как предотвратить CSRF-атаку, что оставляет меня сомневающимся в том, что я узнал. В итоге мне приходится разбираться во всем самостоятельно.
Я хочу, чтобы вам было легче понять CSRF, поэтому я решил написать статью с полной (и пошаговой) информацией об атаках CSRF. Я надеюсь, что эта статья даст вам ясность и уверенность, необходимые для создания безопасных веб-приложений.
Два вида CSRF-атак
Существует два вида CSRF-атак:
- Обычная CSRF-атака
- Войти CSRF
Сначала мы обсудим обычную CSRF-атаку, а затем логин CSRF.
Что такое CSRF-атака
Атака CSRF — это атака, которая обманом заставляет жертву отправить вредоносный запрос — запрос, который они не собирались делать — на веб-сайт, где они аутентифицированы (вошли в систему).
Запрос должен исходить от другого веб-сайта, который называется «Кросс-сайт». Этот запрос также выдает себя за аутентифицированного пользователя, что дает ему имя «Подделка запроса».
CSRF-атаки слепые — это означает, что злоумышленник не видит, что происходит после отправки запроса жертвой. Атаки CSRF часто нацелены на изменение состояния сервера.
Что такое изменение состояния? По сути, все, что изменяет базу данных, является изменением состояния.
Примеры изменений состояния включают:
- Изменить имя пользователя и пароль
- Отправка денег на счет
- Отправка поддельных сообщений из учетной записи пользователя
- Обмен неприемлемыми изображениями или видео из учетной записи пользователя
Атаки CSRF используют тот факт, что браузеры автоматически отправляют файлы cookie на сервер при каждом запросе. Без какой-либо защиты CSRF сервер может предположить, что запрос действителен, когда присутствует файл cookie аутентификации.
Файлы cookie аутентификации могут быть любыми, если сервер использует их для проверки подлинности пользователя. Это может быть токен доступа. Это также может быть идентификатор сеанса. Это зависит от того, как сервер обрабатывает аутентификацию.
Предпосылки для работы CSRF-атак
Для успешной атаки CSRF необходимы четыре предварительных условия.
- На сервер отправляется запрос любого метода.
- Пользователь должен пройти аутентификацию.
- Сервер должен хранить информацию об аутентификации в файлах cookie.
- На сервере не реализованы методы предотвращения CSRF (о которых будет сказано ниже).
Как работают CSRF-атаки
Прежде чем злоумышленник сможет запустить CSRF-атаку, ему необходимо найти последовательный запрос, на который он может нацелиться. Они бы знали, что делает запрос. Это может быть любой запрос — GET, POST, PUT или DELETE.
Как только они выбирают целевой запрос, они должны сгенерировать поддельный запрос, чтобы обмануть пользователя.
Наконец, они должны обманом заставить пользователя отправить запрос. В большинстве случаев это означает:
- Найдите способ отправить запрос автоматически без ведома пользователя. Наиболее распространенные подходы — это использование тегов изображений и автоматическая отправка формы JavaScript.
- Искажение ссылки (или кнопки), которая заставляет пользователя щелкнуть ее. (AKA Социальная инженерия).
Атаки через GET-запрос
Атаки CSRF с запросом GET работают только в том случае, если сервер позволяет пользователю изменять состояние с помощью запросов GET. Вам не нужно беспокоиться об этом типе атаки CSRF, если ваши запросы GET доступны только для чтения.
Но допустим, у нас есть сервер, который не следует передовым методам программирования и позволяет изменять состояние с помощью запроса GET. Если они сделают это, у них будут проблемы — огромные проблемы.
Например, предположим, что есть банк, который позволяет вам переводить деньги со следующей конечной точкой. Вам просто нужно ввести «счет» и «сумма» в запросе GET, чтобы отправить деньги человеку следующим образом:
``` оболочка
https://bank.com/transfer?account=Mary&amount=100
Злоумышленник может сгенерировать ссылку, которая отправляет деньги на его счет.
``` оболочка
Отправляет 9999 на аккаунт злоумышленника
https://bank.com/transfer?account=Attacker&amount=9999
На этом этапе злоумышленник может найти способ автоматически активировать ссылку без ведома пользователя.
Один из способов — включить ссылку в изображение 0x0 на веб-странице или в электронном письме. Если пользователь посещает эту веб-страницу или электронную почту, запрос GET запускается автоматически, поскольку браузеры и электронные письма настроены на автоматическую загрузку изображений.
(Теперь я понимаю, почему провайдеры электронной почты отключают загрузку изображений в качестве меры предосторожности).
```разметка
<изображение
src="https://bank.com/transfer?account=Attacker&amount=9999"
ширина = "0"
высота = "0"
граница = "0"
Другой способ — исказить то, что делает ссылка. Это работает, потому что люди не проверяют ссылки, прежде чем перейти по ним. Если человек щелкнет ссылку, он отправит запрос GET для злоумышленника, не зная об этом.
```разметка
<a href="https://bank.com/transfer?account=Attacker&amount=9999"
Просмотреть мои фотографии</a
Если пользователь аутентифицирован, сервер получит файл cookie аутентификации, который позволяет ему поверить, что запрос действителен. Если сервер не использовал никаких механизмов защиты от CSRF, деньги будут отправлены злоумышленнику.
Примеры GET CSRF-атак:
- uTorrent подвергся CSRF-атаке еще в [2008] (https://en.wikipedia.org/wiki/Cross-site_request_forgery/), и он позволял изменять состояние с помощью запросов GET.
- Ранее в 2008 на YouTube была обнаружена уязвимость в системе безопасности, которая позволяла злоумышленнику выполнять практически все действия, возможные для пользователя, включая отправку сообщений, добавление в список друзей и т. д.
Если вы нажмете на ссылки выше. Вы сможете найти примеры реальных GET-запросов, которые создают такую CSRF-атаку. (Не волнуйтесь, здесь нет странных ссылок 😜).
CSRF-атаки с POST-запросами
CSRF-атаки с POST-запросами следуют той же схеме, но они не могут быть отправлены через ссылки или теги изображений. Их нужно отправить через форму или через JavaScript.
Предположим, что у нас есть одна и та же уязвимая конечная точка, и злоумышленнику просто нужно ввести информацию об «учетной записи» и «сумме», чтобы инициировать запрос.
``` оболочка
ПОСТ https://bank.com/transfer?account=Attacker&amount=9999
Злоумышленник может создать форму и скрыть от пользователя значения «счет» и «сумма». Люди, которые нажимают на эту искаженную форму, отправляют запрос POST без их ведома.
```разметка
Вы никогда не сможете выполнить CSRF-атаку через HTML.
А теперь интересное: как люди отправляют запросы PUT и DELETE через форму, если HTML этого не позволяет? После некоторых исследований я обнаружил, что большинство фреймворков позволяют отправлять запрос POST с параметром _method.
```разметка
<метод формы="сообщение" ...>
Вы можете выполнить CSRF-атаку «PUT» через JavaScript, но механизм предотвращения по умолчанию в современных браузерах и серверах делает эти атаки очень сложными — вы должны намеренно ослабить защиту, чтобы это произошло. Вот почему.
Чтобы выполнить CSRF-атаку «PUT», вам необходимо отправить запрос Fetch с методом «put». Вам также необходимо включить опцию «credentials».
```javascript
константная форма = document.querySelector('form')
// Отправляет запрос автоматически
форма.отправить()
// Перехватывает отправку формы и использует Fetch для отправки запроса AJAX.
form.addEventListener('отправить', событие => {
событие.preventDefault()
принести(/.../, {
метод: «положить»
Credentiials: 'include' // Включает куки в запрос
.тогда(/.../)
.ловить(/.../)
Это не сработает по трем причинам.
Во-первых, этот запрос НЕ будет выполняться браузерами автоматически из-за CORS. Если, конечно, сервер не создаст уязвимость, разрешив запросы от кого угодно со следующим заголовком:
``` оболочка
Доступ-Контроль-Разрешить-Происхождение: *
Во-вторых, даже если вы разрешаете всем источникам доступ к вашему серверу, вам все равно нужна опция «Access-Control-Allow-Credentials», чтобы браузеры могли отправлять файлы cookie на сервер.
``` оболочка
Access-Control-Allow-Credentials: правда
В-третьих, даже если вы разрешите отправку файлов cookie на сервер, браузеры будут отправлять файлы cookie только с атрибутом sameSite
, установленным на none
. (Они также называются сторонними файлами cookie).
Если вы понятия не имеете, о чем я говорю в отношении третьего пункта, вы в безопасности — вы действительно должны быть злонамеренным разработчиком, который хочет испортить ваш сервер, если вы отправляете файлы cookie аутентификации как сторонние файлы cookie.
Этот раздел огромен. Я создал еще несколько статей, чтобы помочь вам понять, что именно происходит — и почему так чертовски невероятно сложно подвергнуть себя CSRF-атаке PUT:
- [Общие сведения о файлах cookie sameSite] (https://zellwk.com/blog/samesite-cookies/)
- [Понимание учетных данных Fetch] (https://zellwk.com/blog/handling-cookies-with-fetchs-credentials/)
Короче говоря, вам нужно беспокоиться только о CSRF-атаках POST, если вы действительно не облажались со своим сервером.
Методы предотвращения CSRF
Наиболее распространенными методами профилактики CSRF на сегодняшний день являются:
- Двойной шаблон отправки файлов cookie
- Метод заголовка файлов cookie
Оба метода следуют одной и той же формуле.
Когда пользователь посещает ваш веб-сайт, ваш сервер должен создать токен CSRF и поместить его в файлы cookie браузера. Общие имена для этого токена:
- CSRF-ТОКЕН
- X-SRF-ТОКЕН
- X-XSRF-ТОКЕН
- X-CSRF-ТОКЕН
Используйте любое имя токена, которое вы предпочитаете. Все они работают.
Важно то, что токен CSRF должен быть случайно сгенерированной криптографически стойкой строкой. Если вы используете Node, вы можете сгенерировать строку с помощью crypto
.
```js
импортировать крипто из 'crypto'
функция csrfToken (req, res, next) {
вернуть crypto.randomBytes(32).toString('base64')
Если вы используете Express, вы можете поместить этот токен CSRF в свои файлы cookie следующим образом. При этом я рекомендую также использовать строгую опцию sameSite
. (Мы немного поговорим о sameSite
).
```javascript
импортировать cookieParser из «cookie-parser»
// Используйте это для чтения файлов cookie
app.use(cookieParser())
// Установка токена CSRF для всех конечных точек
app.use(*, (req, res) => {
const {CSRF_TOKEN} = req.cookies
// Устанавливает токен, если пользователь посещает эту страницу впервые в этой сессии
если (!CSRF_TOKEN) {
res.cookie('CSRF_TOKEN', csrfToken(), {sameSite: 'strict'})
То, как вы используете токен CSRF, меняется в зависимости от того, поддерживаете ли вы шаблон отправки Double cookie или метод Cookie to header (или оба).
Шаблон файла cookie с двойной отправкой
Название этого шаблона немного вводит в заблуждение, потому что оно, похоже, означает отправку файла cookie дважды с помощью «Double Submit Cookie».
Что это означает:
- Вы можете отправить токен CSRF в файле cookie
- Вы визуализируете
<form>
с токеном CSRF, который будет включен в отправку формы.
(Отсюда двойное подчинение).
Если вы используете Express, вы можете передать токен CSRF в HTML следующим образом:
```javascript
app.get('/some-url', (req, res) => {
const {CSRF_TOKEN} = req.cookies
// Рендерим с помощью Nunjucks.
// Замените Nunjucks любым другим механизмом шаблонов, который вы используете
res.render('page.nunjucks', {
CSRF_TOKEN: CSRF_TOKEN
Затем вы можете использовать форму CSRF_TOKEN
следующим образом:
```javascript
<форма>
Затем сервер может проверить действительность сеанса, сравнив два токена CSRF. Если они совпадают, это означает, что запрос не подделан, потому что злоумышленник не может угадать значение токена CSRF на другом веб-сайте.
```js
// Проверяем действительность токена CSRF
app.post('/login', (req, res) => {
const {CSRF_TOKEN} = req.cookies
const {csrf} = req.body
// Прервать запрос
// Вы также можете выдать ошибку, если хотите
если (CSRF_TOKEN !== csrf) вернуть
Метод Cookie to Header
Метод cookie для заголовка аналогичен, за исключением того, что он выполняется с помощью JavaScript. В этом случае токен CSRF должен быть включен как в файл cookie, так и в заголовок запроса.
В этом случае нам необходимо:
- Установите «учетные данные» на «включить» или «тот же источник», чтобы включить файлы cookie.
- Возьмите токен CSRF из
document.cookies
и добавьте его в качестве заголовка запроса.
Вот пример запроса:
```js
// Получает значение именованного файла cookie
функция getCookie () {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
если (совпадение) возвратить совпадение[2]
// Отправляет запрос
fetch('/login', (req, res) => {
учетные данные: «включить»,
заголовки: {
'CSRF_TOKEN': getCookie('CSRF_TOKEN')
Сервер может проверить действительность токена CSRF следующим образом:
```js
// Проверяем действительность токена CSRF
app.post('/login', (req, res) => {
const {CSRF_TOKEN} = req.cookies
const {CSRF_TOKEN: csrf} = req.headers
// Прервать запрос
// Вы также можете выдать ошибку, если хотите
если (CSRF_TOKEN !== csrf) вернуть
Сделайте все это проще с библиотекой
Я показал вам, как вручную создавать и тестировать токены CSRF, потому что хотел дать вам представление о процессе.
Этот процесс уже решался много раз, поэтому нам не следует выполнять его вручную (если только вы не учитесь, как это сделал я здесь).
Если вы используете Express, я рекомендую использовать библиотеку csurf, поскольку она более надежна и гибка по сравнению с тем, что я мог показать в этом примере выше.
Атрибут файла cookie SameSite
Установка для sameSite
значения strict
в приведенном выше примере гарантирует, что файл cookie токена CSRF будет отправлен на сервер только в том случае, если запрос исходит с того же веб-сайта. Это гарантирует, что токен CSRF никогда не попадет на внешние страницы.
Вы можете — необязательно, но рекомендуется — установить для атрибута sameSite
значение strict
, когда вы устанавливаете файл cookie для аутентификации. Это гарантирует, что никакие CSRF-атаки не могут быть проведены, поскольку файлы cookie аутентификации больше не будут включаться в межсайтовые запросы.
Нужна ли вам защита токена CSRF, если вы установили для sameSite
значение strict
для своего файла cookie аутентификации?
В большинстве случаев я бы сказал нет, потому что sameSite
уже защищает сервер от межсайтовых запросов. Нам по-прежнему нужен токен CSRF для защиты от одного конкретного типа CSRF: Логин CSRF.
Вы можете узнать больше о файлах cookie sameSite в этой статье.
Войти CSRF
Логин CSRF полностью отличается от обычных CSRF-атак с точки зрения намерений.
В CSRF для входа злоумышленник обманом заставляет пользователя войти в систему с учетными данными злоумышленника. После успешной атаки пользователь продолжит использовать учетную запись злоумышленника, если не обращает внимания.
```разметка