REST аутентифікація та відкриття ключа API


93

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

Якщо я будую API RESTful і хочу його захистити, одним із методів, який я бачив, є використання маркера безпеки. Коли я використовував інші API, був маркер і спільний секрет ... має сенс. Що я не розумію, це те, що запити на операцію з відпочинку надаються через javascript (XHR / Ajax), що не дозволяє комусь обнюхати щось просте, як FireBug (або "переглянути джерело" у браузері) та копіювання ключа API, а потім видавання себе за особу за допомогою ключа та секрету?


Один із методів, які я бачив, - це використовувати маркер безпеки , там дійсно багато методів. Маєте конкретний приклад. Я можу подумати, що ви плутаєте "REST" проти "зробити доступним API javascript лише для зареєстрованих користувачів" (наприклад, Google Maps).
PeterMmm

1
Оскільки ви запитували майже 2 роки тому: чим ви врешті-решт користувалися самі?
Ар'ян

Я насправді нічого не використовував, я просто намагався обернути голову навколо створення концепцій. Коментований вище коментар PeterMmm, мабуть, правдивий ... все ще не було потреби реалізувати щось із цього, але я хотів покращити себе ... спасибі за подальші дії.
tjans

Відповіді:


22

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


9
Тож, якщо це просто знак, який передано ... хіба це все ще не виставляється в JavaScript ... так що, якщо я розміщу мерехтливу фотографію на своїй веб-сторінці через їх API (називається javascript), і ви відвідуєте мою сторінку, чи не так " t Я відкриваю свій ключ API кожному, хто відвідує мою сторінку?
tjans

6
Я не думаю, що я правильно задаю своє запитання ... напевно, це частина причини, в якій я не знаходив того, що шукав. коли я здійснюю ajax-виклик, скажімо, використовуючи jquery, мені доведеться вставити ключ api в виклик ajax, щоб він передався серверу ... в цей момент хтось може побачити ключ API. Якщо я розумію це неправильно, як ключ API надсилається із запитом, якщо його не вбудовано у сценарій клієнта?
tjans

4
на закінчення: людям буде призначена пара apikey + apisecret перед використанням openapi / restapi, знак apikey + буде переведений на сервер, щоб переконатися, що сервер знає, хто робить запит, apisecret ніколи не буде переданий на сервер для безпеки .
Джеймс.Xu

7
Отже, заява @ James.Xu про те, що "секрет використовується для генерування знаку поточного запиту", ЛЖЕ! Оскільки клієнт не знає секрет, тому що було б небезпечно надіслати його (а як ще він би це знав?) "Секрет", який технічно є "приватним ключем", використовується ТОЛЬКО СЕРВЕРОМ (оскільки ніхто більше не знає цього) для створення знака, який порівнюється зі знаком клієнта. Отже, питання: Які дані поєднуються з клавішею api, яку ніхто більше не знає, крім клієнта та сервера? Знак = api_key + що?
АК

1
Ви маєте рацію, @AC. Навіть якщо обидва сервери (веб-сайт та сторонній API) знають один і той же секрет, не можна обчислити деяку підпис на сервері веб-сайту, а потім помістити цей результат у HTML / JavaScript, а потім змусити браузер передавати його в API. У цьому випадку будь-який інший сервер може запросити HTML з першого веб-сервера, отримати підпис із відповіді та використовувати його в HTML на своєму власному веб-сайті. (Я справді думаю, що вищезгаданий пост не відповідає на питання про те, як відкритий ключ API в HTML може бути безпечним.)
Ар'ян,

61

Ми відкриваємо API, який партнери можуть використовувати лише в доменах, які вони зареєстрували у нас. Його вміст частково є загальнодоступним (але бажано лише показуватися у відомих нам доменах), але переважно є приватним для наших користувачів. Так:

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

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

