Tokens and sessions - практики

65 views
Skip to first unread message

Nikolay Georgiev

unread,
Feb 10, 2022, 5:26:32 PM2/10/22
to Bulgarian Java Users Group
Привет, колеги,

в практиката забелязвам често подход, в който при автентикация сървъра (API) генерира един JWT токен и го праща на клиента (web application). Клиента го запазва при себе си и го ползва като го изпраща заедно с всяка заявка към сървъра. Сървъра не пази токена никъде, но пък може да верифицира, че е валиден и да го ползва за да върне правилен резултат за клиента. Говоря за stateless security mechanism с JWT.

Най-силния аргумент на този подход е че улеснява хоризонталното скалиране.

Това, което не мога да си обясня е ОК ли е да се ползва за "сесия" въобще? Следните ресурси твърдят, че токените не трябва да се ползват за сесия:
Добрите праките, на които попадам трърдят, че токените трябва да са с кратък живот от порядъка на минути до часове. Това, на което попадам по различни проекти е, че се държат до ден, че дори и повече най-вече за да не "разлогва" потребителите често. Проблемът с това е, че няма начин да инвалидираме токен от сървъра ако потребителя е компрометиран.

Друг проблем, за който намерих много противоречиви мнения е къде в уеб приложение се пази въпросния токен. Изводът ми е, че е най-добре да се връща като secure, httpOnly Cookie от сървъра с активирана CSRF защита (която идва на готово с модерните frameworks). Контра аргумента на това е, че back-end API-a не би се ползвал от не-браузър клиенти, a също и че трябва допълнителна работа за да работи cross-origin.
Поради това често срещам този токен да се връща като резултат от API след автентикация и да се запазва в браузъра в Local Storage или Session Storage. Това излага клиента на XSS атаки защото всеки JavaScript в приложението може да достъпи тези хранилища. В защита на този подход освен, че API-а работи по-просто с всякакви типове клиенти срещнах и аргументи като "Ако има XSS значи нещата са в лошо състояние така или иначе" (така разбирам дискусията в GitHub issue tracker на JHipster - в техните проекти пазят токена в Session Storage). Още CSRF би могъл да се "излъже" ако вече има XSS.  

Моето мнение е, че ако нещо намаля attack vector-a дори с малко е добре да се направи защото би елиминирало някои лесни атаки. В практиката виждам, че компаниите не се впечатляват много от тези проблеми. Изглежда изпускам нещо в цялата картина защото мен ме притеснява възможността за XSS, въпреки че внимаваме какви CDN-и и JS библиотеки използваме в проектите.

От цялото четене по темата съм объркан за:
Този подход въобще трябва ли да се ползва и в какви случаи?
Ако се ползва, как е най-правилно да се съхранява от уеб приложението?

Какво сте видяли във вашата практика като решения на този проблем?

Disclaimer: Интересно ми е конкретно stateless механизъмът с токъни. Не искам да обсъждам sticky sessions или shared session storage.  

Martin Toshev

unread,
Feb 11, 2022, 3:02:12 AM2/11/22
to bg-...@googlegroups.com
  Здравей, Ники, 
  JWT token-ите са си един вид сесия, която се пази от страна на клиента (най-често под формата на cookie например, както е реализирано в Microsoft AD), та от тази гледна точка може да се използва, за да се пази допълнителнителна информация (под формата на custom claims), но според мен решението за това зависи дали тази информация е обвързана с token expiration-a (т.е. с атрибутите на потребителя например) или не. Много често сесията на страна на клиента/сървъра трябва да пази друга информация с различен период на валидност от тази на JWT token-a и от тази гледна точка дори не е правилно да се обвързва тази информация с JWT token-a. Също така и при данни, които не трябва да се пазят в сесия при клиента, ами на сървъра, тогава не е удачно да се ползва JWT също така.
   Също така по-добрата практика е да използваш външен auth server, които да издава и управлява JWT token-ите (а не в уеб приложението) и който е обвързан например с AD или база данни, което прави съхранението на допълнителнителни данни от сесията на приложението (необвързани с информацията за потребителя в AD-то или базата) неудобно.
   Поздрави,
   Марто


--
Получихте това съобщение, защото сте абонирани за групата „Bulgarian Java Users Group“ в Google Групи.
За да се отпишете от тази група и да престанете да получавате имейли от нея, изпратете имейл до bg-jug+un...@googlegroups.com.
За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/d4d7d534-8536-4e88-b4db-5e491109bbbdn%40googlegroups.com.

Nikolay Georgiev

