Подпись HTTP-запроса без сеанса

Я думаю о веб-службе отдыха, которая гарантирует, что для каждого отправленного ему запроса:

  • Запрос был сгенерирован пользователем, заявившим о нем;
  • Запрос не был изменен кем-то другим (uri/method/content/date);
  • Для запросов GET должна быть предусмотрена возможность создания URI с достаточным количеством информации для проверки подписи и установки даты истечения срока действия. Таким образом, пользователь может делегировать временные разрешения READ соавтору на ограниченный период времени для ресурса с помощью сгенерированного URI.

Клиенты аутентифицируются с помощью идентификатора и подписи содержимого на основе их пароля.

Сессии вообще не должно быть, поэтому состояние сервера! Сервер и клиент имеют общий секретный ключ (пароль)

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

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

Служба использует настраиваемый заголовок «Content-signature» для хранения учетных данных. Аутентифицированный запрос должен содержать этот заголовок:

Content-signature: <METHOD>-<USERID>-<SIGNATURE>

<METHOD> is the sign method used, in our case SRAS.
<USERID> stands for the user ID mentioned earlier.
<SIGNATURE> = SHA2(SHA2(<PASSWORD>):SHA2(<REQUEST_HASH>));
<REQUEST_HASH> = <HTTP_METHOD>\n
                 <HTTP_URI>\n
                 <REQUEST_DATE>\n
                 <BODY_CONTENT>;

Запрос считается недействительным через 10 минут после его создания.

Например, типичный HTTP-ЗАПРОС будет таким:

POST /ressource HTTP/1.1
Host: www.elphia.fr
Date: Sun, 06 Nov 1994 08:49:37 GMT
Content-signature: SRAS-62ABCD651FD52614BC42FD-760FA9826BC654BC42FD

{ test: "yes" }

Сервер ответит:

401 Unauthorized

OR

200 OK

Переменные будут:

<USERID> = 62ABCD651FD52614BC42FD
<REQUEST_HASH> = POST\n
                 /ressource\n
                 Sun, 06 Nov 1994 08:49:37 GMT\n
                 { test: "yes" }\n

Параметры URI

Некоторые параметры могут быть добавлены в URI (они перегружают информацию заголовков):

  • _sras.content-signature=‹METHOD>-‹USERID>-‹SIGNATURE>: УКАЖИТЕ учетные данные в URI, а не в заголовке HTTP. Это позволяет пользователю поделиться подписанным запросом;
  • _sras.date=Sun, 06 Nov 1994 08:49:37 GMT (дата запроса*): Дата создания запроса.
  • _sras.expires = Sun, 06 Nov 1994 08:49:37 GMT (дата истечения срока действия*): сообщить серверу, что срок действия запроса не должен истечь до указанной даты.

*формат даты: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18

Спасибо за ваши комментарии.


person Sébastien VINCENT    schedule 22.12.2010    source источник


Ответы (2)


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

1- К нестандартным заголовкам принято добавлять префикс «X-Namespace-», в вашем случае вы можете назвать свой заголовок примерно так: «X-SRAS-Content-Signature».

2- Заголовок Date может не обеспечивать достаточного разрешения для значения nonce, поэтому я бы посоветовал использовать отметку времени с разрешением не менее 1 миллисекунды.

3- Если вы не сохраните хотя бы последний одноразовый номер, вы все равно можете воспроизвести сообщение в 10-минутном окне, что, вероятно, неприемлемо для запроса POST (может создать несколько экземпляров с одинаковыми значениями в вашей веб-службе REST). Это не должно быть проблемой для глаголов GET PUT или DELETE.

Однако на PUT это можно использовать для атаки типа «отказ в обслуживании», заставляя много раз обновлять один и тот же объект в течение предложенного 10-минутного окна. При GET или DELETE существует аналогичная проблема.

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

4- Этот метод также требует, чтобы клиент и серверы были синхронизированы по часам с расхождением менее 10 минут. Это может быть сложно отладить или невозможно реализовать, если у вас есть клиенты AJAX, для которых вы не управляете часами. Это также требует установки всех временных меток в формате UTC.

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

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

5- Вам также необходимо обратить внимание на повторные передачи из-за сетевых ошибок. Нельзя предполагать, что сервер не получил последнее сообщение, для которого клиент не получил TCP Ack перед разрывом TCP-соединения. Поэтому одноразовый номер необходимо увеличивать между каждой повторной передачей выше уровня TCP и пересчетом подписи с новым одноразовым номером. Тем не менее, необходимо добавить номер сообщения, чтобы предотвратить двойное выполнение на сервере: двойной POST приведет к созданию 2 объектов.

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

