Как запретить сливать токен с ограниченным permission при небезопасном хранении?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Защита ограниченных токенов при небезопасном хранении
Это критичная проблема безопасности на фронтенде. Если токен с ограниченными правами лежит в localStorage или sessionStorage, его легко украсть через XSS. Давайте разберемся в стратегиях защиты.
Основная проблема
Если токен хранится в доступном хранилище, он может быть украден через:
- XSS (выполнение чужого JS в контексте сайта)
- CSRF (если хранится в localStorage)
- DevTools (если пользователь инфицирован малваром)
Стратегия 1: Использование HttpOnly cookies
Это основная защита — браузер не дает JS доступ к cookie. На бэкенде:
response.set_cookie(
auth_token,
token,
httponly=True,
secure=True,
samesite=Strict,
max_age=3600
)
На фронтенде токен отправляется автоматически при включении credentials:
fetch("/api/user", {
credentials: "include"
})
Плюсы:
- XSS не может украсть токен
- CSRF защищена через SameSite
- Браузер управляет сроком действия
Стратегия 2: Разделение токенов (Access + Refresh)
Делим токены по назначению: access token живет мало (15 мин), refresh token — долго (7 дней). Оба в httpOnly cookies:
async function apiCall(url) {
let response = await fetch(url, {
credentials: "include"
});
if (response.status === 401) {
await fetch("/api/refresh", {
method: "POST",
credentials: "include"
});
response = await fetch(url, {
credentials: "include"
});
}
return response;
}
Плюсы:
- Access токен живет мало (даже если украдут, мало вреда)
- Refresh токен в httpOnly (не видим JS)
- Можно отозвать токен на бэкенде
Стратегия 3: CSRF токены для дополнительной защиты
Если нельзя использовать httpOnly cookies:
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch("/api/update", {
method: "POST",
headers: {
"X-CSRF-Token": csrfToken
},
body: JSON.stringify(data)
});
Бэкенд проверяет: если токен не совпадает с тем, что в cookie — отклоняет запрос.
Стратегия 4: Memory storage + auto-logout
Хранить токен только в памяти (теряется при перезагрузке):
let accessToken = null;
let inactivityTimer = null;
function setAccessToken(token) {
accessToken = token;
resetInactivityTimer();
}
function resetInactivityTimer() {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(() => {
accessToken = null;
}, 30 * 60 * 1000);
}
Плюсы:
- XSS не может украсть токен (только во время сессии)
- Автоматический logout
Стратегия 5: Binding токена к браузеру
Связываем токен с уникальным ID браузера:
const deviceId = localStorage.getItem("device_id") || generateUUID();
localStorage.setItem("device_id", deviceId);
const response = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({
username,
password,
device_id: deviceId
})
});
fetch("/api/data", {
headers: {
"X-Device-ID": deviceId
},
credentials: "include"
});
Плюсы:
- Если украдут cookie, без device_id не помогает
- Защита от replay атак
Рекомендуемый подход
Комбинация стратегий:
- Access token в httpOnly cookie (15 мин)
- Refresh token в httpOnly cookie (7 дней)
- CSRF токен через X-CSRF-Token заголовок
- Device binding через X-Device-ID заголовок
- SameSite=Strict на всех cookies
- Secure флаг (только HTTPS)
Результат на клиенте:
const response = await apiClient.post("/api/update", data);
Всё управляется внутри: cookies отправляются автоматически, CSRF токен добавляется автоматически, refresh происходит при необходимости.
Эта комбинация делает кражу токена практически невозможной и обеспечивает защиту от основных векторов атак на фронтенде.