Галоўная / Артыкулы / Сэшны протыкса JWT-аўтантыфікацыя: выбор правильнага модэлю аутантыфікацыі для Node.js

Сэшны протыкса JWT-аўтантыфікацыя: выбор правильнага модэлю аутантыфікацыі для Node.js

Дазвольце даклэ расказаць, у чым насправдзе разлік межа сэсіямі і JWT-файламі пад аутантыкацыяй у Node.js, як функцыонуюць кожны з іх, і як выбраць аднойчы тое, што не будзе прыкро пазніяй.

1335 слоў

Проблема, яка існуе ў обох падходах

У HTTP няма вбудованай памяці пра ўсё, што было у пытанні ранейшага запиту. Кожны новы запит выглядае так, нібы ён прыходзіць ад абсалютна незнаёмага, якщо толькі ён не несе з сабой доказа ідэнтыфікаціі. Сесіі та JWT-клейтвы рашаюць гэту проблему адносова тым самым фундаментальным спосабом: яны даюць кліенту фрагмент дадзейнаў, які ён можа вернуць у кожны наступны запит. Тое, што насправды ўскладнюе ўжыццё іх, — гэта характер таго фрагмента дадзейнаў, а ўсё важлівейшае — тое, дзе фактычна знаходзіцца автаномныя даны пра тое, „хто ўвійшоў у систему“.

Варыянт першы: Сесіі

У схеме, якая базуецца на сесіях, автаномнае права мае сервер. Калі корыстнік увайшоў у систему, сервер стварае вялікі адбыточны ідэнтыфікатор сесіі, зберагае рэальную інформацыю пра корыстніка пад гэтым ідэнтыфікаторам у такіх средствах, як Redis або база дадзейнаў, і дае кліенту толькі сам ідэнтыфікатор, які зазвычай зберагаецца ў куку.

app.post("/login", async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  const sessionId = generateSecureId();

  await redis.set(`session:${sessionId}`, JSON.stringify({ userId: user.id }), "EX", 86400);
  res.cookie("sessionId", sessionId, { httpOnly: true, secure: true });
  res.json({ success: true });
});

app.use(async (req, res, next) => {
  const sessionId = req.cookies.sessionId;
  const session = await redis.get(`session:${sessionId}`);
  req.user = session ? JSON.parse(session).userId : null;
  next();
});

Пачаткуючы з гэтага моманту, кожны прыходзячы выкалека запускае пошук у там, дзе зберагаюцься даныя сесіі. Гэты адзін механізм поясняе як тое, чаму сесіі ўжытковыя, так і тое, чаму яны ствараюць додатковую навантажэння.

Плюсы: вы маглівы абсолютна і негайна контроль над тым, хто залишаецца запісаным. Заканчэнне сесіі, незалежна ад таго, чыя прычына — выход корыстніка, перазначэнне пароля чы адміністратар, які закрыў скомпрометаваны акаунт, — гэта проста выдаленне дакумента з базы сесій. Не трэба чакаць, пакуль ўсё сама заканчыцца за час абліквідацыі.

Мінусы: тепер кожны выкалека вымагае двухкроковага пераводу да базы сесій, што дадае невялікую, але рэальную затрэмтанасць і навантажэння на інфраструктуру. Це таксама ператварае вашу базу сесій у спяльны стан, да якога павінны дасягнуць усе вашы серверы, што стае сэрйозной архітектурнай проблемай як толькі вы расширюецеся за межы адзінаго сервера.

Варыянт два: JWT

JWT (JSON Web Token) працюе інакш: сервер падписвае токен, які вже мае даныя пользователя, а кліент перасылае гэты самы токен з кожным запитам. Сервер пераканальвае падпис і давае значэнне там, што ў токене, без патрэбы шукаць інфармацыю ў іншых месцах.

app.post("/login", async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "1h" });
  res.cookie("token", token, { httpOnly: true, secure: true });
  res.json({ success: true });
});


app.use((req, res, next) => {
  try {
    const decoded = jwt.verify(req.cookies.token, process.env.JWT_SECRET);
    req.user = decoded.userId;
  } catch {
    req.user = null;
  }
  next();
});

Не выконваецца жадных запытоў да базы дадзеных і не выконваецца жадных запытоў да Redis. Сам па сабе крыптаграфічны падпис дастатковы, ўбеджваючы, што токен не быў пахістаны з моменту його выдачы.

Плюсы: няма неабходнасці шукаць інфармацыю ў базе дадзеных чы кэшы з кожным запытам, што значна прыбліжае працэсы, а таксама няма спакульнага хранення сэсій, да якога павінны дасягнуць усе серверы, што спрощае горизонтальнае масштабаванне і робіць архітектуры безстацыйных мікросэрвісаў значна простымі для разумення.

Компроміс: калі чак JWT быў выдан, сервер не мае можлівасці анулювацыя яго прыбліжна да часу выканання. Якщо токен будзе вкрадзены, або якшчы доступ пользователя трэба негайна паставіць пад контроль, сервер не можа нічога адмахнуць, таму што спачатку не было схована нічога центральнага. Гэты самэўплата часта застае людзей без адпаведных заходоў, зазвычай пасля таго, як яны вже спроекавалі свой система на адной падставе, што JWT — гэта проста быстрейшая, лепшая версія сесіі.

Хыбныя уявленні, якія ствараюць рэальныя проблемы

Фраза „JWT-ы ў стане без стану“ часта выкорыстоўваецца як безумовная перавага, таму люди не разумеюць яе справжньяго значэння: наявнасць у стане без стану таксама означае, што па зялёнай лініі яны ўсталяваны без можлівасці анулювання. Якщо кукі сесіі будзе скомпрометавана, яе проста трэба адмахнуць. У той час як вкрадзены JWT застаецца абсолютна дыяўальным і цэлкам падтрымваным вашым серверам працэс часу, пакуль трывае тымчасавы період його дзейнасці, як толькі вы самі не створылі дадатковых мераў, каб гэта запобiec.

