Найкращі практики поводження з JWT жетонами на стороні сервера [закрито]


111

(породжена з цієї теми, оскільки це справді питання власне і не характерне для NodeJS тощо)

Я реалізую сервер API REST з аутентифікацією, і я успішно реалізував обробку токенів JWT, щоб користувач міг увійти через кінцеву точку / входу з ім'ям користувача / паролем, після чого маркер JWT генерується з секрету сервера і повертається до клієнт. Потім маркер передається від клієнта до сервера в кожному аутентифікованому запиті API, після чого секрет сервера використовується для перевірки маркера.

Однак я намагаюся зрозуміти кращі практики для того, як саме і в якій мірі маркер повинен бути затверджений, щоб зробити справді безпечну систему. Що саме має бути залучено до "перевірки" жетону? Чи достатньо, щоб підпис можна було перевірити за допомогою секрету сервера, чи я також повинен перехресно перевірити маркер та / або корисний набір токена на деякі дані, що зберігаються на сервері?

Система аутентифікації на основі лексеми буде настільки ж безпечною, як передача імені користувача / пароля в кожному запиті, за умови, що отримати маркер так само або складніше, ніж отримати пароль користувача. Однак у наведених нами прикладах єдина інформація, необхідна для створення маркера, - це ім'я користувача та секрет на стороні сервера. Чи це не означає, що якщо припустити, що зловмисний користувач здобуває знання серверної таємниці, він тепер може виробляти жетони від імені будь-якого користувача, тим самим маючи доступ не лише до одного даного користувача, як це було б фактом, якби пароль був отримані, але насправді для всіх облікових записів користувачів?

Це підводить мене до питань:

1) Чи слід обмежувати перевірку токена JWT лише перевіркою підпису самого маркера, покладаючись лише на цілісність секрету сервера, або супроводжуватися окремим механізмом перевірки?

  • У деяких випадках я бачив комбіноване використання жетонів і сеансів на сервері, де при успішному вході через / логін вхід встановлюється сеанс. API запитів перевіряє маркер, а також порівнює декодовані дані, знайдені в маркері, з деякими даними, що зберігаються в сеансі. Однак використовувати сеанси означає використовувати файли cookie, і в деякому сенсі це перемагає мету використання підходу на основі лексеми. Це також може спричинити проблеми для певних клієнтів.

  • Можна було б уявити, як сервер зберігає всі маркери, які зараз використовуються, у мемаш-пам'яті чи подібних, щоб гарантувати, що навіть якщо секрет сервера порушений, щоб зловмисник міг видавати "дійсні" жетони, лише ті токени, які були згенеровані через кінцеву точку / вхід буде прийнято. Це розумно чи просто зайве / зайве?

2) Якщо перевірка підпису JWT є єдиним засобом перевірки маркерів, тобто цілісність секрету сервера є точкою розриву, як слід керувати секретами сервера? Читання із змінної середовища та створене (рандомізоване?) Раз на розгорнутий стек? Періодично повторно нові або повертаються (і якщо так, як поводитися з існуючими дійсними маркерами, які були створені перед обертанням, але їх потрібно перевірити після обертання, можливо, цього достатньо, якщо сервер тримається за поточний та попередній секрет у будь-який момент часу) ? Щось ще?

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


1
Є великі запитання. Відповідь: питання 2. У мене та сама проблема з будь-якими секретними ключами, які зберігаються на стороні сервера. Якщо ви виконуєте будь-які хеш-збіги або асиметричне розшифрування, - чи це підписання jwt, або розшифровка інформації про cc, що зберігається в db, у вас повинен бути секретний ключ, доступний за кодом на сервері. Так де, чорт, ти це тримаєш ?? Ось найкраща відповідь, яку я знайшов: pcinetwork.org/forum/index.php?threads/… - можливо, настільки ж безпечний, як і для клавіші jwt.
jbd

Що таке секретний ключ у маркері jwt? Я думаю, що jwt став собі секретом. Або секретний ключ міг бути RSAPrivateKey privateKey??
kittu

3
Це запитували деякий час тому, але, можливо, хтось вважатиме це корисним. У моєму випадку у мене є "секретний ключ" на кожного користувача. Тому щоразу, коли користувач входить, я генерую цей секрет і зберігаю з записом користувача в БД. Я підтверджую маркер, використовуючи цей секрет. Після виходу я знімаю це значення. Це автоматично визнає недійсними інші створені раніше жетони (саме це мені і потрібно).
Нельсон Родрігес

