RESTful веб-сервіс - як аутентифікувати запити інших служб?


117

Я розробляю RESTful веб-сервіс, до якого потрібно звертатися користувачам, а також інші веб-сервіси та програми. Усі вхідні запити повинні бути засвідчені автентифікацією. Вся комунікація відбувається через HTTPS. Аутентифікація користувача буде працювати на основі маркера аутентифікації, отриманого шляхом POSTing імені користувача та пароля (через SSL-з'єднання) на ресурс / сеанс, наданий службою.

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

Варіанти, які я можу придумати (або мені були запропоновані):

  1. Запропонуйте клієнтським службам використовувати "підроблені" ім'я користувача та пароль та підтвердити їх автентифікацію так само, як користувачі. Мені не подобається цей варіант - він просто не почуває себе правильно.

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

  3. Я можу додати перевірку IP-адреси до попереднього параметра. Це ускладнить виконання підроблених запитів.

  4. Клієнтські сертифікати. Налаштувати власні повноваження щодо сертифікатів, створити кореневий сертифікат та створити клієнтські сертифікати для клієнтських послуг. Однак на думку приходить декілька питань: а) як я все ж дозволяю користувачам аутентифікуватись без сертифікатів; б) наскільки складно реалізувати цей сценарій з точки зору обслуговування клієнтів?

  5. Ще щось - там повинні бути інші рішення?

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


Якщо я можу запропонувати, ознайомтесь із сервісами веб-сайтів із великим вікном та виберіть, що вам подобається. Ваші користувачі також виявлять подібність з найкращими методами інших послуг RESTful.
Ізмір Рамірес

Знайшов ще одне питання (майже два роки), яке стосується подібної теми: stackoverflow.com/questions/1138831/…
Томмі

На якій ОС розміщуються служби (як Інтернет, так і інші)? Вони працюють на серверах, які є частиною тієї ж інфраструктури?
Андерс Абель

Операційна система може змінюватись: Win, * nix і т. Д. А послуги клієнтів можуть бути або не бути в тій же інфраструктурі, що і моя служба.
Томмі

Відповіді:


34

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

Я не думаю, що ваш пункт А щодо рішення клієнтського сертифіката важко вирішити. Ви просто використовуєте гілку. if (client side certificat) { check it } else { http basic auth }Я не експерт з Java, і ніколи не працював з цим, щоб робити сертифікати на стороні клієнта. Однак швидкий Google веде нас до цього підручника, який виглядає прямо на вашій алеї.

Незважаючи на все обговорення "що найкраще", дозвольте мені лише зазначити, що існує ще одна філософія, яка говорить: "менше коду, тим менше розумності, тим краще". (Я особисто дотримуюся цієї філософії). Рішення сертифіката клієнта звучить як багато коду.

Я знаю, що ви висловили запитання щодо OAuth, але пропозиція OAuth2 включає рішення вашої проблеми під назвою " жетони носія ", яку необхідно використовувати разом з SSL. Я думаю, що для простоти я б обрав або жорстко закодований користувач / пропуск (по одному на додаток, щоб їх можна було відкликати індивідуально), або дуже схожі жетони носія.


27
Клієнтські сертифікати НЕ є загальною таємницею. Ось чому вони існують. Клієнт має приватний ключ, а сервер - відкритий ключ. Клієнт ніколи не ділиться своїм секретом, а відкритий ключ не є секретом.
Тім

5
Посилання на підручник не призводить до статті підручника, але до якоїсь індексної сторінки Java на сайті Oracle ...
Marjan Venema

2
@MarjanVenema Ну, це тому, що ви пробуєте посилання +2 роки після відповіді newz2000, але ви завжди можете спробувати WayBack Machine: web.archive.org/web/20110826004236/http://java.sun.com/…
Fábio Герцог Сільва

1
@MarjanVenema: Вибачте, але ви очікуєте, що сюди прийде newz2000 та оновить посилання після того, як воно померло? Як ви сказали, це гниття зв’язку, тому це рано чи пізно відбувається. Або ви намагаєтеся отримати доступ до архіву, щоб побачити, що бачив автор у той час, або ви знайдете нове посилання та зробите позитивний внесок. Я не бачу, як ваш коментар комусь допоміг. Але ось, перейдіть за цим посиланням: oracle.com/technetwork/articles/javase/… (будьте обережні, що з часом теж загниє )
Fábio Duque Silva

