Най-добри практики за обработка на JWT токени от страна на сървъра [затворено]

(породено от тази тема, тъй като това наистина е въпрос сам по себе си и не е специфичен за NodeJS и т.н.)

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

Въпреки това се опитвам да разбера най-добрите практики за това как точно и до каква степен токенът трябва да бъде валидиран, за да направя една наистина сигурна система. Какво точно трябва да бъде включено в "валидирането" на токена? Достатъчно ли е, че подписът може да бъде проверен с помощта на тайната на сървъра, или трябва също така да проверя кръстосано токена и/или токена с някои данни, съхранени в сървъра?

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

Това ме навежда на въпросите:

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

  • В някои случаи съм виждал комбинирано използване на токени и сървърни сесии, при които при успешно влизане през крайната точка /login се установява сесия. API заявките валидират токена и също така сравняват декодираните данни, намерени в токена, с някои данни, съхранени в сесията. Използването на сесии обаче означава използване на бисквитки и в известен смисъл проваля целта на използването на подход, базиран на токени. Това също може да създаде проблеми за определени клиенти.

  • Човек може да си представи, че сървърът съхранява всички токени, използвани в момента, в memcache или подобен, за да гарантира, че дори ако тайната на сървъра е компрометирана, така че нападателят да може да произведе „валидни“ токени, само точните токени, които са били генерирани чрез крайната точка /login ще бъдат приети. Това разумно ли е или просто е излишно/прекалено?

2) Ако проверката на подписа на JWT е единственото средство за валидиране на токени, което означава, че целостта на тайната на сървъра е критичната точка, как трябва да се управляват тайните на сървъра? Прочетено от променлива на средата и създадено (рандомизирано?) веднъж на разгърнат стек? Подновява се или се ротира периодично (и ако е така, как да се справят със съществуващите валидни токени, които са създадени преди ротация, но трябва да бъдат валидирани след ротация, може би е достатъчно, ако сървърът държи текущата и предишната тайна във всеки даден момент) ? Нещо друго?

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


person JHH    schedule 29.05.2015    source източник
comment
Има страхотни въпроси. Re: въпрос 2. Имам същия проблем с ВСЕКИ секретен ключ, който се съхранява от страна на сървъра. Ако извършвате някакъв вид хеш съвпадение или асиметрично декриптиране, независимо дали това е подписване на jwt или декриптиране на cc информация, съхранена в db, трябва да имате таен ключ, достъпен чрез код на сървъра. И така, къде, по дяволите, го държиш?? Ето най-добрия отговор, който намерих: pcinetwork.org/forum/index.php?threads/ -- вероятно толкова сигурно, колкото и за jwt ключ.   -  person jbd    schedule 22.01.2016
comment
Какво е таен ключ в jwt token? Мисля, че самият токен jwt е тайна. Или тайният ключ може да бъде RSAPrivateKey privateKey ??   -  person kittu    schedule 26.02.2016
comment
Това беше зададено преди малко, но може би на някой ще му е полезно. В моя случай имам таен ключ за всеки потребител. Така че всеки път, когато потребител влезе, аз генерирам тази тайна и я съхранявам с потребителския запис в DB. Потвърждавам токена, използвайки тази тайна. При излизане изчиствам тази стойност. Това автоматично прави невалидни други токени, създадени преди (това ми трябваше).   -  person Nelson Rodriguez    schedule 06.10.2017


Отговори (5)


Играя и с токени за моето приложение. Въпреки че не съм експерт по никакъв начин, мога да споделя някои от моя опит и мисли по въпроса.

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

Що се отнася до полезния товар, подписът ще ви каже само, че предоставеният ви токен е бил точно такъв, какъвто е бил, когато вашият сървър го е изпратил. проверката дали съдържанието на полезния товар е валидно или подходящо за вашето приложение, очевидно зависи от вас.

За вашите въпроси:

1.) Съгласно моя ограничен опит, определено е по-добре да проверите своите токени с втора система. Простото валидиране на подписа просто означава, че токенът е генериран с вашата тайна. Съхраняването на всички създадени токени в някакъв вид DB (redis, memcache/sql/mongo или някакво друго хранилище) е фантастичен начин да се гарантира, че приемате само токени, които вашият сървър е създал. При този сценарий, дори ако вашата тайна изтече, това няма да има голямо значение, тъй като всички генерирани токени така или иначе няма да бъдат валидни. Това е подходът, който използвам с моята система - всички генерирани токени се съхраняват в DB (redis) и при всяка заявка проверявам, че токенът е в моята DB, преди да го приема. По този начин токените могат да бъдат отменени по каквато и да е причина, като токени, които са били пуснати в дивата природа по някакъв начин, излизане на потребителя, промени на парола, тайни промени и т.н.