Цей ключ API дійсно видимий для всіх, ми не автентифікуємо свого партнера іншим способом, і нам не потрібен REFERER . Все-таки це безпечно:

  1. Коли наш get-csrf-token.js?apiKey=abc123запит:

    1. Знайдіть ключ abc123у базі даних та отримайте список дійсних доменів для цього ключа.

    2. Шукайте файл cookie для перевірки CSRF. Якщо його не існує, генеруйте захищене випадкове значення та покладіть його в cookie - сеанс, призначений лише для HTTP . Якщо файл cookie існував, отримайте наявне випадкове значення.

    3. Створіть маркер CSRF з ключа API та випадкове значення з файлу cookie та підпишіть його . (Замість того, щоб зберігати список жетонів на сервері, ми підписуємо значення. Обидва значення будуть читабельні у підписаному маркері, це добре.)

    4. Встановіть відповідь не кешована, додайте файл cookie та поверніть сценарій, наприклад:

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }
      

    Примітки:

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

    • Та сама політика щодо JavaScript забезпечує, що браузер не може використовувати XHR (Ajax) для завантаження та перевірки джерела JavaScript. Натомість звичайний браузер може завантажувати його лише за допомогою <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(або динамічного еквівалента), а потім запускає код. Звичайно, ваш сервер не повинен підтримувати крос-походження ресурсів та JSONP для створеного JavaScript.

    • Сценарій браузера може змінити значення, document.domainперш ніж завантажити вищезазначений сценарій. Але та ж політика походження тільки дозволяє скоротити домен шляхом видалення префіксів, як переписування subdomain.example.comпросто example.com, або myblog.wordpress.comна wordpress.com, або в деяких браузерах , навіть bbc.co.ukв co.uk.

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

  2. Коли користувач входить у систему, встановіть певне печиво користувача будь-яким способом. (Користувач, можливо, вже увійшов до введення в систему JavaScript.)

  3. Усі наступні запити API на сервер (включаючи запити GET та JSONP) повинні містити маркер CSRF, файл cookie для перевірки CSRF та (якщо увійшов у систему) файл cookie користувача. Тепер сервер може визначити, чи потрібно довіряти запиту:

    1. Наявність дійсного маркера CSRF забезпечує завантаження JavaScript з очікуваного домену, якщо він завантажений браузером.

    2. Наявність маркера CSRF без файлу cookie підтверджує підробку.

    3. Наявність як маркера CSRF, і файлу cookie для перевірки CSRF нічого не забезпечує: це може бути або запит на стороні підробленого сервера, або дійсний запит від браузера. (Це не може бути запит браузера, зроблений із непідтримуваного домену.)

    4. Наявність файлу cookie користувача гарантує, що користувач увійшов у систему, але не гарантує, що він є членом даного партнера, а також, що користувач переглядає правильний веб-сайт.

    5. Наявність файлу cookie користувача без файлу cookie для перевірки CSRF вказує на підробку.

    6. Наявність файлу cookie користувача забезпечує поточний запит через браузер. (Передбачається , що користувач не вводитиме свої облікові дані на невідомому сайті, і припускаючи , що ми не дбаємо про користувачів , використовуючи свої власні облікові дані , щоб зробити запит який - то на боці сервера.) Якщо ми також є куки перевірки CSRF, то , що перевірка CSRF печиво було також отриманий за допомогою браузера. Далі, якщо у нас також є маркер CSRF з дійсним підписом, івипадкове число у файлі cookie валідації CSRF відповідає номеру цього маркера CSRF, тоді JavaScript для цього маркера також був отриманий під час того самого самого попереднього запиту, під час якого було встановлено файл cookie CSRF, отже, також за допомогою браузера. Це також означає, що вищевказаний код JavaScript був виконаний до встановлення маркера, і що в той час домен був дійсним для даного ключа API.

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

    7. Якщо в будь-який момент сервер не довіряє запиту, то повертається 403 Заборонено. Віджет може відповісти на це, показавши попередження користувачеві.

Підписувати файли cookie для перевірки CSRF не потрібно, оскільки ми порівнюємо його з підписаним маркером CSRF. Якщо не підписувати файл cookie, кожен запит HTTP стає коротшим, а перевірка сервера - трохи швидшою.

Створений маркер CSRF дійсний нескінченно, але лише в поєднанні з файлом cookie, тому ефективно, поки браузер не закриється.

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

Для тих, хто використовує OAuth, див. Також OAuth та віджети на стороні клієнта , з яких я отримав ідею JavaScript. Для серверного використання API, в якому ми не можемо покластися на код JavaScript для обмеження домену, ми використовуємо секретні ключі замість відкритих ключів API.