Тыповы спосаб рашэння, які выбіраюць люди, — це падтрымка спіса заблокаваных токенам:

app.use(async (req, res, next) => {
  try {
    const decoded = jwt.verify(req.cookies.token, process.env.JWT_SECRET);
    const isRevoked = await redis.get(`revoked:${decoded.jti}`);
    if (isRevoked) throw new Error("Token revoked");
    req.user = decoded.userId;
  } catch {
    req.user = null;
  }
  next();
});

Гэта дзеяўчы, але прыглядзіцеся цікава да таго, што вы ўжо зрабілі: вы вернуліся да перагляду кожнай запыткі ў спяльным хранілішчы дадзэнняў, што якраз і є тым надтрымлівам, ад каго якраз і павінны былі пазбавіцца JWT. У гэты момент у вас больш не існуе системы без стану. У вас ёсць система, базаваная на сесіях, якая маскуюцца пад іншае, з болей заплутаным спосабам рашэння проблем.

Тады каторую ж насправе трэба вжываць?

Выбірайте сесіі, калі: вам патрэбна негайная і надзеяна ануляцыя (думаючы пра дапрыемкі для банкавання, панелі адарбніка чы ўсё, дзе важліва безпека), вы запускаеце адну аплікацыю за дапамою балансэра навантажэння, які можа без вялікіх проблем выкарыстоўваць спяльнае хранілішча сесій, або ж вам проста лепей падтрымваць едынага джерела правды, чым кераваць трымкамі токенаў і логікаю блак-ліста.

Іспользуйце JWT-ы калі: вы керуеце аутэнтыкацыяй у сапраўдна незалежных службах, якія не патрабуюць прымусовага прыступу да аднаго спільнага хранальніка сесый, вы ствараеце систему, у якой корыстэнне короткавечных токенаў (хвіліны замест дзён) дазволяе прыйняць можлівасць перыяду паўтарнага заборання, або фактычная прычына, чаму вы хочаце безстановую модель, — гэта тое, што вы обслужваеце многа незалежных корыстувачаў API, а не толькі таму, што гэта звучыць прыемнаясцю.

Адзін нюанс, якій варта ўвага: на практыцы большасць систем для вырабоцтва не выбірае адну з сторон без альтэрнатыў. Формата, да якога воні прыходзяць, — це короткагалоўкіе JWT-токены, якія союзуюцца з токенам апавежэння, які зберагаецца на стороне сервера. Цэлая схема ўскладнена, а не двухкомпонентная: сесіі керуюць довгастралявым довяроўчам стацусам і процэсам анулювання, тады як JWT-токены забезпечваюць короткія, безстацусныя періяды верыфікацыі межу ўсім гэтым. Якщо вы спрыяжаеце «сесія проты JWT» як выбор между двума альтэрнатывамі, гэта зазвычай ўскладненне, якое свідчыць пра тое, што вы ўсё яшчэ не досяглі такога масштабу, калі такая гібрыдная схема становіцься доцэльной, тое значыць простая сесія, верагатна, будзе чыстасцю і простасцю кращай пачатковай точкай.

Фактычны выбор

Пад спуском, пытанне ніколі не было чыста тэхнічная, на самай працо ёна стосуецца выбору таго, дзе будзе практыкувана вартасць. Сесіі налагоджуюць гэтую вартасць за кожны адзін запит, але ў замяну вы отрымваете кантроль, які завжды є актуальным, ніколи не застарэлым. JWT-ы усунуць гэтую плату за кожны запит, але цінаю для гэтага є перыяд часу, калі тое, што ваш сервер сачытае, і тое, што ў рэальнасці правда, можа тыхо адступіць адзін ад другога. Жадны з гэтых падходоў не заслуговае на атрыбут «савэцкі» чы «застарэлы», незалежна ад таго, як часта ў дыялогах па ўсё гэтае ставятся акценты. Це проста два разныя спосабы рашэння той самай дилемы, якая ніколі насправдзе не зникае.

Спадневана літэратура

  • Выбір между EC2, ECS і EKS для задач на Node.js — Порівнюе, як EC2, ECS з Fargate і EKS керуюць роботай прыкладанняў на Node.js з пункту точакі выконання, чым дапамагае выбраць наяўны сервіс AWS для вашай команды ў залежнасці ад масштабу і навыкаў.
  • Впрынцэпаванне токенав доступу і паўнаважненняя разам у Node.js — Дзеўедзіце, як саюдзьвязаць токены доступу з короткім тымчасавым періодам дзеяння з токенамі паўнаважненняя, якія змінююцца, у Node.js, каб падтрымаць баланс между безпекай і неперарвымі сесіямі корыстуначаў.
  • Стварэнне аутентыкацыі на аснове JWT, гатовай да выкорыстання ў працоўных сэрвісах, у API на Node.js — як хешаваць паролі, выдаваць тымчасовыя токены JWT, дадаць токены апавержэння і механізмы ўсунення з вядома, аддзеляць правыя на доступ, захіствацца ад атак типу brute force і пераканальванне, што аутентыкацыя правільна не вядома.
  • Калі аутентыкацыя на аснове JWT стае залежной ад стану: праблемы і рашэння з викорыстаннем сесій на боку сервера ў Node — як спискі блакірання та базы дадзеных для токенаў апавержэння робяць аутентыкацыю на аснове JWT хаварнай, як сесіі у Express з падтрымкай Postgres спростоўваюць ўсё гэта, і дзе JWT все ж можна выкорыстоўваць.