2.) Това е нещо, в което нямам много опит и е нещо, което все още активно проучвам, тъй като не съм специалист по сигурността. Ако намерите някакви ресурси, не се колебайте да ги публикувате тук! В момента използвам само частен ключ, който зареждам от диска, но очевидно това далеч не е най-доброто или най-сигурното решение.

person Akshay Dhalwala    schedule 17.06.2015
comment
За втората точка има добър отговор: security.stackexchange.com/questions/87130/ - person Bossliaw; 08.10.2015
comment
Тъй като токените са налични в заглавката, какво ще стане, ако токенът бъде откраднат и злонамерен се опита да влезе с този токен (като знае имейл адреса на потребителя)? - person kittu; 27.02.2016
comment
Ако съхранявате всеки JWT, тогава няма полза от JWT и можете да се придържате към произволни идентификатори на сесии. - person ColinM; 26.11.2017

Ето някои неща, които трябва да имате предвид, когато внедрявате JWT във вашето приложение:

  • Поддържайте живота на JWT относително кратък и го управлявайте на сървъра. Ако не го направите и по-късно трябва да изискате повече информация във вашите JWT, ще трябва или да поддържате 2 версии, или да изчакате, докато по-старите ви JWT изтекат, преди да можете да приложите промяната си. Можете лесно да го управлявате на сървъра, ако гледате само полето iat в jwt и игнорирате полето exp.

  • Помислете за включване на URL адреса на заявката във вашия JWT. Например, ако искате вашият JWT да се използва в крайна точка /my/test/path, включете поле като 'url':'/my/test/path' във вашия JWT, за да сте сигурни, че то се използва само по този път. Ако не го направите, може да откриете, че хората започват да използват вашите JWT в други крайни точки, дори такива, за които не са създадени. Можете също така да помислите за включване на md5(url) вместо това, тъй като наличието на голям url в JWT в крайна сметка ще направи JWT много по-голям и те могат да станат доста големи.

  • Изтичането на JWT трябва да може да се конфигурира от всеки случай на употреба, ако JWT се внедряват в API. Например, ако имате 10 крайни точки за 10 различни случая на употреба за JWT, уверете се, че можете да накарате всяка крайна точка да приема JWT, които изтичат по различно време. Това ви позволява да блокирате някои крайни точки повече от други, ако например данните, обслужвани от една крайна точка, са много чувствителни.

  • Вместо просто да изтичате JWT след определено време, помислете за прилагане на JWT, които поддържат и двете:

    • N usages - can only be used N times before they expire and
    • изтичат след определен период от време (ако имате токен само за еднократна употреба, не искате той да живее вечно, ако не се използва, нали?)
  • Всички неуспешни JWT удостоверявания трябва да генерират заглавка на отговор "грешка", която посочва защо JWT удостоверяването е неуспешно. напр. „изтекъл“, „без останали употреби“, „отменен“ и т.н. Това помага на изпълнителите да разберат защо техният JWT се проваля.

  • Помислете за игнориране на „заглавката“ на вашите JWT, тъй като те изпускат информация и дават известна степен на контрол на хакерите. Това се отнася най-вече за полето alg в заглавката - игнорирайте това и просто приемете, че заглавката е това, което искате да поддържате, тъй като това избягва хакерите да се опитват да използват алгоритъма None, който премахва проверката за сигурност на подписа.

  • JWT трябва да включва идентификатор, уточняващ кое приложение е генерирало токена. Например, ако вашите JWT се създават от 2 различни клиента, mychat и myclassifiedsapp, тогава всеки трябва да включва името на проекта си или нещо подобно в полето "iss" в JWT, напр. "iss": "mychat"

  • JWT не трябва да се записват в лог файлове. Съдържанието на JWT може да бъде регистрирано, но не и самият JWT. Това гарантира, че разработчиците или други не могат да вземат JWT от лог файлове и да правят неща с други потребителски акаунти.
  • Уверете се, че внедряването на JWT не позволява алгоритъма „Няма“, за да избегнете хакерите да създават токени, без да ги подписват. Този клас грешки могат да бъдат избегнати изцяло чрез игнориране на "заглавката" на вашия JWT.
  • Силно обмислете използването на iat (издадено на) вместо exp (изтичане) във вашите JWT. Защо? Тъй като iat основно означава кога е създаден JWT, това ви позволява да коригирате на сървъра кога изтича JWT въз основа на датата на създаване. Ако някой премине в exp, което е 20 години в бъдещето, JWT всъщност живее вечно! Обърнете внимание, че вие ​​автоматично изтичате JWT, ако техният iat е в бъдещето, но оставете малко място за мърдане (напр. 10 секунди), в случай че времето на клиента леко се разминава с времето на сървъра.
  • Помислете за внедряване на крайна точка за създаване на JWT от json полезен товар и принудете всичките си внедряващи клиенти да използват тази крайна точка за създаване на своите JWT. Това гарантира, че можете лесно да адресирате всички проблеми със сигурността, които желаете, с начина, по който се създават JWT на едно място. Не направихме това веднага в нашето приложение и сега трябва бавно да внедряваме актуализации за сигурност от страна на сървъра на JWT, защото нашите 5 различни клиента се нуждаят от време за внедряване. Също така, накарайте вашата крайна точка за създаване да приеме масив от json полезни натоварвания за създаване на JWT и това ще намали броя http заявки, идващи до тази крайна точка за вашите клиенти.
  • Ако вашите JWT ще бъдат използвани в крайни точки, които също поддържат използване по сесия, уверете се, че не поставяте нищо във вашия JWT, което е необходимо за удовлетворяване на заявката. Можете лесно да направите това, ако гарантирате, че вашата крайна точка работи със сесия, когато не е предоставен JWT.
  • Така че JWT най-общо казано в крайна сметка съдържа потребителски идентификатор или групов идентификатор от някакъв вид и позволява достъп до част от вашата система въз основа на тази информация. Уверете се, че не позволявате на потребителите в една област на вашето приложение да се представят за други потребители, особено ако това осигурява достъп до чувствителни данни. Защо? Е, дори ако вашият процес на генериране на JWT е достъпен само за "вътрешни" услуги, разработчиците или други вътрешни екипи могат да генерират JWT за достъп до данни за всеки потребител, напр. главен изпълнителен директор на компания на произволен клиент. Например, ако вашето приложение предоставя достъп до финансови записи за клиенти, тогава чрез генериране на JWT, разработчикът може да вземе финансовите записи на всяка компания изобщо! И ако хакер все пак влезе във вашата вътрешна мрежа, той може да направи същото.
  • Ако възнамерявате да разрешите който и да е URL адрес, който съдържа JWT, да бъде кеширан по някакъв начин, уверете се, че разрешенията за различни потребители са включени в URL адреса, а не в JWT. Защо? Тъй като потребителите може да получат данни, които не трябва. Например, кажете, че суперпотребител влиза в приложението ви и изисква следния URL адрес: /mysite/userInfo?jwt=XXX и този URL адрес се кешира. Те излизат и няколко минути по-късно обикновен потребител влиза в приложението ви. Те ще получат кешираното съдържание - с информация за супер потребител! Това обикновено се случва по-малко на клиента и повече на сървъра, особено в случаите, когато използвате CDN като Akamai и оставяте някои файлове да живеят по-дълго. Това може да бъде коригирано чрез включване на съответната информация за потребителя в URL адреса и валидиране на това на сървъра, дори за кеширани заявки, например /mysite/userInfo?id=52&jwt=XXX
  • Ако вашият jwt е предназначен да се използва като бисквитка за сесия и трябва да работи само на същата машина, за която е създаден jwt, трябва да обмислите добавянето на jti поле към вашия jwt. Това е основно CSRF токен, който гарантира, че вашият JWT не може да бъде прехвърлен от браузъра на един потребител на друг.