Відповіді:


52

Я також граю з жетонами для моєї програми. Хоча я не є експертом жодним чином, я можу поділитися деяким досвідом та думками з цього питання.

Суть JWT - це по суті цілісність. Він надає механізм для того, щоб ваш сервер підтвердив, що маркер, який йому був наданий, справжній та наданий вашим сервером. Підпис, згенерований за допомогою Вашого секрету, - це те, що передбачає це. Так, так, якщо ваша секрет якось просочився, ця людина може генерувати жетони, які ваш сервер вважає за власні. Система на базі токенів все-таки буде більш безпечною, ніж ваше ім’я користувача / пароль просто через перевірку підпису. І в цьому випадку, якщо хтось у вас є секретом, у вашій системі є інші проблеми безпеки, ніж хтось робить підроблені жетони (і навіть тоді, лише зміна секрету гарантує, що будь-які жетони, зроблені зі старим секретом, тепер недійсні).

Що стосується корисного навантаження, то підпис підкаже лише вам, що наданий вам маркер був таким самим, як був, коли ваш сервер його розіслав. очевидно, переконайтеся, що вміст корисних навантажень дійсний або відповідний вашій заявці.

Для ваших питань:

1.) За моїм обмеженим досвідом, безумовно, краще перевірити свої жетони за допомогою другої системи. Проста перевірка підпису просто означає, що маркер створений із вашим секретом. Збереження будь-яких створених жетонів у якомусь БД (redis, memcache / sql / mongo або інший сховище) - це фантастичний спосіб гарантувати, що ви приймаєте лише маркери, створені вашим сервером. У цьому випадку, навіть якщо ваш секрет просочився, це не матиме великого значення, оскільки будь-які створені маркери не будуть дійсними. Це такий підхід, який я використовую зі своєю системою - всі генеровані маркери зберігаються в БД (redis), і під час кожного запиту я перевіряю, що маркер є в моїй БД, перш ніж я прийму його. Таким чином, маркери можуть бути відкликані з будь-якої причини, наприклад, лексеми, які якось були випущені в дику природу, вихід користувача, зміна пароля, секретні зміни тощо.

2.) Це те, що я не маю багато досвіду, і це те, що я все ще активно досліджую, оскільки я не фахівець із безпеки. Якщо ви знайдете будь-які ресурси, сміливо розміщуйте їх тут! Наразі я просто використовую приватний ключ, який завантажую з диска, але, очевидно, це далеко не найкраще чи найбезпечніше рішення.


5
Для другого пункту тут хороша відповідь: security.stackexchange.com/questions/87130/…
Bossliaw

1
Оскільки маркери доступні в заголовку, що робити, якщо маркер викрадений і зловмисна спроба ввійти в систему з цим маркером (усвідомлюючи електронну адресу користувача)?
kittu

22
Якщо ви зберігаєте кожен JWT, тоді JWT не має ніякої користі, і ви можете також дотримуватися випадкових ідентифікаторів сеансу.
ColinM

46

