Как да внедрим JWT в API, изградени с Node.js

  • Разбиране на структурата на JWT (заглавка, полезен товар и подпис) и нейната роля в безсъстоятелното удостоверяване.
  • Конфигурирайте API в Node.js и Express с middleware, който проверява и защитава маршрути, използвайки JWT токени.
  • Прилагайте добри практики за сигурност: използване на HTTPS, управление на секретни данни, кратък срок на валидност, токени за опресняване и анулиране.
  • Интегрирайте външно удостоверяване, като например Google OAuth, като впоследствие издадете свой собствен JWT за API.

Внедряване на JWT в API, изградени с Node.js

Когато започнете да изграждате API с Node.js и трябва да се погрижите за сигурността, рано или късно се появява същият протагонист: JSON Web Tokens (JWT) като система за удостоверяванеТе са леки, работят чудесно със съвременни архитектури и ви позволяват да мащабирате, без да се налага да управлявате сървърни сесии.

В това ръководство ще видите, стъпка по стъпка и много подробно, Как JWT работи вътрешно и как да го интегрирате в Node.js API с ExpressТова обхваща както типичното влизане с потребителско име и парола и влизането в Google, така и най-добрите практики за сигурност, отмяна, използване на HTTPS, мидълуер и структура на проекта. Целта е, когато приключите с четенето, да имате цялостен преглед и да можете да го внедрите в собствения си API без никакви изненади.

Какво е JWT и как е структуриран токен?

JSON уеб токенът официално е отворен стандарт (RFC 7519) за предаване на твърдения във формат JSON между две части по компактен начин. За практически цели, това е прост текстов низ с три Base64 блока, разделени с точки: заглавка, полезен товар и подпис.

Когато потребител успешно се регистрира или влезе във вашия API, бекендът генерира JWT токен с неговите данни и го връща на клиента. Този токен след това пътува във всяка следваща заявка., обикновено в заглавката на Authorization като Bearer, за да се покаже кой е потребителят и какви разрешения има.

Типичен пример за токен би изглеждал подобно на тази структура: Заглавна част, кодирана в Base64, полезен товар, кодиран в Base64, и сигнатура, изчислени с хеш алгоритъмвсички свързани помежду си с точки. Въпреки че може да изглежда като безсмислици, всъщност това е много систематичен формат.

Ако вземете тази верига и я залепите на инструменти като jwt.ioЩе видите как се декодира в движение: от едната страна е суровият токен, а от другата, в обикновен текст, заглавката и полезният товар, с ключовете и стойностите, които сте въвели при генерирането му.

El хедър Обикновено включва полета като ALG (алгоритъм, например HS256) и тип (тип токен, обикновено JWT). полезен товар Той съдържа твърденията, т.е. информацията, която искате да транспортирате: потребителски идентификатор, име, роля, дата на изтичане и т.н. подпис Изчислява се чрез прилагане на HMAC или алгоритъм за подпис към кодирания заглавен файл и полезния товар, заедно със секретен ключ или двойка публичен/частен ключ.

Най-често срещаният механизъм за подписване в API с Node.js и Express е HMAC-SHA256 (HS256)който комбинира код за удостоверяване на съобщение, базиран на хеш, със споделена тайна. Подписът се генерира чрез прилагане на функцията към: заглавна част, кодирана в Base64, полезен товар, кодиран в Base64, и тайна, известна само на сървъраОсвен това е препоръчително да знаете как управление на сертификати и подписи когато работите с публични/частни ключове.

Хубавото на всичко това е, че ако някой подправи токена по пътя, Подписът вече не съвпада, когато сървърът проверява токена.Веднага щом проверката на подписа се провали, бекендът знае, че токенът е бил модифициран или не идва от легитимен източник и заявката трябва да бъде отхвърлена с код 401 или 403.

Важно е да се разбере това Base64 не е шифър, а просто кодиране.Всичко, което въвеждате в полезния товар, може да бъде прочетено, ако някой заснеме токена, поради което не бива да включвате критична информация в открит текст в JWT. Ето защо комбинирането на JWT с HTTPSтака че целият трафик да пътува криптиран и да не може лесно да бъде „подслушван“ в мрежата.

Предимства на използването на JWT в Node.js API

