Аутентыкацыя проты разрэшэння: дзе кожна з іх палягае ў вашам коде
Дазвольце даклэ научыцца, як аутентыкацыя і автарызацыя разлічаюцца па способе рэалізацыі, кодах стану HTTP і архітэктуре системы, каб запобiec частым проблемам з безпекай.
Два словы, якія практычна можна заменіць адзінам другым калі толькі паглядзець на яны, але кожна з іх рэгулюе абсалютна разную частку логікі вашай системы.
Якща вы калі-небудзь адправлялі код 401 у ситуацыі, калі на самай працэ быў код 403, або запускалі процес заходжання і заявлялі, што аутентыкацыя завершана, не чыпаючы логіку праваў, значыць вы вже сталкнуліся з проблемамі, якія выступаюць унаследке памешання эўх двух падходаў. Давайце раз і назаўсёды ўсё выяснім.
Аутентыкацыя: паўтарна перакананне ў ідэнтычнасці
Аутентыкацыя — это этап, калі система падтверджвае, што корыстнік дэйсна ёсць тым, кім себе прызнае. Яна адпавядае на адну толькі запытанне: чы рэальна гэта ідэнтычнасць?
Это адміністратыўны захад, які зазвычай выконваецца пад час заходжання ў систему, і як результат ствараецца певны тип аутентыкацыйных дакументаў — сесія, токен, кукі — якія дазволяюць рэшты прыемлівача доверляць апытчыку без павторнай пераўеркі пароля ў кожным запытанні.
Тыповыя спосабы рэалізацыі аутентыкацыі включаюць:
- Аутентыкацыя на адной паролі — наданыя аутентыкацыйныя дакументы паўтарна пераўераныя з адпаведным хэшаваным значэнням, якое зберагаецца.
- Аутентыкацыя на токене (JWT) — падписаны токен, створанный пасля успешнага заходжання ў систему, які пазней пераўераны ў кожным наступным запытанні.
- OAuth 2.0 / OpenID Connect — пераўеркі ідэнтычнасці аддаюцца зовнішнему прадаўцу, такаму як Google або GitHub.
- Клучы API — фіксаваная таёмная строчка, якая ідентыфікуе конкрэтнага кліента або сервіс.
Што выглядае стандартны абмян паўерыранням:
POST /api/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "hashed_and_verified_serverside"
}
Response: 200 OK
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Сервер паўерынае адправленыя данні пры входзе і, калі яны падтверджаныя, вяртае токен. Ад гэтага момента гэты токен служыць доказам аідэнтычнасці аб’екта, які выкарыстоўвае запыт, у кожным запыце:
GET /api/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Мідлвэр, які адпавядае за паўерыну гэтага токена, зазвычай выглядае так:
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token provided' });
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) return res.status(401).json({ error: 'Invalid or expired token' });
req.user = decoded;
next();
});
}
Зверніце увагу на тое, што цей мідлвэр спецыяльна не включае. Ніколі ён не выначае, што можа робіць корыстнік. Ён проста паўтарае, што токен є легітымны, і прыўязвае декодаваны пэйлоад да об’екта запиту. Нічога большага не ўскладнюе завадзівкі аутэнтыкацыі.
Автарызацыя: перакананне ў правах
Автарызацыя запускаецца толькі пасля таго, як аутэнтыкацыя вже успела. Яна адпавядае на абсалютна іншы вопыт: чы розглядваная ідэнтычнасць мае право выконваць гэтую конкрэтную дзеянне?
Дзеякі з распашчытных падходаў да структуравання автарызацыі:
- Контроль даступу на адной засадзе ролей (RBAC) — правыя прыўязваюцца да ролей такіх, як
admin,editorчыviewer
read:messages або write:filesТыповая перацэнка автарызаціі выкананая на адміністрацыю на адміністрацыю на адміністрацыю на адміністрацыю на адміністрацыю запыту, які ўжо праўерыўся праз аутэнтыкацыю:
function authorize(requiredRole) {
return (req, res, next) => {
if (req.user.role !== requiredRole) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.get('/api/admin/dashboard', authenticate, authorize('admin'), (req, res) => {
res.json({ data: 'sensitive admin data' });
});
Это два разныя функцыяй мідлвэра, якія керуюць двума рознымі задачамі і выконваныя за фіксаваным порядкам: authenticate выявляе, хто ўсё-такі здаў запит, а authorize вяршыць, чы розраховваецца на тое, што гэты аберант зможа працаваць далей. Запит можа перайсці праз першую пераканальню і ўсё-такі быць адхіленым праз другую — і такая адхіленне не мае нічынаго спраўа з ідэнтыфікацыяй.
Коды статусу, якія фактычна праказваюць гэтую разніцу
Сяродзе гэтага тэаретычны разлік ператвараецца на ўсё, што можна буквальна пераканаць у кодзе.
401 Unauthorized — назва ўводзіць у глухы кут, але гэты код на самай працоўцы сигналізуе пра проблему з аутентыкацыяй. Гэта значыць, што сервер не атрымаў жадных крантыфікацый, альбо тые, якія ён атрымаў (вакол токена), є нэваліднымі або выйшлі з часу дзеяння. У гэты момант сервер не мае жадных паводзяў, хто самэй здаў запит.
403 Forbidden — гэты код сігналізуе пра проблему з автарызацыяй. Сервер уже ведае, хто ўчаснік запыту; ён проста не дазволяе гэтай айдэнтыцы выканаць запрошаную дзеянне чыта прыйсці да запрошанага ресурсу.
// Authentication failure — identity unknown or invalid
res.status(401).json({ error: 'Invalid credentials' });
// Authorization failure — identity known, permission denied
res.status(403).json({ error: 'You do not have access to this resource' });
Адправка коду 401 у разы, калі насправды ідзе пра проблему з правамі, — гэта частая памылка. Гэта прасіць кліента знову заўвайсці, хоць насправды всё з заўвайшчам у порядку — проста у акаунті няма неабходнай ролі. Лячна такая памылка можа ператворыць тое, што магло бы стаць простым рашэнням, у дзяўгі, некоректны процес адлучэння прычыны на боку кліента, калі шукаюць баг у заўвайшчы, якога няма.
Дзе гэта відбываецца ў рэальных архітектурах
Палеты дадзеных JWT часта сумешчваюць гэтыя аспекты без жадання каго-небудзь. Токен павінен хаваць даныя або ідэнтыфікацыі — такія як sub, email, iat і exp. Тое, чы роўна яго трэба таксама носіць даныя прывілеў, напрыклад role або scope, — гэта рашэнне па дизайну, якое залежыць ад системы. Размешчэнне ролей безпосередна ў токене ёсць зручным і дазволяе ухіліцца ад дадатковых пошукоў, але гэта караецца рэальнымі наследкамі: будзь-яя змена прывілеў пользователя не будзе застосавана, пакуль той токен не будзе выданы знова. Гэта справжняя кампенсацыя, а не шляхавацтво без негатыўных наследкаў.
{
"sub": "user_12345",
"email": "user@example.com",
"role": "editor",
"iat": 1710000000,
"exp": 1710003600
}
Сістэмы, паблікаваныя на адміністрацыйскай стороне сервера, зазвычай роздзеляюць гэтыя два аспекты яшчэ больш. Ідэнтыфікатор сесіі адпрацоўвае праблему аутантыкацыі, тады калі правыя на выконанне дзеянняяў запрашваюцца з базы дадзеных за кожны запит. Чыгунам такі падход узбегае проблему застарэлых даных, але цэна — дадатковы запит да базы дадзеных за кожны запит.
У раскладах з мікросервісамі аутантыкацыя зазвычай централізавана — яе адпрацоўвае гэйтвей или прадаўцу ідэнтычнасці, такі як Auth0, Keycloak чым AWS Cognito, які выдае токен. Аднак автарызацыя зазвычай залічваецца на адпаведным сервісе, які чытае інформацыю з гэтага токена і застосоўвае своія правіла. У результате адна і тая ж аутантыкацыйная дзейніца можа быць пераканана аднаццацю разных сервісоў по-разнаму, пры чым кожны з іх незалежна вырашае, што гэтая ідэнтычнасць можа робіць.
Правіло, якое запобегае большасці багоў у безпэцы
Автанацыя павінна выконвацца раней за автарызацыю, і гэтыя два процесы ніколі не павінны быць адналежна ўжоў у адной сумарнай перацэнке.
// Correct: sequential, separate concerns
app.post('/api/posts/:id/delete', authenticate, authorize('editor'), deleteHandler);
// Wrong: conflating identity with permission in one step
app.post('/api/posts/:id/delete', (req, res) => {
if (req.headers.authorization === 'valid-token-and-also-admin') {
// fragile, unscalable, and impossible to audit correctly
}
});
Сістэма, якая рэгулярна атестуе, але пры этам прахоўвае автарызацыю, ідеальна параджуе айдэнтычнасць, а потым дазваляе кожной параджанай айдэнтычнасці робіць все, што яна хоча — таму атака на адны кантэкст з мягкімі правамі фактычна значыць атаку на весь системны кантэкст. Якща ж автарызацыя слабая, але автарызацыя рэгулярна налаштаваная, то выступае працоўны проблем: рэгулярна налаштаваныя правы працоўваюць над дверзямі, якія кожны можа працягнуць без жадных перакананняў.
Обе складовыя павінны быть надзейнымі, застаўляцца у правым порядку і расследжваліся як аднае самастоятельнае пытанне. У гэтым і заключаецца весь разлік — і калі гэта будзе належным чынам адбітася ў вашых кодах статусу, ланцюгу мідлвэра і спосабе проектавання токенаў, цэлы клас бягоў безпекі проста зникае.
Супакойлена літэратура
- Выкананне токенаў доступу і оновлення разам у Node.js — Дазнаецеся, як саюзваць токены доступу з короткім трыманнем і токены оновлення, якія зменшваюцца, у Node.js, каб падбаць як безпеку, так і бесперашкодныя сеансы корыстнікаў.