Ввод пароля в вызове REST API

31

Предположим, у меня есть REST API, который также используется для установки / сброса паролей. Давайте также предположим, что это работает через соединения HTTPS. Есть ли веская причина не вводить этот пароль в путь вызова, скажем, я закодирую его в BASE64?

Примером может быть сброс пароля таким образом:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

Я понимаю, что BASE64 - это не шифрование, но я хочу только защитить пароль для серфинга в этом случае.

Барт Фридрихс
источник
61
Вы предлагаете побочные эффекты на GET? Это нарушение протокола прямо здесь.
Эсбен Сков Педерсен
27
Это не совсем REST, потому что resetpassword/OLDPASSWD/NEWPASSWDэто не ресурс. Это вызов процесса. Вам не нужно вставлять все в URL.
usr
5
@Esben: кто сказал, что это GET? ОП никогда не говорил этого.
Дагналии
3
Правда, он не в вопросе. Но его комментарий к ответу Нетча говорит: «Думаю, мне все-таки придется использовать POST», поэтому мы можем предположить, что он изначально намеревался / спросил о GET. Который, как указывает Эсбен, является плохим. GET должен только для чтения.
Mawg

Ответы:

76

Хороший сервер регистрирует все отправленные ему запросы, включая URL-адреса (часто без переменной части после '?'), IP-адрес источника, время выполнения ... Вы действительно хотите, чтобы этот журнал (потенциально читаемый широкой группой администраторов) содержал критически безопасная информация как пароли? Base64 не стопор против них.

Netch
источник
42
Это не самая большая причина для использования POST. Это причина безопасности. Но, как Эсбен уже заметил в комментариях, изменение состояния с помощью GET является нарушением такой службы отдыха
Pinoniq
2
@BartFriederichs: и история браузера будет помнить URL. И анонимно опробовать набор паролей, сделав веб-страницу, содержащую ссылку для всех паролей, которые вы хотите попробовать, и разрешив роботу Googlebot выполнять фактические запросы ...
RemcoGerlich
2
«Но, как Эсбен уже заметил в комментариях, изменение состояния с помощью GET является нарушением такой службы отдыха», я тоже заметил этот комментарий, но я не вижу, где кто-то говорит, что это был запрос GET. Вы можете встраивать информацию в URI и все-таки POST. Однако это не совсем RESTful , поскольку URI фактически не называет ресурс.
Джошуа Тейлор
Привет, хороший ответ, но я не согласен с "без переменной части после '?'" ... многие хранят полный URL-адрес !!!
dagnelies
2
«изменение состояния с помощью GET является нарушением такой службы Rest» - давайте не будем слишком увлекаться REST. Изменение состояния с помощью GET является нарушением HTTP .
Дэн Эллис
69

То, что вы предлагаете, не является ни безопасным, ни RESTful.

@Netch уже упоминал о проблеме с журналами, но есть и другая проблема, которая заключается в том, что вы показываете пароли, отправляемые по HTTP, что упрощает захват паролей с помощью любого вида перехватчика проводов или атаки «человек посередине».

Когда вы выполняете запрос GET с использованием REST, различные элементы в URL представляют более мелкозернистые элементы. Ваш URL читается так, как будто вы возвращаете NEWPASSWD часть OLDPASSWD, которая является частью пароля для сброса. Это не имеет никакого семантического смысла. GET не должны использоваться для сохранения данных.

Вы должны делать что-то вроде этого:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST, потому что вы пишете данные, и https, потому что вы не хотите, чтобы их прослушивали.

(Это действительно низкий уровень безопасности. Абсолютный минимум, который вы должны сделать.)

Горт Робот
источник
2
Разве это не означает хеширование паролей на стороне клиента? Это рекомендуется?
Роуэн Фриман
1
Примечание: это не позволит применять требования к паролю (длина и т. Д.). Это не может быть проблемой в вашем случае, хотя это частая практика безопасности, и иногда требуется для некоторых организаций.
Пол Дрейпер
8
Вы не создаете новую запись, а обновляете существующую (обычно), поэтому это должно быть PUT, а не POST.
4
Это не очень приятно. resetpassword не является ресурсом, не говоря уже о подресурсе. Тем не менее, /user/joe/passwordэто немного лучше, но не оптимально.
Вихрь
12
@CamilStaps Нет, вы не можете использовать PUT, потому что PUTидемпотент. Но когда пароль был изменен с secretна supersecretуспешно, то тот же запрос не будет выполнен во второй раз, так POSTчто здесь правильно. Конечно, как сказал @whirlwin, этот ресурс не очень хорошо назван.
Residuum
60

Предлагаемая схема имеет проблемы в нескольких областях.

Безопасность