person Brad Parks    schedule 29.05.2017
comment
Това, което наричате created_by, вече има иск за това в JWT и се нарича iss (издател). - person Fred; 03.10.2017
comment
да, добра точка - ще актуализирам с това... благодаря! - person Brad Parks; 03.10.2017
comment
Първата точка е полезна, ако трябва да поставите често актуализирани данни в полетата на полезния товар на JWT във вашите случаи на употреба (така че ще трябва редовно да опреснявате токена). Мисля, че краткотрайният JWT просто го прави по-сигурен. - person Han; 05.02.2021

Не мисля, че съм експерт, но бих искал да споделя някои мисли относно Jwt.

  • 1: Както каза Akshay, по-добре е да имате втора система за валидиране на вашето означение.

    a.: Начинът, по който се справям: съхранявам генерирания хеш в хранилище за сесия с времето на изтичане. За да потвърдите токен, той трябва да е издаден от сървъра.

    b.: Има поне едно нещо, което трябва да се провери използвания метод на подписване. например:

    header :
    {
      "alg": "none",
      "typ": "JWT"
    }
    

Някои библиотеки, валидиращи JWT, биха приели това, без да проверяват хеша. Това означава, че без да знае вашата сол, използвана за подписване на токена, хакер може да си даде някои права. Винаги се уверявайте, че това не може да се случи. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