1
Під час використання CORS, можливо, можна сміливо розширити це. Замість викладеного вище, під час обробки заздалегідь перевіреного OPTIONSзапиту за допомогою якогось відкритого ключа API в URL-адресі сервер може повідомити браузеру, які домени дозволені (або скасувати запит). Але будьте обережні, що для деяких запитів не потрібен попередньо перевірений запит, або він взагалі не використовуватиме CORS , а CORS потребує IE8 +. Якщо для IE7 використовується якась резервна копія Flash, можливо, деяка динаміка crossdomain.xmlможе допомогти досягти того ж. Ми ще не пробували CORS / Flash.
Ар'ян

10

На це питання є прийнята відповідь, але просто для уточнення подібна таємна автентифікація працює так:

  1. Клієнт має відкритий ключ, ним можна поділитися з будь-ким, неважливо, тому ви можете вбудовувати його у javascript. Це використовується для ідентифікації користувача на сервері.
  2. Сервер має секретний ключ, і цей секрет ПОВИНЕН бути захищений. Тому для автентифікації загального ключа потрібно захистити свій секретний ключ. Отже, публічний клієнт JavaScript, який підключається безпосередньо до іншої служби, неможливий, оскільки для захисту секрету вам потрібен посередник сервера.
  3. Сервер підписує запит, використовуючи якийсь алгоритм, який включає секретний ключ (секретний ключ на зразок солі), і переважно часова марка, після чого надсилає запит до служби. Позначення часу - це запобігання "повторним" атакам. Підпис запиту дійсний лише протягом п яти секунд. Ви можете перевірити це на сервері, отримавши заголовок часової мітки, який повинен містити значення часової позначки, яка була включена в підпис. Якщо термін дії цієї мітки минув, запит не працює.
  4. Сервіс отримує запит, який містить не тільки підпис, але й усі поля, які були підписані простим текстом.
  5. Потім служба аналогічно підписує запит за допомогою спільного секретного ключа та порівнює підписи.

Щоправда, але, розробивши вашу відповідь, не виставляйте ключ API. Тим НЕ менше, в деяких API , ключ API є загальнодоступною, а це те, що мова йшла про: «запити до операції інших служб [...] зроблено через JavaScript (XHR / Ajax)» . (Я вважаю, що прийнята відповідь неправильна і з цього приводу; я вважаю, що ваш пункт 2 про це зрозумілий, добре.)
Арджан

1

Я думаю, ви маєте на увазі ключ сеансу, а не ключ API. Ця проблема успадковується від протоколу http і називається викраденням сесії . Звичайний "спосіб вирішення" - як і на будь-якому веб-сайті - змінити на https.

Щоб забезпечити безпечну послугу REST, потрібно ввімкнути https та, можливо, автентифікацію клієнта. Зрештою, це поза ідеєю REST. REST ніколи не говорить про безпеку.


8
Я насправді мав на увазі ключ. Якщо я правильно пам’ятаю, щоб користуватися API, ви передаєте ключ API та секрет службі відпочинку, щоб перевірити автентифікацію, правда? Я знаю, що як тільки він буде переданий через провід, він буде зашифрований SSL, але перш ніж він буде надісланий, це чудово видно за кодом клієнта, який його використовує ...
tjans

1

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

Ідентифікатор сеансу передається лише один раз, і ЦЕ ОБОВ'ЯЗКОВО переходитиме через SSL.

Дивіться приклад тут

Підписуйте запит, щоб не допустити викрадення сеансу, без відмітки та позначки часу.


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

1

Я спробую відповісти на питання в оригінальному контексті. Тому питання "Чи безпечний секретний (API) ключ для розміщення в JavaScript.

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

На мій погляд, рішення полягає в тому, що виклик JavaScript по суті передає запит внутрішньому компоненту сервера, який відповідає за виклик відпочинку. Скажімо, внутрішній компонент сервера, скажімо, сервлет прочитає ключ API із захищеного джерела, наприклад, файлова система на основі дозволів, вставить у заголовок HTTP та здійснить зовнішній виклик спокою.

Я сподіваюся, що це допомагає.

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