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

Аутентификация на основе токена REST API

Я разрабатываю REST API, требующий аутентификации. Поскольку сама аутентификация происходит через внешний веб-сервис через HTTP, я решил, что мы будем раздавать токены, чтобы избежать неоднократного вызова службы проверки подлинности. Это подводит меня к моему первому вопросу:

Неужели это действительно лучше, чем просто требовать от клиентов использовать HTTP Basic Auth для каждого запроса и кэшировать вызовы на сервер проверки подлинности?

Основное решение Auth имеет то преимущество, что не требует полного обратного перехода к серверу до того, как начнутся запросы на контент. Токены потенциально могут быть более гибкими по охвату (т.е. Предоставлять только права на определенные ресурсы или действия), но это представляется более подходящим для контекста OAuth, чем мой более простой вариант использования.

В настоящее время токены приобретаются следующим образом:

curl -X POST localhost/token --data "api_key=81169d80...
                                     &verifier=2f5ae51a...
                                     &timestamp=1234567
                                     &user=foo
                                     &pass=bar"

Для всех запросов требуются api_key, timestamp и verifier. "Верификатор" возвращается:

sha1(timestamp + api_key + shared_secret)

Мое намерение состоит только в том, чтобы разрешать вызовы от известных сторон и предотвращать повторное использование вызовов.

Это достаточно хорошо? Underkill? Overkill?

С помощью токена в руках клиенты могут приобретать ресурсы:

curl localhost/posts?api_key=81169d80...
                    &verifier=81169d80...
                    &token=9fUyas64...
                    &timestamp=1234567

Для простейшего вызова это кажется довольно ужасным. Учитывая, что shared_secret завершит внедрение (как минимум) приложения iOS, из которого я бы предположил, что он может быть извлечен, это даже предложение чего-либо, кроме ложного смысла безопасности?

4b9b3361

Ответ 1

Позвольте мне разобрать все и решить каждую проблему отдельно:

Аутентификация

Для аутентификации baseauth имеет то преимущество, что это зрелое решение на уровне протокола. Это означает, что многие "могут возникнуть позже" проблемы уже решены для вас. Например, с помощью BaseAuth пользовательские агенты знают, что пароль является паролем, поэтому он не кэширует его.

Загрузка сервера Auth

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

Безопасность передачи

Если вы можете использовать SSL-соединение, все, что с ним связано, соединение безопасно *. Чтобы предотвратить случайное многократное выполнение, вы можете отфильтровать несколько URL-адресов или попросить пользователей включить в URL случайный компонент ( "nonce" ).

url = username:[email protected]/api/call/nonce

Если это невозможно, и передаваемая информация не является секретной, я рекомендую защитить запрос с помощью хэша, как вы предложили в подходе к токенам. Поскольку хеш обеспечивает безопасность, вы можете поручить своим пользователям предоставлять хэш в качестве пароля baseauth. Для повышения надежности я рекомендую использовать случайную строку вместо метки времени как "nonce" для предотвращения повторных атак (два логических запроса могут быть сделаны в течение одной и той же секунды). Вместо того, чтобы предоставлять отдельные поля "shared secret" и "api key", вы можете просто использовать ключ api как общий секрет, а затем использовать соль, которая не изменяется, чтобы предотвратить атаки радужных таблиц. Поле имени пользователя кажется хорошим местом для установки nonce, поскольку оно является частью auth. Итак, теперь у вас есть чистый вызов:

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:[email protected]/api/call

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

Защита секретного хранилища

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

Если вы пытаетесь заставить других поставщиков программного обеспечения получить ваш ключ API для предотвращения развития альтернативных клиентов, почти подходит подход, основанный на шифровании и хранении. Это криптовальный код whitebox, и на сегодняшний день никто не придумал действительно безопасного решения проблем этого класса. Самое меньшее, что вы можете сделать, это по-прежнему выдавать один ключ для каждого пользователя, чтобы вы могли запретить злоупотребляемые ключи.

(*) EDIT: SSL-соединения больше не должны считаться безопасными без предпринять дополнительные шаги для проверки их.

Ответ 2