Една от основните причини, поради които JWT стана толкова популярен, е, че Токените са самостоятелни и без гражданство.Това означава, че за да валидира потребителя, сървърът не е необходимо да осъществява достъп до базата данни при всяка заявка, тъй като съответната информация се съдържа в подписания токен.

Тъй като това е просто текстов низ, JWT може да се изпраща в URL адреси, в тялото на POST заявка или в HTTP заглавки.Вписва се безпроблемно в типичните REST API и SPA (Single Page Application) потоци. Това е компактен формат, идеален за мобилни приложения, фронтендове в React, Vue, Angular и др., и дори за комуникация между микросървиси.

Друга силна страна е гъвкавостта на алгоритмите за подписване: Можете да използвате HMAC със споделена тайна (HS256) Или можете да изберете асиметрични схеми като RSA или ECDSA, където подписвате с частен ключ и проверявате с публичен ключ, което е много полезно в сложни бизнес среди.

В архитектури с множество услуги, JWT е чудесен избор, защото Това позволява на различни микросървиси да се доверяват на един и същ токен. без да е необходимо споделяне на сесии в паметта или преразпределяне на потребителски състояния. Всяка услуга се нуждае само от ключа за проверка и от правилата за интерпретиране на твърденията.

От гледна точка на UX, ако го имплементирате добре, JWT улеснява гладките и мащабируеми сесии, поддържащи механизми за обновяване на токени, контролирани срокове на валидност и като цяло добър баланс между сигурност и удобство за потребителя.

Подгответе средата за разработка с Node.js и Express

За да настроите API с JWT удостоверяване в Node.js, обичайният подход е да се разчита на Express като HTTP рамка и да го комбинирате с няколко ключови библиотеки, които значително опростяват живота.

Типичен работен процес при стартиране би бил да се създаде нова папка за проекта, да се влезе в нея и да се изпълни npm init -y за да генерирате package.json. Оттам инсталирате необходимите зависимости: Express за сървъра, jsonwebtoken за управление на JWT, bcrypt или bcryptjs за хеширане на пароли и dotenv за обработка на променливи на средата.

Ако ще работите с истинска база данни, като MongoDB, това обикновено е от значение. Mongoose за дефиниране на модели и схемиВ този случай, в директорията на проекта ви ще имате нещо като: основен стартов файл (например app.js), папка models с модела User.js и .env файл с чувствителни ключове.

Вътре в .env можете да съхранявате неща като JWT_SECRET, URI адресът за връзка с базата данни и времето на изтичане на токенаСлед това ще заредите тези данни с dotenv, така че да не бъдат твърдо кодирани в изходния код или качени в хранилища.

Потребителският модел обикновено включва полета като потребителско име или имейл и паролаи кука за предварително запазване, която хешира паролата преди да я съхрани. По този начин, дори ако някой получи достъп до базата данни, той няма да има паролите в обикновен текст, а хешове, генерирани с bcrypt.

В главния файл на приложението е нормално да се инициализира Express, активирайте мидълуера за парсиране на JSONСвържете се с MongoDB (ако го използвате) и дефинирайте маршрутите за регистрация, влизане и достъп до защитени ресурси. Оттам идва ключовата част: генериране и проверка на JWT токени.

Пълен процес на удостоверяване с JWT

В система за удостоверяване, базирана на JWT, типичната последователност обикновено включва регистрация на потребители, вход в системата, издаване на токени, проверка на всяка заявка и защита на частни маршрутиВъпреки че детайлите може да варират, цялостната структура винаги е една и съща.

В регистърКогато потребител изпрати данните си от клиента, вашият API получава тялото на заявката, хешира паролата с bcrypt, запазва потребителя в базата данни (или в структура в паметта, ако правите демонстрация) и отговаря, потвърждавайки регистрацията. Връщането на токен по време на регистрация не е задължително, въпреки че много API го правят, за да избегнат допълнителна стъпка за влизане.

В ВлизанеНещата стават интересни: получавате потребителско име и парола, търсите потребителя в базата данни, сравнявате предоставената парола със съхранения хеш, използвайки bcrypt, и ако съвпада, генерирате JWT токен, използвайки функцията знак на библиотеката jsonwebtokenВ този токен включвате полезни твърдения: потребителски идентификатор, име, роля и, много важно, дата на изтичане.

