Подтвердить что ты не робот

Аутентификация в веб-службах RESTful

В настоящее время я создаю веб-сайт, на котором пользователи могут просматривать и изменять свои виджеты. Все взаимодействия с данными виджета, хранящиеся на моем сервере, будут выполняться через веб-службы RESTful. Например, если пользователь хочет увидеть список своих виджетов, поток выполнения будет выглядеть примерно так:

  • Пользователь 12345 обращается к https://www.example.com/Login.htm и аутентифицируется на сервере (в моем случае через поставщика OpenID)
  • Пользователь 12345 затем обращается к странице https://www.example.com/Widgets.htm
  • Сервер отвечает HTML-страницей и javascript, которые будут использоваться для доступа к моим веб-службам.
  • Когда страница HTML загрузится, будет вызываться функция javascript getWidgets(). getWidgets() вызовет мой веб-сервис https://www.example.com/Services/Widget/12345
  • Служба отвечает списком виджетов пользователей, которые другие функции javascript renderWidgets(widgets) будут обновлять страницу html

Я не хочу, чтобы кто-либо еще, кроме пользователя 12345, обращался к своим собственным виджетам, поэтому я предполагаю, что getWidgets() должен будет предоставить некоторую аутентификацию для моей веб-службы. Я не уверен, что лучший способ достичь этого.

Я думал, что клиент и сервер могут иметь общий секрет, который getWidgets() отправит в веб-службу. Сервер может генерировать этот секрет как случайную строку (число, GUID или что-то еще) и включать ее в заголовок ответа, когда клиент запрашивает исходную HTML-страницу. Клиент будет использовать этот секретный ключ при отправке запросов на сервер.

Это звучит как разумная идея?

Это обычное требование, так есть стандартный способ достижения одного и того же? Насколько мне известно, это выходит за рамки OpenID и OAuth не подходит.

Спасибо заранее.

4b9b3361

Ответ 1

Это отличный вопрос, но я думаю, что ваше решение может быть немного сложнее, чем вы думаете.

В общем, способ аутентификации такого сценария заключается в двухэтапном рукопожатии. Первым шагом является то, что ваше приложение предоставит серверу закрытый ключ (сгенерированный сервером, уникальный для клиентского приложения), чтобы аутентифицировать его, фактически, действительный клиент. Это то, что дает авторитетные доказательства вашему серверу, что запрос поступает от программного обеспечения, которое он знает и которому можно доверять.

Вторым шагом является то, что когда пользователь переходит к входу в клиентское приложение, они предоставляют комбинацию имени пользователя и пароля. Эта информация вместе с вашим ключом приложения должна быть отправлена ​​на сервер через SSL.

SSL шифрует данные таким образом, что сторонняя сторона с пакетом-сниффером не может читать данные в пути, а сервер выполняет следующее:

  • Проверяет, что ключ приложения действителен.
  • Подтверждает, что имя пользователя существует и связано с приложением.
  • Шифрует пароль и проверяет зашифрованную версию на зашифрованную версию в базе данных, связанную с именем пользователя.
  • ЕСЛИ ALL перечисленных выше проверок проходит, сервер возвращает идентификатор сеанса, который может быть помещен в клиентский cookie - и используется для повторной аутентификации пользователя при каждом последующем запросе, ЕСЛИ ЛЮБОЕ тестов терпит неудачу - сервер возвращает ответ 401: Unauthorized или другую аналогичную ошибку.

В этот момент клиент может использовать возвращенный идентификатор сеанса без необходимости повторного отправки ключа приложения.

Ваше приложение

Теперь, в вашем случае, вы можете фактически разместить клиент/сервер в том же приложении и на том же сервере. В этом случае вы можете вообще пропустить все части, вращающиеся вокруг частного ключа приложения, и просто запретить запросы на межсайтовый script.

Почему? - потому что вещь, которую вы действительно защищаете, такова:

Сервер A размещает ваш RESTful API. Клиентские клиенты B, C и D, которые будут полагаться на API сервера A. Вы не хотите, чтобы клиент E (а не ваше приложение - и вредоносное) мог получить доступ к серверу A путем обхода или кражи учетных данных одного из других Клиентов.

Если, однако, оба клиента и сервер размещены в одном месте и, следовательно, имеют один и тот же URL-адрес, то есть RESTful API находится в www.yourdomain.com/api, а клиент находится в www.yourdomain.com/ - вы обычно можете просто не разрешать Запросы типа AJAX, которые происходят за пределами yourdomain.com - и это ваш уровень безопасности.