Ось декілька речей, які слід врахувати при впровадженні 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 звичаїв - можна використовувати лише N разів до їх закінчення та
    • закінчується через певний час (якщо у вас є лише один маркер використання, ви не хочете, щоб він жив вічно, якщо він не використовується, чи не так?)
  • Усі збої аутентифікації JWT повинні генерувати заголовок відповіді "помилки", який зазначає, чому JWT аутентифікація не вдалася. наприклад, "минув термін дії", "не залишилось жодного користування", "відкликано" і т. д. Це допомагає виконавцям знати, чому їх JWT не працює.

  • Подумайте про ігнорування "заголовка" своїх JWT, оскільки вони просочуються інформацією та дають міру контролю хакерам. В основному це стосується algполя в заголовку - ігноруйте це і просто припускайте, що заголовок - це те, що ви хочете підтримати, оскільки це дозволяє уникнути хакерів, які намагаються використовувати Noneалгоритм, який знімає перевірку безпеки підпису.

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

  • JWT's не повинні реєструватися у файлах журналів. Вміст JWT може бути записаний, але не сам JWT. Це гарантує, що розробники чи інші не можуть захоплювати JWT з файлів журналів і робити речі в акаунтах інших користувачів.
  • Переконайтеся, що ваша реалізація JWT не дозволяє алгоритму "None", щоб уникнути хакерів, що створюють жетони, не підписуючи їх. Цього класу помилок можна повністю уникнути, ігноруючи "заголовок" вашого 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 в кінцевому підсумку містить якийсь userId або groupId, і дозволяють отримати доступ до частини вашої системи на основі цієї інформації. Переконайтеся, що ви не дозволяєте користувачам в одній області вашої програми видавати себе за інших, особливо якщо це забезпечує доступ до конфіденційних даних. Чому? Добре, навіть якщо ваш процес генерації JWT доступний лише для "внутрішніх" служб, розробники та інші внутрішні команди можуть генерувати JWT для доступу до даних для будь-якого користувача, наприклад, генерального директора компанії якогось випадкового клієнта. Наприклад, якщо ваш додаток забезпечує доступ до фінансових записів для клієнтів, то, створивши JWT, розробник може взагалі захопити фінансові записи будь-якої компанії! І якщо хакер все-таки потрапить у вашу внутрішню мережу, вони могли б зробити те саме.
  • Якщо ви збираєтесь дозволити будь-який URL-адрес, що містить JWT, кешуватися будь-яким чином, переконайтеся, що дозволи для різних користувачів включені в URL-адресу, а не JWT. Чому? Оскільки користувачі можуть отримати дані, які вони не повинні. Наприклад, скажіть, що супер користувач увійде у вашу програму та запитує таку URL-адресу:, /mysite/userInfo?jwt=XXXі що ця URL-адреса буде кешована. Вони виходять і через пару хвилин звичайний користувач входить у ваш додаток. Вони отримають кешований вміст - з інформацією про суперкористувача! Це зазвичай трапляється менше на клієнті, а більше на сервері, особливо у випадках, коли ви використовуєте CDN, наприклад Akamai, і ви дозволяєте деяким файлам жити довше. Це можна виправити, включивши відповідну інформацію про користувача в URL-адресу та перевіривши це на сервері, навіть для кешованих запитів, наприклад/mysite/userInfo?id=52&jwt=XXX
  • Якщо ваш jwt призначений для використання, як сесійне cookie, і повинен працювати лише на тій же машині, для якої jwt був створений, вам слід розглянути можливість додавання поля jti до вашого jwt. Це, в основному, маркер CSRF, який гарантує, що ваш JWT не може бути переданий з браузера одного користувача в інший.

1
Те, про що ви посилаєтесь created_by, у JWT вже є претензія, і воно називається iss(емітент).
Фред

так, хороший момент - я оновлю з цим ... дякую!
Бред Паркс

8

Я не думаю, що я експерт, але я хотів би поділитися деякими думками про Jwt.

  • 1: Як сказав Акшай, краще мати другу систему для перевірки вашого маркера.

    А.: Те, як я поводжусь із цим: я зберігаю геш генерований у сховищі сеансів із закінченням часу. Щоб перевірити маркер, він повинен бути виданий сервером.

    b.:Є хоча б одне, що повинно бути перевірено використаним методом підпису. наприклад:

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

Деякі бібліотеки, що підтверджують JWT, прийняли б цю, не перевіряючи хеш. Це означає, що, не знаючи, що ваша сіль використовується для підписання жетону, хакер може надати собі деякі права. Завжди переконайтесь, що цього не може статися. https://auth0.com/blog/2015/03/31/critical-vulnerasions-in-json-web-token-libraries/

c .: Використання файлу cookie з ідентифікатором сеансу не було б корисним для перевірки вашого маркера. Якщо хтось захоче захопити сеанс користувача лямбда, йому просто доведеться скористатися снайфером (наприклад: wireshark). Цей хакер матиме обидві дані одночасно.

  • 2: Це однаково для кожного секрету. Завжди є спосіб це пізнати.

Те, як я обробляю це, пов'язане з пунктом 1.а. : У мене є секрет, змішаний із випадковою змінною. Секрет унікальний для кожного маркера.

Однак я намагаюся зрозуміти кращі практики для того, як саме і в якій мірі маркер повинен бути затверджений, щоб зробити справді безпечну систему.

Якщо ви хочете отримати найкращу безпеку, не слід сліпо слідувати кращим практикам. Найкращий спосіб - зрозуміти, що ви робите (я думаю, що це нормально, коли я бачу ваше запитання), а потім оцінити необхідну безпеку. І якщо Моссад хоче отримати доступ до ваших конфіденційних даних, вони завжди знайдуть спосіб. (Мені подобається цей пост у блозі: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )


Хороший момент для того, щоб мати унікальний секрет для кожного маркера, але як ви створюєте унікальний секрет кожного разу? Я використовую бібліотеку
nimbus jwt