При конфигуриране на токена е добра практика да се установи разумен срок на валидност, например 1 или 2 часа за токена за достъпТова намалява времето за експозиция, ако токенът попадне в ръцете на някой, който не би трябвало да го има. За дълги сесии е полезно използването на токени за опресняване, което ще обсъдим по-късно.

Секретният ключ, който използвате за подписване на токена, трябва да идва от Променлива на средата никога не трябва да се записва директно в кода.Ако подозирате, че този ключ е бил компрометиран в даден момент, вашата стратегия за сигурност трябва да включва ротиране на секретни данни, отмяна на стари токени и генериране на нови.

След като данните за вход върнат токена на клиента, клиентът го съхранява (например в httpOnly бисквитка или в паметта) и го изпраща. в заглавката на Authorization със схемата Bearer при всяко извикване към защитени крайни точки. Сървърът, на тези крайни точки, ще активира мидълуер, за да валидира токена, преди да продължи с логиката на контролера.

JWT проверка и мидълуер за защита на маршрути

Ядрото на удостоверяването в API с Node.js и Express обикновено е многократно използваем междинен софтуер, който проверява токена при всяка заявкаФизически това е функция, която се изпълнява преди обработчика на маршрути и решава дали заявката преминава през филтъра или е отхвърлена.

Този мидълуер може да търси токена на различни места, но най-често срещаният метод е да прочете заглавката. Авторизация с формат на носителВ други проекти ще видите, че се използва персонализиран заглавен файл, като например x-auth-token; концепцията е същата, променя се само името на заглавния файл.

След като токенът бъде извлечен, междинният софтуер извиква jwt.verify с токена и секретния ключАко подписът съвпада и токенът не е изтекъл, ще получите декодирания полезен товар. Този полезен товар обикновено е прикачен към обекта req (например req.user), така че другите контролери да знаят кой е потребителят и какви претенции има.

Ако проверката е неуспешна, защото подписът не съвпада, токенът е бил променен, е изтекъл или просто не съществува, Подходящият отговор от мидълуера ще бъде 401 (не е удостоверено) или 403 (забранено).в зависимост от това дали проблемът е липса на идентификационни данни или недостатъчни разрешения.

Благодарение на този подход можете да защитите частните си крайни точки много лесно: просто трябва вмъкнете middleware като втори аргумент в дефиницията на маршрутаНапример, в маршрут /clients, който връща данни за клиенти, middleware-ът за удостоверяване може да бъде поставен като втори параметър, така че ако заявката не съдържа валиден токен, информацията да не се връща.

Ако се опитате да направите заявка към защитена крайна точка без токен, мидълуерът ще прекъсне потока и ще видите ясен отговор за грешка. Веднага щом включите валиден токен в заглавката, Същата крайна точка ще върне защитените даннидемонстриране, че контролът на достъпа работи правилно.

Същият този модел може да се екстраполира към повече нива на сигурност: можете да имате общ междинен софтуер, който проверява дали потребителят е удостоверен и на тази основа, други междинни софтуери за преглед на специфични роли, обхвати или разрешения преди да се позволят по-деликатни операции.

Контрол на достъпа, роли и най-добри практики за генериране на токени

След като основният процес на удостоверяване работи, следващата естествена стъпка е управлявайте роли и разрешения във вашия JWTЗа да направите това, можете да включите твърдения в полезния товар, като например роли (администратор, потребител и др.) или обхвати, които указват какви действия може да извършва потребителят.

Много е изкушаващо да се натъпче колкото се може повече информация в полезния товар, но е най-добре да се устои: JWT не е проектиран за пренос на големи обеми данни или чувствителна информация.Винаги, когато е възможно, ограничете съдържанието до необходимия минимум за идентифициране на потребителя и контекста на неговото оторизиране.

Изтичането на срока на годност е друг критичен аспект. Колкото по-кратък е животът на токена за достъп, толкова по-малък е прозорецът с възможности за атака, ако някой го открадне.Обичайната практика е да се използват токени за достъп с кратки срокове на валидност (например един или два часа) и да се управлява постоянството на сесията с токени за опресняване.

Токените за опресняване са вторични токени, обикновено с по-дълъг срок на годност и се съхранява много сигурноЗа предпочитане в httpOnly бисквитки с активиран флаг за сигурност. Функцията им е да позволят на клиента да получава нови токени за достъп, без да се налага многократно да изисква идентификационни данни от потребителя.