2
@ Фабіосільва: Ні. Я не очікую, що він це зробить. Я прочитав архів, але я не встиг піти шукати нове посилання, тому зробив наступне найкраще: додати коментар, що він мертвий, щоб хтось із спільноти міг знайти нове місце та оновити пост. Оскільки ви, очевидно, мали час знайти нове посилання, чому ви не оновили посилання в публікації, а не помістили її в коментар, що мене приковує?
Мар'ян Венема

36

Прочитавши ваше запитання, я б сказав, генеруйте спеціальний маркер для виконання запиту. Цей маркер буде жити в певний час (скажімо за один день).

Ось приклад для створення маркера аутентифікації:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

наприклад: 3 червня 2011 року

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

потім з'єднайтеся з паролем користувача, наприклад "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Потім зробіть MD5 цього рядка:

05a9d022d621b64096160683f3afe804

Коли ви телефонуєте на запит, завжди використовуйте цей маркер,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

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

Надія допомагає

:)


1
Мені дуже подобається те, як ви додавали маркер безпеки до кожного запиту, але що відбувається з цим, коли програміст вже створив 100 сторінок jsp і після цього t0 реалізує захист у раніше створених 100сторінках, а також на сторінках, які будуть створені. У такому випадку додавання маркера до кожного запиту не є правильним вибором. У будь-якому випадку +1 для вашої техніки. :)
Анкур Верма

4
Що станеться, якщо годинник не синхронізується? Чи не в цьому випадку клієнт генерує неправильний маркер? Навіть якщо обидва генерують дату в UTC, їх годинник все одно може бути різним, що призводить до вікна часу щодня, коли маркер не працює?
NickG

@NickG, У мене була ця проблема раніше, єдиний спосіб убезпечити це, запитуючи час сервера. Це 99% вбиває проблему UTC. Звичайно, недоліком є ​​додатковий виклик на сервер.
короро

але я все ще можу споживати вашу веб-службу, використовуючи цей маркер протягом дня, правда? я не бачу, як це допоможе
Міна Габріель

@MinaGabriel Ви можете додати більше часових рамок у генерації токенів. (хвилина * 10) + (година * 100) + (день * 1000) + (місяць * 10000) + (рік (останні 2 цифри) * 100000)
kororo

11

Можна скористатися декількома різними підходами.

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

  2. Служба клієнтів може зберігати файл cookie, який підтримує ідентифікатор сеансу. Я особисто не вважаю це образливим, як деякі пуристи, від яких я чую - автентифікацію можна повторювати знову і знову. Здається, ви не надто любите цю ідею, хоча.

  3. З вашого опису, це дійсно здається, що вас може зацікавити OAuth2 Мій досвід досі, що я бачив, - це те, що це заплутане і якесь кровотеча. Там є реалізації, але їх небагато і між ними. У Java я розумію, що він інтегрований у модулі захисту Spring3 . (Їх підручник чудово написаний.) Я чекав, чи не буде розширення в Restlet , але поки що, хоча це було запропоновано і може бути в інкубаторі, він все ще не повністю включений.


Я не маю нічого проти варіанту 2 - я вважаю, що це прекрасне рішення в програмі RESTful - але звідки клієнтська служба отримує жетон в першу чергу? Як вони аутентифікуються вперше? Можливо, я вважаю це неправильним, але здається дивним, що для служби клієнтів для цього потрібно мати власне ім’я користувача та пароль.
Томмі

Якщо кінцевий користувач є вашим користувачем, то служба-посередник може передати свої повноваження разом з вами за першим запитом, і ви можете повернути файл cookie чи інший маркер.
jwismar

Аналогічно, у сценарії OAuth кінцевий користувач делегує посередницькій службі свій доступ до вашої веб-служби.
jwismar

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

1
Я просто додам, що перерахований вище варіант №1 повинен здійснюватися лише через HTTPS.
mr-sk

3

Я вважаю, що підхід:

  1. Перший запит, клієнт надсилає id / пароль
  2. Обмін ідентифікатором / пропуском на унікальний маркер
  3. Підтвердьте маркер кожного наступного запиту до його закінчення

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

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

Сподіваюся, це допомагає


3

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

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