1
ймовірно, використовується хеш-пароль вашого користувача.
momokjaaaaa

1
"Якщо ви не робите речі так, як це роблять інші люди, людям буде складніше знайти шлях через вашу безпеку". Для мене це звучить як Безпека через невпевненість. Найкращі практики називають це тому, що вони практично пом'якшують найпоширеніші ризики.
Mnebuerquo

@Mnebuerquo Я повністю з вами згоден, хлопець, який написав, що не слід довіряти ;-)
Деблатон Жан-Філіпп

1
Він правдивий, хоча не слід сліпо слідувати кращим практикам. Добре зрозуміти, чому найкращі практики вважаються найкращими . У кожному проекті рішення щодо безпеки існує компроміс між безпекою та зручністю використання. Розуміння того, чому означає, що ви можете приймати ці рішення розумно. (Дотримуйтесь кращих практик, хоча ваші користувачі не стануть.)
Mnebuerquo

3

Тут багато хороших відповідей. Я інтегрую деякі відповіді, які, на мою думку, є найбільш актуальними, і додам ще кілька пропозицій.

1) Чи слід обмежувати перевірку токена JWT лише перевіркою підпису самого маркера, покладаючись лише на цілісність секрету сервера, або супроводжуватися окремим механізмом перевірки?

Ні, через причини, не пов'язані з компрометацією лексеми таємниці. Кожен раз, коли користувач входить в систему через ім’я користувача та пароль, сервер авторизації повинен зберігати або створений маркер, або метадані про створений маркер. Розгляньте ці метадані як запис авторизації. Дана пара користувачів та додатків повинна мати лише один дійсний маркер або авторизацію в будь-який момент часу. Корисними метаданими є ідентифікатор користувача, пов’язаний з маркером доступу, ідентифікатором програми та часом видачі маркера доступу (що дозволяє відкликати існуючі маркери доступу та видавати новий маркер доступу). У кожному запиті API підтвердіть, що маркер містить належні метадані. Потрібно зберігати інформацію про те, коли було видано кожен маркер доступу, щоб користувач міг відкликати наявні маркери доступу, якщо дані облікового запису порушені, і знову увійти в систему і почати використовувати новий маркер доступу. Це оновить базу даних з часом видачі маркера доступу (створений час авторизації). У кожному запиті API переконайтеся, що час видачі маркера доступу після створеного часу авторизації.

Інші заходи безпеки включали не реєстрацію JWT та не потребували захищеного алгоритму підписання, як SHA256.

2) Якщо перевірка підпису JWT є єдиним засобом перевірки маркерів, тобто цілісність секрету сервера є точкою розриву, як слід керувати секретами сервера?

Компроміс секретів сервера дозволить зловмиснику видавати маркери доступу для будь-якого користувача, а зберігання даних маркера доступу на кроці 1 не обов'язково перешкоджатиме серверу приймати ці маркери доступу. Наприклад, скажіть, що користувачеві видано маркер доступу, а потім пізніше зловмисник генерує маркер доступу для цього користувача. Час авторизації маркера доступу буде дійсним.

Як каже Акшай Далвала, якщо ваш секрет на стороні сервера порушений, то у вас виникають більші проблеми, оскільки це означає, що зловмисник порушив вашу внутрішню мережу, сховище вихідного коду або те і інше.

Однак система для пом’якшення пошкодження компрометованого секретного сервера та уникнення зберігання секретів у вихідному коді передбачає поворот маркерів таємницею за допомогою служби координації, наприклад https://zookeeper.apache.org. Використовуйте cron завдання, щоб генерувати секрет програми кожні кілька годин або близько того (хоча довгі ваші жетони доступу дійсні) та надішліть оновлений секрет до Zookeeper. На кожному сервері додатків, який повинен знати секрет маркера, конфігуруйте ZK-клієнт, який оновлюється кожного разу, коли значення вузла ZK змінюється. Зберігайте первинну та вторинну таємницю, і щоразу, коли секрет лексеми змінюється, встановлюйте новий секрет лексеми на первинний та старий секрет лексеми на вторинний. Таким чином, наявні дійсні маркери все ще будуть дійсними, оскільки вони будуть підтверджені щодо вторинної таємниці. На час, коли вторинна таємниця буде замінена на стару первинну таємницю, усі жетони доступу, видані із вторинною таємницею, в будь-якому разі закінчуються.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.