c.: Използването на бисквитка с идентификатор на сесия не би било полезно за валидиране на вашия маркер. Ако някой иска да отвлече сесията на ламбда потребител, той просто ще трябва да използва снифър (напр.: wireshark). Този хакер би имал и двете информации едновременно.

  • 2: Същото е за всяка тайна. Винаги има начин да го разберете.

Начинът, по който се справям, е свързан с точка 1.а. : Имам тайна, смесена със случайна променлива. Тайната е уникална за всеки жетон.

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

Ако искате възможно най-добрата сигурност, не бива да следвате сляпо най-добрите практики. Най-добрият начин е да разберете какво правите (мисля, че е добре, когато видя въпроса ви), и след това да прецените сигурността, от която се нуждаете. И ако Мосад иска да има достъп до вашите поверителни данни, те винаги ще намерят начин. (Харесвам тази публикация в блога: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )

person Deblaton Jean-Philippe    schedule 02.10.2015
comment
Добра идея за наличието на уникална тайна за всеки токен, но как да създавате уникална тайна всеки път? Използвам nimbus jwt библиотека - person kittu; 27.02.2016
comment
вероятно използвайте хеш паролата на потребителя си. - person momokjaaaaa; 20.04.2016
comment
Ако не правите нещата по същия начин, както го правят другите хора, ще бъде по-трудно за хората да намерят път през вашата сигурност. Това ми звучи като Security Through Obscurity. Най-добрите практики се наричат ​​така, защото смекчават най-често срещаните рискове по практически начин. - person Mnebuerquo; 05.06.2017
comment
@Mnebuerquo Напълно съм съгласен с теб, не трябва да се вярва на човека, който го е написал ;-) - person Deblaton Jean-Philippe; 06.06.2017
comment
Той обаче е прав, че човек не трябва сляпо да следва най-добрите практики. Добре е да разберете защо най-добрите практики се считат за най-добри. Във всяко решение за дизайн на сигурността има компромис между сигурност и използваемост. Разбирането на защо означава, че можете да вземате тези решения интелигентно. (Продължавайте обаче да следвате най-добрите практики, защото вашите потребители няма да го направят.) - person Mnebuerquo; 06.06.2017
comment
@Mnebuerquo Аз бях този, който написа този отговор, който актуализирах след вашия коментар. Сигурно имах нужда от още кафе, докато го писах преди 2 години ;-) - person Deblaton Jean-Philippe; 06.06.2017

Тук има много добри отговори. Ще интегрирам някои от отговорите, които смятам за най-подходящи, и ще добавя още няколко предложения.

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

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

Други мерки за сигурност включват нерегистриране на JWT и изискване на защитен алгоритъм за подписване като SHA256.

2) Ако проверката на подписа на JWT е единственото средство за валидиране на токени, което означава, че целостта на тайната на сървъра е критичната точка, как трябва да се управляват тайните на сървъра?

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

Както казва Akshay Dhalwala, ако вашата тайна от страна на сървъра е компрометирана, тогава имате по-големи проблеми, с които да се справите, защото това означава, че нападател е компрометирал вътрешната ви мрежа, хранилището ви за изходен код или и двете.

Въпреки това, система за смекчаване на щетите от компрометирана тайна на сървъра и избягване на съхраняването на тайни в изходния код включва ротация на секретни токени с помощта на услуга за координиране като https://zookeeper.apache.org. Използвайте задание на cron, за да генерирате тайна на приложение на всеки няколко часа или така (колкото и време да са валидни вашите токени за достъп) и изпратете актуализираната тайна към Zookeeper. Във всеки сървър на приложения, който трябва да знае тайната на токена, конфигурирайте ZK клиент, който се актуализира всеки път, когато стойността на ZK възела се промени. Съхранявайте първична и вторична тайна и всеки път, когато тайната на токена се променя, задавайте новата тайна на токена на основната и старата тайна на токена на вторичната. По този начин съществуващите валидни токени ще продължат да бъдат валидни, защото ще бъдат валидирани спрямо вторичната тайна. Докато вторичната тайна бъде заменена със старата основна тайна, всички токени за достъп, издадени с вторичната тайна, така или иначе ще изтекат.

person skeller88    schedule 15.07.2017

IETF има RFC в ход в работната група oAuth, вижте: https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html

person SPoint    schedule 23.05.2019