От гледна точка на сигурността, вашето приложение трябва да има механизми за обезсилване на токени в критични ситуациикато например промени в паролата, изрично затваряне на сесия или откриване на подозрителна активност. Тъй като JWT е без запазване на състоянието, няма списък със сесии на сървъра, така че трябва да се използват стратегии като черни списъци, кеширане или агресивно намаляване на времето за изтичане.

Никога не забравяйте, че Секретният ключ на JWT е един от най-чувствителните активи на системата.Тя винаги трябва да се съхранява в променливите на средата, достъпът до нея трябва да бъде ограничен и ротацията ѝ трябва да бъде планирана, ако има съмнение за нарушение. Ако някой получи тази тайна, той може да генерира валидни токени по желание.

Стратегии за отмяна, обновяване на токени и сигурно съхранение

Една от чувствителните точки на JWT е ранно отменяне на активни токениТъй като сървърът не съхранява състоянието за всеки токен, не е достатъчно просто да го изтриете от таблицата на сесиите, както би се случило при други традиционни решения.

Често използвана стратегия е поддържането черни списъци в паметта или в бърза база данни (напр. Redis)където съхранявате идентификаторите на токени, които са били отменени преди датата им на изтичане. Всеки път, когато пристигне удостоверена заявка, освен проверката на подписа, проверявате дали токенът не е в този списък. Тази стратегия е в съответствие с най-добрите практики на Управление на инциденти в облака с Azure за бизнес сценарии.

Друг вариант е да си играете с времето: можете да зададете много кратки токени за достъп И да приемем, че въпреки че не можете да ги отмените веднага, временното въздействие е значително намалено. Успоредно с това управлявате постоянството на сесията, използвайки токени за опресняване, които можете изрично да анулира в базата данни.

Що се отнася до съхранението от страна на клиента, най-препоръчителният сайт за обновяване на токена в уеб приложения е httpOnly бисквитка със защитени атрибути и в много случаи правилно конфигурирана sameSite бисквитка За да се намалят рисковете от XSS и CSRF, токенът за достъп може да се съхранява в паметта (например в състоянието на frontend приложението) и да се подновява, преди да изтече.

Не забравяйте да допълните това с Други мерки за сигурност: задължително използване на HTTPS, защитни заглавки (Content-Security-Policy, X-Frame-Options и др.), цялостна проверка на входните данни, контрол срещу CSRF при използване на бисквитки и одит на събития за удостоверяване (неуспешни опити за влизане, промени на парола, излизания и др.).

В корпоративна среда или мащабни проекти обикновено е препоръчително комбинирайте JWT с по-модерни системи за управление на идентичносттаПолитики за вътрешна сигурност и редовни тестове за киберсигурност, включително тестове за проникване и прегледи на архитектурата.

Практически пример: API с Node.js, Express и MongoDB

Ако обмисляте API, който е малко по-близо до производствената среда, обикновено ще комбинирате Node.js, Express, MongoDB и Mongoose за настройване на бекенд слоя. Потребителският модел е дефиниран със схема, която включва задължителни полета, и е добавен мидълуер за предварително запазване, който криптира паролата с bcrypt, преди да я запази.

В основния API файл, след свързване с MongoDB и конфигуриране на Express, са дефинирани следните параметри: маршрутите /register и /loginВ командата `/register`, бекендът получава потребителското име и паролата, създава потребителски екземпляр, изпълнява мидълуера за криптиране и запазва потребителя в базата данни. Ако всичко върви добре, той отговаря със статус код 201, което показва, че потребителят е създаден успешно.

В пътя /login, сървърът първо проверява дали потребителят съществува; след това валидира паролата, като я сравнява със съхранения хеш. Ако проверката е успешна, сървърът генерира токена, използвайки jwt.sign. потребителският идентификатор като основно твърдение (под-или идентификатор), секретният ключ, прочетен от process.env.JWT_SECRET, и конфигурирано време на изтичане.

Отговорът за вход обикновено включва основни потребителски данни и JWT токентака че клиентът да може да започне да използва защитените крайни точки. Използвайки инструменти като Postman или Insomnia, можете лесно да репликирате този поток: първо, извиквате /login, копирате върнатия токен и след това го изпращате в заглавката Authorization, когато правите заявки към частни маршрути като /clients.