Чистый RESTful API должен использовать стандартные функции протокола:

  • Для HTTP API RESTful должен соответствовать существующим заголовкам HTTP. Добавление нового HTTP-заголовка нарушает принципы REST. Не заново изобретайте колесо, используйте все стандартные функции в стандартах HTTP/1.1, включая коды ответа на состояние, заголовки и т.д. Веб-службы RESTFul должны использовать и полагаться на стандарты HTTP.

  • УСЛУГИ RESTful ДОЛЖНЫ быть БЕСПЛАТНЫМИ. Любые трюки, такие как аутентификация на токенах, которая пытается запомнить состояние предыдущих запросов REST на сервере, нарушают принципы REST. Опять же, это ДОЛЖНО; то есть, если веб-сервер сохраняет любую относящуюся к запросу/ответ информацию о контексте на сервере, пытаясь установить какой-либо сеанс на сервере, тогда ваш веб-сервис НЕ является безгосударственным. И если он не является апатридом, он НЕ RESTFul.

Нижняя строка: для целей аутентификации/авторизации вы должны использовать стандартный заголовок авторизации HTTP. То есть вы должны добавить заголовок авторизации/проверки подлинности HTTP в каждом последующем запросе, который должен быть аутентифицирован. API REST должен следовать стандартам схемы аутентификации HTTP. Специфика того, как этот заголовок должен быть отформатирован, определен в стандартах RFC 2616 HTTP 1.1 - раздел 14.8. Разрешение RFC 2616 и в HTTP-аутентификации RFC 2617: аутентификация по базовому и дайджест-доступу.

Я разработал сервис RESTful для приложения Cisco Prime Performance Manager. Найдите в Google документ REST API, который я написал для этого приложения, для получения более подробной информации о соответствии API RESTFul API здесь. В этой реализации я выбрал использовать "Базовую" авторизационную схему HTTP. - проверьте версию 1.5 или выше этого документа API REST и выполните поиск авторизации в документе.

Ответ 3

В сети протокол с состоянием основан на наличии временного токена, который обменивается между браузером и сервером (через заголовок файла cookie или перезаписи URI) по каждому запросу. Этот токен обычно создается на стороне сервера, и это кусок непрозрачных данных, который имеет определенное время жизни, и он имеет единственную цель - идентифицировать конкретного агента веб-пользователя. То есть токен является временным и становится ГОСУДАРСТВОМ, который веб-сервер должен поддерживать от имени клиентского агента пользователя в течение всего сеанса. Следовательно, связь с использованием токена таким образом является STATEFUL. И если разговор между клиентом и сервером STATEFUL, это не RESTful.

Имя пользователя/пароль (отправляется в заголовке авторизации) обычно сохраняется в базе данных с целью идентификации пользователя. Иногда пользователь может означать другое приложение; однако имя пользователя/пароль НИКОГДА не предназначено для идентификации конкретного пользовательского агента веб-клиента. Разговор между веб-агентом и сервером на основе использования имени пользователя/пароля в заголовке авторизации (в соответствии с базовой авторизацией HTTP) является ОТВЕТСТВЕННЫМ, поскольку интерфейс веб-сервера не создает и не сохраняет какую-либо информацию STATE от имени конкретной сети клиентский агент пользователя. И, основываясь на моем понимании REST, в протоколе четко сказано, что разговор между клиентами и сервером должен быть ОТВЕТСТВЕННЫМ. Поэтому, если мы хотим иметь истинную услугу RESTful, мы должны использовать имя пользователя/пароль (см. RFC, упомянутое в моем предыдущем сообщении) в заголовке авторизации для каждого отдельного вызова, а не как вид привязки (например, токены сеанса, созданные на веб-серверах, Токены OAuth, созданные на серверах авторизации и т.д.).

Я понимаю, что несколько вызываемых REST-провайдеров используют маркеры, такие как OAuth1 или OAuth2 accept-tokens, которые должны быть переданы в качестве заголовков HTTP в качестве "Authorization: Bearer". Тем не менее, мне кажется, что использование этих токенов для сервисов RESTful нарушит истинное значение STATELESS, означающее, что REST охватывает; потому что эти жетоны являются временным элементом данных, созданным/поддерживаемым на стороне сервера, для идентификации конкретного пользовательского агента веб-клиента за действительную продолжительность сеанса веб-клиента/сервера. Поэтому любая служба, использующая эти токены OAuth1/2, не должна называться REST, если мы хотим придерживаться ИСТИННОГО значения протокола STATELESS.

Рубенс