unread,
Feb 14, 2022, 4:22:13 AM2/14/22
to Bulgarian Java Users Group
Благодаря, Марто!

Да, отделен auth сървър би вършил супер работа, но попадам на проекти, които сами се управляват токените и за екипите им това работи за сега.
Мен най-много ме притеснява, че front-end проектите към тези APIs пазят токена не в cookie (както даде пример с MS AD), ами в local storage (аргументите ми за и против в първия пост). Според теб това лоша практика ли е или няма значение защото ако си уязвим на XXS то и CSRF също е опция?

Поздрави,
Ники

Danail Alexiev

unread,
Feb 14, 2022, 7:45:19 AM2/14/22
to bg-...@googlegroups.com
Привет, Ники,

Включвам се и аз с моите 2 стотинки:

От това, което съм чел, наистина предпочитаният начин за запазване на access token за web е чрез http only cookie. local storage абсолютно не се препоръчва точно заради много по-лесната атака. Всичко това го казвам с уговорката, че web познанията ми далеч не са на ниво.

Мога да ти дам пример как работим с токени в мобилни приложения, в които сигурността е основен приоритет (иди обяснявай, че във всяко приложение това трябва да е така) - избягва се запазването на access token в какъвто и да е persistent storage. Най-често се пази в паметта, като дори се препоръчва да не се пази като String, а като char[], чието съдържание може да се "замаскира" с цел предпазване от някой, който ти направи dump на паметта докато приложението върви. При такъв сценарий на работа, кратката сесия не е проблем, тъй като е честа практика след период на неактивност потребителят да бъде каран да се re-authenticate-ва (справка - приложенията за мобилно банкиране). За да се предостави възможно най-добро изживяване на потребителя, често се работи и с refresh token. Той е long lived и се пази в persistent storage с нужното ниво на защита - криптиране, като ключът се пази харуерно-защитената памет (най-често се изисква биометрия или някакъв друг код, за да се отключи). При 401 от сървъра заявката се прихваща, изпраща се заявка за нов access token на база refresh token, полученият access token се запазва в паметта и се продължава работа. В зависимост от избрания подход, това може да се случи с или без участието на потребителя.

С лимитираните ми web познания не мога да докарам аналогия на този workflow, но, ако ти послужи за основа, ще се радвам. 

Поздрави,
Даката

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/ba2ef27a-9eaa-4556-a8cf-827340b3e8b1n%40googlegroups.com.

Nikolay Georgiev

unread,
Feb 14, 2022, 10:49:47 AM2/14/22
to Bulgarian Java Users Group
Привет, Дака,

Да, полезно ми е да ми отвориш очите как става при мобилните, защото с това не съм се занимавал. В уеб съм правил подхода с токен и refresh tokens по много сходен начин - токен в паметта, а refresh token в secure, http only cookie, което също се обвързва с конкретен path (пр. /auth/token), за да се праща само където трябва.
И ти каза, че не се препоръчва local storage, но проекти като JHipster там го пазят (всъщност session storage, но все едно). Аз затова се чудя струва ли си допълнителното усилие с бисквитките?
Казвам "допълнително усилие" защото се настройва и CORS по особен начин и се губи гъвкавост - по трудно front-end екипите могат да вържат локалните си приложения към динамични back-end API среди да речем. Поне това е моя опит.

Поздрави,
Ники

Danail Alexiev

unread,
Feb 14, 2022, 11:03:09 AM2/14/22
to bg-...@googlegroups.com
Йезуитският отговор тук е "зависи" :)

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

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/4a1a236f-0d3f-421b-a178-1182662a09a0n%40googlegroups.com.

Martin Toshev

unread,
Feb 14, 2022, 11:39:20 AM2/14/22
to bg-...@googlegroups.com
    Често казано доколко е при клиента не виждам особено значение дали е в cookie или local storage, тъй като и при двата варианта трябва да се подаде към сървъра под една или друга форма (i.e. с header), така че от тази гледна точка може би няма голямо значение ... 

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/CAJ2LTs-_a%3D2YKNack5MhaWp0v1tkbwuQ_PzCjNDF7S4iZ2tSrg%40mail.gmail.com.

Plamen Stefanov

unread,
Feb 15, 2022, 2:46:50 AM2/15/22
to Bulgarian Java Users Group
Дали токена ще се пази в куки, сторидж или в паметта на JS енджина разликите, за мен, са притеснително големи.
- куки - браузъра го праща на всеки, т.е. трябва да се пазиш и от CSRF, кето пак става с токен, който задължително не трябва да е в куки. Ако не ползваш куки си спестяваш CSRF токена.
- сторидж - по-добър вариант, но пак браузъра го пази при себе си и идваме на момента как бразърите ги менаджират тези сториджи. За мен е по-приемлив вариант, защото имам повече доверие на браузъра, но все пак.
- паметта на JS енджина - супер решение,  но изисква начална заявка за получаване на токен (ака логин). JS ендижна, макар и в браузър, е клиента който говори с въпросното рест апи.