7- Ваш метод не гарантирует клиенту, что сервер является подлинным и не был взломан DNS. Аутентификация сервера обычно считается важной для безопасной связи. Эта услуга может быть предоставлена ​​путем подписания ответов с сервера с использованием того же одноразового номера, что и для запроса.

person Jean Vincent    schedule 26.12.2010
comment
Привет @жан. 1- Я проверю RFC для этого. 2- Я не понимаю, что вы имеете в виду, нет одноразового номера, но давайте посмотрим на следующий вопрос. 3- Да, вы правы. Чтобы правильно обрабатывать повторные атаки, я должен хранить использованные подписи на сервере memcached, чтобы увидеть, использовались ли они (срок действия истекает через 10 минут). 4- я могу их синхронизировать, первый запрос дает дату сервера, клиент ajax проверяет разницу между сервером и ним. И укажите хорошую ДАТУ UTC. 5- это не пб. И может проверить, использовалась ли подпись 6- Да, верно!. 7- Да, я проверю, чтобы добавить поддержку. - person Sébastien VINCENT; 28.12.2010
comment
@Sébastien, 2- Дата фактически используется как одноразовый номер в предлагаемом вами протоколе. Если два запроса отправлены в течение одной и той же секунды, они будут иметь одинаковую дату и должны быть отклонены, поскольку они использовали тот же одноразовый номер, что и предыдущий запрос. Увеличения разрешения до одной миллисекунды может быть достаточно, а может и нет. 4- Если вы синхронизируете с помощью запроса на установку, может быть проще использовать счетчик. Кроме того, у вас все еще может быть проблема с фоновым обновлением даты. 5- Если вы проверяете подпись, вы должны отклонить второе сообщение как несанкционированное (воспроизведенное), что может запутать клиента. - person Jean Vincent; 28.12.2010
comment
2- Я не отклоняю запрос, если дата уже видна. Возможно, я не ясно где-то раньше. 4- Я просто сохраняю разницу в секундах между локальным временем и временем сервера. Это можно сделать с любым запросом, отправленным с сервера. Для этого не нужно делать запрос, а просто проверить последний. 5- Это ожидаемое поведение. Если запрос оказался неудачным, пользователь должен сделать новый с новой подписью (и изменить секундную часть даты). - person Sébastien VINCENT; 29.12.2010
comment
@Sebastien 2- Если уже увиденная дата не отклонена, одноразового номера действительно нет. Это подвержено повторным атакам, например. множественный POST, приводящий к созданию 2 или более объектов. В данном случае я не понимаю цели даты. Вам нужно уточнить, чего вы хотите достичь: таким образом, пользователь может делегировать временные разрешения READ соавтору на ограниченный период времени для ресурса со сгенерированным URI. Это похоже на проблему безопасности, поскольку делегирование должно обрабатываться на уровне авторизации, а не на уровне целостности сообщения. 5- Это может привести к двойному POST. - person Jean Vincent; 31.12.2010
comment
2- Одноразовый номер - это ХЭШ-ПОДПИСЬ. Запрос не может быть использован дважды. Если запрос изменится, подпись изменится. В случае, если запрос не дошел до сервера, я делаю новый идентичный, но с новой подписью (для подписи используется дата). Делегат означает, что я хочу предоставить кому-то URI для доступа к ресурсу, на который у меня есть права. Я могу подумать об этом, потому что вы правы, это не подходящее место для этого. 5- Мне нужно разобраться с этим в другом месте. В архитектуре REST я должен стараться быть как можно более ИДЕМПОТЕНТНЫМ. - person Sébastien VINCENT; 31.12.2010

Я хотел бы отметить, что вы можете выполнить это с помощью OAuth, в первую очередь "двухстороннего OAuth", когда клиент и сервер совместно используют секрет. См. http://tools.ietf.org/html/rfc5849#page-14. В вашем случае вы хотите опустить параметр oauth_token и, вероятно, использовать метод подписи HMAC-SHA1. В этом нет ничего особенно болтливого; вам не нужно проходить через потоки получения токена OAuth, чтобы делать что-то таким образом. Это дает возможность использовать любую из нескольких существующих библиотек OAuth с открытым исходным кодом.

Что касается состояния на стороне сервера, вам необходимо отслеживать, какие секреты используются с какими клиентами, а также какие одноразовые номера использовались в последнее время (для предотвращения повторных атак). Вы можете пропустить проверку одноразового номера / время жизни, если вы запускаете что-то через HTTPS, но если вы собираетесь это делать, то HTTPS + Basic Auth дает вам все, что вы описали, без необходимости писать новое программное обеспечение.

person Jon Moore    schedule 26.12.2010
comment
Спасибо. Я не уверен, что действительно хочу войти в большую машину OAuth. Но, возможно, я попробую двухсторонний OAuth. Спасибо за ваш ответ. - person Sébastien VINCENT; 28.12.2010