URL-пути часто регистрируются; установка нехэшированных паролей в пути - плохая практика.

HTTP

Информация об аутентификации / авторизации должна появиться в заголовке авторизации. Или, возможно, для браузера - заголовок Cookie.

ОСТАЛЬНЫЕ

Глаголы, такие как resetpasswordв вашем URL, как правило, являются явным признаком не-репрезентативной парадигмы передачи состояния. URL должен представлять ресурс. Что это значит, чтобы получить resetpassword? Или УДАЛИТЬ?

API

Эта схема требует всегда знать предыдущий пароль. Вы, вероятно, захотите разрешить больше дел; например, пароль утерян.


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

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

Он не помещает сверхсекретную информацию в путь и следует соглашениям HTTP и REST.

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

Пол Дрэйпер
источник
4

Помимо безопасности, проблема в том, что это не очень RESTful подход.

OLDPASSWDи NEWPASSWDничего не стоит в иерархии ресурсов, и, что еще хуже, операция не идемпотентна.

Таким образом, вы можете использовать только в POSTкачестве своего глагола, и вы не должны включать два пароля в путь к ресурсу.

biziclop
источник
1
Вы не создаете новую запись, а обновляете существующую (обычно), поэтому это должно быть PUT, а не POST.
2
@CamilStaps Если бы он просто устанавливал пароль, это могло бы быть. Но поскольку предположительно старый пароль тоже нужно проверять, это делает операцию неидемпотентной, и поэтому PUTдисквалифицируется как глагол. С ним можно было бы перенастроить, PUTно в его нынешнем виде это не так.
Бизиклоп
OLDPASSWD - это информация для аутентификации, и ее вообще не должно быть в URL.
Не обязательно, это обычная практика явно запрашивать старый пароль поверх аутентификации.
Бизиклоп
3

Проблема состоит в том, чтобы избежать простых текстовых паролей в ваших запросах. Существует два варианта выполнения остальных требований веб-сервиса.

1. Хеширование на стороне клиента

  • Я думаю, вы храните свои пароли, например, хэш (пароль + соль)
  • Вы можете хешировать новый пароль с солью на стороне клиента
  • Это означает: создать новую соль на стороне клиента, создать хеш, например, хэш (newPassword + newSalt)
  • Отправьте новый созданный хэш плюс соль на ваш спокойный веб-сервис
  • Отправить старый пароль также в виде хэша (oldPassword + oldSalt)

2. Шифрование

  • Создайте ресурс «одноразовый ключ» (otk) для пользователя, например / otk / john
  • Этот ресурс возвращает безопасный случайный уникальный одноразовый ключ, например, kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs и уникальный идентификатор, например, 95648915125
  • Ваш спокойный веб-сервис должен хранить этот случайный otk для следующего безопасного общения с идентификатором 95648915125
  • Зашифруйте ваш новый и старый пароль с помощью otk, например, AES (из соображений безопасности вы должны использовать два отдельных otks для старого и нового пароля)
  • Отправьте зашифрованные пароли на ваш ресурс смены пароля с идентификатором 95648915125
  • Одна комбинация otk и ID может работать только один раз, поэтому вы должны удалить эту комбинацию после изменения пароля
  • Возможный лучший вариант: отправить текущий / старый пароль с помощью Basic-Auth.

Примечание: HTTPS требуется для обоих вариантов!

maz258
источник
1
Зачем мне дважды шифровать (ваша схема шифрования с помощью OTK и HTTPS) обмен паролями? Какой вектор атаки здесь не распространяется по протоколу HTTPS?
Барт Фридрихс
1
Единственная цель - избежать возможных журналов сервера. Тело HTTP-запроса (например, с новым паролем в виде открытого текста) также может быть зарегистрировано, хотя HTTPS используется. Другая возможная атака - использование самозаверяющего сертификата, который особенно используется для внутренних целей.
maz258
2

Каковы особенности операции сброса пароля?

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

Пункт 1 здесь означает, что вы не можете использовать GET, вы должны либо POST-то, представляющее операцию смены пароля, в URI, представляющий ресурс, который обрабатывает изменения пароля, либо PUT, представляющее новый пароль, в URI, представляющий пароль или представляющий что-то пользователь) которого пароль является функцией.

Как правило, мы бы POST, не в последнюю очередь потому, что это может быть неудобно ПОСТАВЛЯТЬ что-то, что мы не можем позже ПОЛУЧИТЬ и, конечно, мы не можем получить пароль.

Таким образом, пункт 2 будет представлять данные, представляющие новый пароль, в том виде, в котором он размещен.

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

Следовательно, URI должен быть похож на <http://example.net/changeCurrentUserPassword>или <http://example.net/users/joe/changePassword>.

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

Джон Ханна
источник