Ако се опитате да промените токена ръчно, например като промените символите в полезния товар, за да си предоставите разрешения, които нямате, подписът вече няма да е валиден и мидълуерът за удостоверяване ще отхвърли заявката. Това демонстрира стойността на HMAC подписа и наличието на надеждна и добре защитена тайна.

В някои проекти, в допълнение към потребителските данни, функцията за знак се използва за въвеждане Допълнителни параметри, като например URI адреса на базата данни, случайни низове или специфични времена на изтичанеВсичко това е конфигурирано с помощта на променливи на средата и е заредено с dotenv, така че всяка среда (разработка, етапно управление, производство) използва свои собствени стойности.

Удостоверяване с Google и други доставчици на трети страни

В допълнение към класическото влизане с потребителско име и парола, много съвременни приложения позволяват Влезте с Google, Facebook или подобни акаунтиИдеята е да се използва система за удостоверяване на трета страна и оттам да се издаде ваш собствен JWT за вътрешна консумация от вашия API.

За да интегрирате Google, първата стъпка е да отидете в конзолата на Google APIs и създайте проектСлед като бъде създаден, активирате съответния API (преди Google+, сега Google Identity Services) и генерирате OAuth уеб идентификационни данни.

В този процес трябва да конфигурирате авторитетни източници на JavaScript (домейни, от които може да се инициира процесът на удостоверяване) и Упълномощени URI адреси за пренасочванеТоест, маршрутите във вашето приложение, към които Google пренасочва, след като потребителят се удостовери с акаунта си.

Когато потребител влезе в Google от фронтенда, клиентското ви приложение получава код за оторизация. Този код пътува до бекенда по специфичен маршрут, например /auth/google, където изготвяте POST заявка към крайната точка на Google, която обменя кодът за оторизация за токен за достъп.

В това обаждане до Google се изпращат параметри като получения код, client_id и client_secret, предоставени от Google, свързаният redirect_uri и grant_type със стойност authorization_code. Ако операцията е успешна, Google отговаря с токен за достъп, който ви позволява да заявите API на потребителския профил.

С този токен за достъп, бекендът отправя GET заявка към Google User API, предавайки токена за достъп в заглавката Authorization във формат Bearer. Ако няма грешки, Отговорът ще съдържа информацията за потребителския профил. (идентификатор, име, имейл, снимка и др.).

Оттам нататък логиката е подобна на всяка регистрация/вход: търсите в базата данни, за да видите дали има потребител с providerId, който Google ви дава; Ако вече съществува, просто генерирате JWT със собствените си данни и го връщате.Ако не съществува, създавате нов потребител с този providerId, имейл, име, снимка, запазвате го и след това издавате токена.

За да се създаде JWT токен в този контекст, е обичайно да се използва специфична услуга, например tokenService, която капсулира логиката на Създайте полезния товар, следвайки спецификацията (sub, iat, exp и други полета, като имейл, displayName или pic) и извикайте jwt.sign с конфигурирания алгоритъм и частния ключ.

По този начин, независимо дали потребителят е влязъл с Google, потребителско име и парола или друг доставчик, Вашият API винаги работи с един и същ JWT формат, което значително опростява оторизацията и контрола на достъпа на останалите крайни точки.

В крайна сметка, интегрирането на външни доставчици е чудесен начин за намаляване на триенето за потребителя и делегиране на част от удостоверяването на добре тествани системи, без да се жертва необходимостта от... ваш собствен JWT-базиран защитен слой в backend-а.

Овладяването на JWT в Node.js API включва задълбочено разбиране на вътрешната му структура, познаване на правилното подписване и проверка на токени, използване на middleware за удостоверяване, осигуряване на подходящи маршрути, комбинирането му с HTTPS, управление на тайни, срокове на валидност, обновяване на токени, отмяна на токени и, ако е необходимо, интегрирането му със социални входове като Google; с всичко това правилно имплементирано, имате солидна основа за изграждане на сигурни, мащабируеми услуги, готови да растат заедно с вашия проект.

Нулево доверие в Azure Active Directory
Свързана статия:
Нулево доверие в Azure Active Directory: какво е това, предимства и как да го внедрите