В этом случае вам нужно будет сделать следующее, чтобы иметь разумный уровень безопасности:

  • Включить SSL для вашего сервера.
  • Разрешать только запросы /auth/login (или независимо от вашего метода входа POST) через SSL (в С# это можно сделать, используя атрибут [RequireHttps] для метода или контроллера).
  • Отклонить любые запросы AJAX, которые происходят за пределами вашего собственного домена.
  • Используйте слой шифрования в вашем файле cookie.

Что должен содержать ваш файл cookie?

В идеале, файл cookie должен содержать двухсторонние зашифрованные данные, которые ТОЛЬКО ваш сервер может расшифровать. Другими словами, вы можете поместить в файл cookie что-то вроде пользователя username или user_id, но с двухсторонним зашифровать его с помощью Rijndael или другой криптографической системы - используя пароль шифрования, к которому имеет доступ только ваш сервер (я предлагаю случайная строка символов).

Затем - когда вы получаете последующие запросы с прикрепленным файлом cookie, вы можете просто сделать следующее:

  • Если файл cookie существует, попробуйте его расшифровать с помощью личного пароля.
  • Если результирующие дешифрованные данные являются мусором - отбросьте ответ 401: Unauthorized (это измененный или поддельный файл cookie).
  • Если полученные дешифрованные данные являются именем пользователя, которое соответствует вашей базе данных, теперь вы знаете, кто делает запрос, и могут соответствующим образом фильтровать/обслуживать данные.

Надеюсь, это поможет.:) Если нет - не стесняйтесь оставлять комментарии и задавать вопросы, и я попытаюсь уточнить.

Ответ 2

Разве это не так просто, как MACing запроса на веб-службу?

Итак, вы предоставляете JavaScript, который вызывает веб-службу, в этом JavaScript вы помещаете nonce. В отличие от большинства версий nonce, вы будете жить в течение всего сеанса пользователя.

При вызове веб-службы JavaScript в GetWidgets() использует nonce в качестве ключа для хеширования некоторых "случайных" данных, я бы, вероятно, использовал время, отформатированное как строка и данные, идентификатор пользователя (12345) как соль. Затем JavaScript отправляет хэшированные данные и неповрежденную временную строку как часть вызова веб-службы, но не nonce. Затем я гарантирую, что время, которое было отправлено, было последним (т.е. последние 20 секунд, ограничивая атаки повтора), и что когда сервер хэширует время с идентификатором пользователя как соль с nonce (что он знает, потому что он его установил), что это получает тот же результат.

Итак, мы использовали общий ключ, установленный сервером и отправленный клиенту, чтобы выступать в качестве ключа для создания кода авторизации хэшированного сообщения (MAC или HMAC), но также предотвращали повторные атаки (MAC будет отличаться для каждый запрос и имеет очень короткое окно воспроизведения) и предотвращение захвата сеанса, не передавая информацию о сеансе в файлы cookie.


Я раньше не сталкивался с вашим конкретным сценарием, но это похоже на конкретный случай общей проблемы аутентификации сообщений без необходимости отправлять учетные данные с каждым сообщением. MACing надежно, как я это видел.

Смотрите: http://en.wikipedia.org/wiki/Message_authentication_code, это дает хороший обзор MACing и даже содержит диаграмму, которая поможет объяснить. Это довольно хорошо проторенное решение, единственное отклонение, которое я бы рекомендовал (потому что я испортил его), заключается в том, чтобы включить время в сообщение, чтобы предотвратить повторы.

Это основа подхода, используемого Last.fm для авторизации доступа к их API, см. часть 8 на этой странице (Подписание вызовов): http://www.last.fm/api/authspec#8. Хэш, который они используют, - MD5, запрос включается в основной текст, который должен быть хэширован, а секрет используется ключ сеанса, полученный из первоначального вызова аутентификации.

Ответ 3

Я не знаю много о безопасности, но я думаю, все зависит от того, сколько времени/затрат вы готовы потратить (имея в виду, что все взломанно).

Как и в случае с безопасностью, вы, вероятно, защищаете переменные сеанса, самая простая вещь, которую вы можете сделать, - это вызов ajax для действия сервера, в котором вы проверяете сеанс и сравниваете его с запросом пользователя.