CSRF (Cross-Site Request Forgery) — это тип атаки на веб-приложения, при которой злоумышленник заставляет пользователя выполнить нежелательные действия на веб-сайте, на который он авторизован. Атака заключается в подделке запросов, отправленных пользователем, что может привести к выполнению опасных действий от его имени, таких как изменение пароля, отправка денежных средств или удаление данных.
В отличие от других видов атак, например, XSS (межсайтовые скриптовые атаки), CSRF не требует внедрения вредоносного кода в приложение. Атака использует доверие, которое веб-сайт оказывает аутентифицированному пользователю.
Предположим, что пользователь авторизован на веб-сайте, например, на интернет-банке. В это время он посещает злонамеренный сайт, который отправляет POST-запрос на URL интернет-банка с действиями, которые он может выполнить, например, перевод денег. Этот запрос будет выполнен от имени пользователя, потому что браузер автоматически прикрепит к запросу авторизационные куки, которые он ранее получил при входе в интернет-банк.
Простой пример атаки CSRF может выглядеть так:
<img src="http://bank.com/transfer?amount=1000&to_account=12345" style="display:none">
Здесь злоумышленник создает запрос, который вызывает перевод средств на другой счет. Если у пользователя открыта сессия в банке, запрос выполнится с его правами.
Для защиты от CSRF в современных веб-приложениях используют несколько методов. Рассмотрим основные из них.
Один из самых распространенных методов защиты — это использование уникальных токенов, которые включаются в каждую форму или запрос, отправляемый с клиента на сервер. Эти токены генерируются на сервере и привязываются к сессии пользователя. Когда запрос отправляется, токен должен быть передан серверу, и сервер проверяет его перед выполнением операции.
Для реализации защиты CSRF с использованием токенов в Svelte можно воспользоваться следующим примером:
Серверная сторона (например, с использованием Express.js):
app.get('/form', (req, res) => {
const token = generateCSRFToken(); // Генерация токена
res.render('form', { csrfToken: token });
});
app.post('/submit', (req, res) => {
const csrfToken = req.body.csrfToken;
if (!validateCSRFToken(csrfToken)) {
return res.status(403).send('CSRF token mismatch');
}
// Обработка запроса
});
Клиентская сторона (Svelte):
<script>
let csrfToken;
// Функция отправки формы
async function submitForm() {
const response = await fetch('/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ csrfToken: csrfToken, data: formData })
});
if (response.ok) {
// Успех
} else {
// Ошибка
}
}
// Загрузка токена
async function fetchToken() {
const response = await fetch('/get-csrf-token');
const data = await response.json();
csrfToken = data.csrfToken;
}
fetchToken();
</script>
<form on:submit|preventDefault={submitForm}>
<!-- Поля формы -->
<input type="hidden" name="csrfToken" bind:value={csrfToken} />
<button type="submit">Отправить</button>
</form>
В данном примере токен CSRF передается через скрытое поле в форме, и сервер проверяет его при получении запроса.
Некоторые фреймворки и библиотеки предлагают защиту с помощью
специальных заголовков HTTP. Одним из таких заголовков является
SameSite, который добавляется к кукам, отправляемым
браузером. Этот заголовок предотвращает отправку куков при
кросс-сайтовых запросах, тем самым защищая от CSRF.
Для того чтобы использовать этот заголовок в приложении, необходимо указать его при отправке куки на сервер:
Серверная сторона (например, с использованием Express.js):
app.use(session({
secret: 'some-secret',
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: true, // Только для HTTPS
sameSite: 'Strict' // Предотвращает отправку куки на кросс-сайтовые запросы
}
}));
Заголовок SameSite может принимать значения:
Strict — запрещает отправку куки на кросс-сайтовые
запросы.Lax — позволяет отправку куки на безопасные
кросс-сайтовые запросы, например, при переходах по ссылкам.None — позволяет отправку куки на любые кросс-сайтовые
запросы, но требует использования защищенного соединения (HTTPS).Проверка заголовка Referer — это еще один способ защиты
от CSRF. Сервер может проверять, что запрос пришел с доверенного домена.
Однако этот метод не является универсальным, так как заголовок
Referer можно подделать, и некоторые браузеры могут не
передавать его по соображениям конфиденциальности.
Пример использования на сервере:
app.post('/submit', (req, res) => {
const referer = req.headers['referer'];
if (!referer || !referer.startsWith('https://trusted-domain.com')) {
return res.status(403).send('Invalid referer');
}
// Обработка запроса
});
Чтобы предотвратить использование уязвимостей CSRF в сочетании с XSS-атаками, можно использовать такие механизмы как Content Security Policy (CSP), которые ограничивают выполнение небезопасных скриптов и использование фреймов.
CSP позволяет указать, какие источники контента считаются безопасными, и ограничивает выполнение скриптов на основе их происхождения. Например:
Content-Security-Policy: frame-ancestors 'self'
Этот заголовок запрещает встраивание страницы в IFrame с других источников.
Для более комплексной защиты от CSRF в приложении на Svelte можно
интегрировать методы, которые описаны выше. Например, можно использовать
библиотеку для работы с токенами CSRF, а также применить защиту через
заголовки SameSite для куков.
Сначала на сервере генерируется и передается токен CSRF в виде
скрытого поля, которое затем отправляется с запросом. Также следует
убедиться, что на сервере правильно настроены заголовки для
предотвращения атак, например, через SameSite для
куков.
Пример отправки защищенного запроса:
<script>
let csrfToken;
// Отправка запроса с CSRF-защитой
async function sendRequest() {
const response = await fetch('/action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken // Отправка токена в заголовке
},
body: JSON.stringify({ actionData: someData })
});
if (response.ok) {
// Обработка успешного ответа
} else {
// Обработка ошибки
}
}
// Получение токена
async function fetchToken() {
const response = await fetch('/get-csrf-token');
const data = await response.json();
csrfToken = data.csrfToken;
}
fetchToken();
</script>
<form on:submit|preventDefault={sendRequest}>
<input type="hidden" name="csrfToken" bind:value={csrfToken} />
<button type="submit">Отправить запрос</button>
</form>
В данном примере защита CSRF осуществляется с использованием как скрытого поля формы, так и передачи токена в заголовке запроса. Также важно, чтобы сервер проверял этот токен и отклонял запросы с неверным значением токена.
Защита от CSRF является важным аспектом безопасности веб-приложений. Использование токенов, правильная настройка заголовков куки и проверка источника запросов — все это помогает предотвратить атаки CSRF и обеспечить безопасность данных пользователя. В случае работы с фреймворком Svelte важно правильно интегрировать эти механизмы защиты на уровне сервера и клиента, чтобы избежать уязвимостей в приложении.