Залежно від веб-сервера, який ви використовуєте, повинен існувати метод визначення автентифікації клієнта, який прийме сертифікат клієнта, але не вимагає цього. Наприклад, у Tomcat, визначаючи свій роз'єм https, ви можете встановити "clientAuth = хочу", а не "true" або "false". Тоді ви переконайтеся, що ви додасте до свого довіреного сертифіката сертифікат CA (за замовчуванням файл кесерів у JRE, який ви використовуєте, якщо ви не вказали інший файл у конфігурації веб-сервера), тому єдиними довіреними сертифікатами будуть ті, що видаються ваш підписаний CA.

На стороні сервера ви дозволятимете доступ до служб, які ви хочете захистити, лише якщо вам вдасться отримати клієнтський сертифікат із запиту (не нульовий) та пройти будь-які перевірки DN, якщо ви хочете отримати додатковий захист. Для користувачів, які не мають клієнтських сертифікатів, вони все одно матимуть доступ до ваших послуг, але у них просто не буде сертифікатів.

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


3

5. Щось інше - там повинні бути інші рішення?

Ти маєш рацію, є! І називається він JWT (JSON Web Tokens).

JSON Web Token (JWT) - це відкритий стандарт (RFC 7519), який визначає компактний та автономний спосіб безпечної передачі інформації між сторонами як об'єкт JSON. Цю інформацію можна перевірити та довірити, оскільки вона підписана цифровим шляхом. JWT можуть бути підписані за допомогою секретного (за допомогою алгоритму HMAC) або пари відкритого / приватного ключів за допомогою RSA.

Я настійно рекомендую заглянути в JWT. Вони набагато простіше вирішення проблеми в порівнянні з альтернативними рішеннями.

https://jwt.io/introduction/


1

Ви можете створювати сеанс на сервері та обмінюватися sessionIdміж клієнтом та сервером при кожному виклику REST.

  1. Перший запит аутентифицироваться REST: /authenticate. Повертає відповідь (відповідно до формату вашого клієнта) за допомогою sessionId: ABCDXXXXXXXXXXXXXX;

  2. Зберігайте це sessionIdв Mapреальному сеансі. Map.put(sessionid, session)або використовувати SessionListenerдля створення та знищення ключів для вас;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Отримайте сесії з кожним викликом REST, як-от URL?jsessionid=ABCDXXXXXXXXXXXXXX(або іншим способом);

  4. Поверніться HttpSessionз карти за допомогою sessionId;
  5. Підтвердити запит на сеанс, якщо сеанс активний;
  6. Надіслати відповідь або повідомлення про помилку

0

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


Гм, я не впевнений, чи зміг би я наслідувати пояснення. Про якого користувача ми говоримо? Я говорю про спілкування програми з іншим додатком. Я припускаю, що ти це зрозумів, але я все ще не можу повністю зрозуміти. Що станеться, коли, наприклад, цей "маркер" закінчиться?
Томмі

Ну а маркер, який ви генеруєте та відправляєте назад в іншу програму, є стійким маркером, він прив’язаний до користувача та до програми. Ось посилання на документи чотирикутників developer.foursquare.com/docs/oauth.html, і це, в основному, oauth2, тож розгляньте це для гарного рішення для аутентифікації.
Девін М

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

Тоді просто генеруйте ключі для додатків, якщо все, що ви робите, - це доступ до програм, то для аутентифікації повинні працювати простий application_id та application_key. Якщо ви хочете, щоб вони перевіряли автентифікацію з використанням маркера, використовуючи параметри аутентифікації маркера, оскільки це був би просто параметр, переданий із запитом url у вашу програму.
Девін М

Але чи не це тоді точно так само, як тоді сценарій токена аутентифікації користувача ім'я користувача + пароль = сеанс? Хіба не application_id та application_key є лише синонімами імені користувача та пароля? :) Цілком чудово, якщо це дійсно є стандартною практикою для такої ситуації - як я вже сказав, я недосвідчений у цьому, - але я просто подумав, що можуть бути інші варіанти ...
Томмі,

-3

Крім автентифікації, пропоную вам подумати над великою картиною. Розгляньте можливість зробити свій сервіс RESTful без будь-якої автентифікації; потім помістіть декілька дуже простих аутентифікацій, потрібних для середнього рівня, між кінцевим користувачем та сервісом резервного сервера.


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

Середнім шаром може бути веб-сервер, такий як Nginx, ви можете робити там автентифікацію. Модель аутентифікації може бути заснована на сеансі.
Даган

Як я намагався пояснити у своєму запитанні, я не хочу схеми аутентифікації на основі сеансу для цих служб клієнтів. (Будь ласка, дивіться мої оновлення цього питання.)
Томмі,

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