Както става ясно - все хакове да ползваме JWT за сесия.... а то милото е направено да си го получи някакъв клиент и да си прави извиквания към рест апи както си иска.

За мен отговора е ясен - за най-простия случай, в който клиент пази токен и вика АПИ без други сървъри и усложнения JWT върши чудесна работа.
За всички други, вкл сесия - OpenID Connect/OAuth2.

Поздрави

Martin Toshev

unread,
Feb 15, 2022, 3:42:53 AM2/15/22
to bg-...@googlegroups.com
   Здравей, Пламене,
   Това, което си написал е невярно и объркващо, ще подчертая само две очевадно грешни твърдения:
    "куки - браузъра го праща на всеки"
    "паметта на JS енджина - супер решение" - и какво става като затвориш браузъра ? 
   Поздрави,
   Марто


За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/5c98205d-a176-4859-b189-3e71398d40c8n%40googlegroups.com.

Nayden Gochev

unread,
Feb 15, 2022, 4:06:33 AM2/15/22
to bg-...@googlegroups.com

Здравейте

Ще добавя 2 цента 😊

localStorage/SessionStorage e vulnerable на XSS с други думи всякакъв JavaScript, който лоаднеш в твоя апп моеж да достъпи твоя jwt token.

 

Така, че за JWT и подобни токъни се ползва cookie :+) край.. :+))

А колкото до jHipster те са французи :Д


Ако намеря статията имаше много готина по темата ще я пусна.

 

 

 

Nayden Gochev

Software Solutions Architect

AyataCommerce

m:

+359 883 486 214

a:

Sofia, Bulgaria

w:

https://dev.to/gochev                                              

e:

goc...@gmail.com                                             

За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/CAJQxrXCKqXLT85aSKNFM2NoQGmdE3Mw%3DEurQu8Yy9Oz5o5iK_w%40mail.gmail.com.

 

Nayden Gochev

unread,
Feb 15, 2022, 4:12:09 AM2/15/22
to bg-...@googlegroups.com

А май беше ей тази статия

 

https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

 

като гледам е малко стара , мисля, че бях виждал и някаква по нова… ама може и да съм попаднал просто на старата преди 1-2 години Д:Д:

Nikolay Georgiev

unread,
Feb 15, 2022, 8:16:54 AM2/15/22
to Bulgarian Java Users Group
Благодаря за стотинките от всеки, беше ми полезно!

Поздрави,
Ники

Plamen Stefanov

unread,
Feb 16, 2022, 8:01:35 AM2/16/22
to Bulgarian Java Users Group
Ех Марто,

Думите ми си имат и малко контекст:
- "куки - браузъра го праща на всеки" => на всеки рекуест (тази думичка съм я изтирвал в бързината, но се подразбира), та за това се появява и възможност за CSRF атака
- "паметта на JS енджина - супер решение - и какво става като затвориш браузъра ?" =>  При следващото пускане  е както си пише "изисква начална заявка за получаване на токен (ака логин)". 
Същото става и като си рестартираш клиента, с който говориш с АПИ-то - напр. Java програма, Node JS приложение или ако ти изтече валидността на токена.

Сигурен съм, че ми разбра мисълта. Няма нужда да си съгласен. Говорим си. :)

Поздрави
Пламен

Martin Toshev

unread,
Feb 16, 2022, 9:36:30 AM2/16/22
to bg-...@googlegroups.com
    Да, така става по-ясно, благодаря за разяснението. При варианта с cookie има варианти да се избегне CSRF (напр. да се използва sameSite=strict, ако е приложимо). При in-memory варианта, които описваш по-скоро се има предвид, че все пак е необходимо да се запази в cookie refresh token, вместо JWT (за да може все пак да се вземе JWT token при затваряне на сесията). И при този вариант зависи пак дали приложението не е уязвимо към XSS атаки, така че да се достъпи JWT token-a от паметта. Но да - прав си, че има разлики (доколко са значими зависи разбира се от естеството на приложението).
   Поздрави,
   Марто



За да видите тази дискусия в мрежата, посетете https://groups.google.com/d/msgid/bg-jug/8e6eb303-cdb1-4e59-a23e-590ce49cadcdn%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages