Сравнение 401 vs 403
Autentication (аутентификация) и Authorization (авторизация) — два разных понятия, которые часто путают. 401 vs 403 — это HTTP-выражение этой разницы. Authentication = «кто ты?». Authorization = «что тебе разрешено?». Сначала всегда первое, потом второе.
Когда что использовать
Используйте 401 когда
- HTTP-запрос пришёл БЕЗ cookie / authorization header.
- Cookie / token истёк.
- JWT signature не валидна.
- Basic Auth не предоставлен.
- Неверный пароль при логине.
- API endpoint требует аутентификации, клиент анонимен.
Используйте 403 когда
- User вошёл, но не admin (пытается /admin).
- User заблокирован (banned).
- Premium feature, но user на free плане.
- IP в чёрном списке.
- Geo-blocking (контент не для этой страны).
- Read-only доступ, но запрос на изменение.
- CSRF token mismatch.
- Maintenance mode для всех кроме админов.
The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials. The 403 (Forbidden) status code indicates that the server understood the request but refuses to fulfill it.— RFC 9110 — HTTP Semantics, IETF, 2022
Best practices
Express.js пример
// Middleware для проверки аутентификации (401)
function requireAuth(req, res, next) {
const token = req.cookies?.session || req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401)
.header('WWW-Authenticate', 'Bearer')
.json({ error: 'Authentication required' });
}
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (e) {
return res.status(401)
.header('WWW-Authenticate', 'Bearer error="invalid_token"')
.json({ error: 'Invalid or expired token' });
}
}
// Middleware для проверки прав (403)
function requireRole(role) {
return (req, res, next) => {
if (!req.user.roles?.includes(role)) {
return res.status(403).json({
error: 'Insufficient permissions',
required_role: role,
});
}
next();
};
}
// Использование
app.get('/api/profile', requireAuth, ...); // Возврат 401 если не вошёл
app.get('/api/admin', requireAuth, requireRole('admin'), ...); // Возврат 403 если не adminFrontend (React) — обработка 401/403
// axios interceptor
axios.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Попытаться обновить токен
const success = await tryRefreshToken();
if (success) {
// Повторить оригинальный запрос
return axios.request(error.config);
}
// Иначе — на login
window.location.href = `/login?return_to=${
encodeURIComponent(window.location.pathname)
}`;
} else if (error.response?.status === 403) {
// Показать toast
toast.error('У вас нет прав для этой операции');
// ИЛИ redirect на специальную страницу
// navigate('/access-denied');
}
return Promise.reject(error);
}
);Логирование
- 401 — обычное явление, не критично логировать каждое. Но: rate-limit на 401 от одного IP — защита от brute-force.
- 403 — ВАЖНО логировать каждое. Если пользователь массово получает 403 — это попытка эскалации привилегий или баг в правах.
- Алерт при массовых 403 — >50 за час от одного user — возможна атака.
Безопасность
- Не различайте «неверный логин» и «неверный пароль». Иначе атакующий может перебирать логины (account enumeration). Стандарт: «Неверный логин или пароль» для обоих случаев.
- Rate limit на login. 5 попыток в 5 минут — защита от brute-force. После — captcha или временная блокировка.
- Скрывайте sensitive ресурсы за 404. Если у пользователя нет прав на /admin/users/42 — возвращайте 404, не 403. Иначе атакующий узнает о существовании юзера 42.
- HTTPS обязательно. Без него токены/cookie перехватываются в открытом виде.
- HttpOnly + Secure + SameSite=Strict для cookie. Защита от XSS и CSRF.
- RFC 9110 — HTTP Semantics. IETF. datatracker.ietf.org/doc/html/rfc9110. 2022.
- OWASP — Authentication Cheat Sheet. OWASP. cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html. 2024.
- MDN — 401 Unauthorized. Mozilla. developer.mozilla.org/en-US/docs/Web/HTTP/Status/401. 2024.
