Использование HMAC-SHA1 для аутентификации API - как безопасно хранить пароль клиента?

В RESTful API, использующем аутентификацию в стиле S3 , клиент API подписывает запрос своим секретным ключом с использованием HMAC-SHA1, поэтому секретный ключ никогда не передается по сети. Затем сервер аутентифицирует клиента, используя секретный ключ этого клиента, чтобы повторить сам процесс подписи и сравнить результат с подписью, переданной клиентом.

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

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

Одно из дополнительных решений - зашифровать все пароли пользователей с помощью некоторого алгоритма симметричного ключа. Однако серверу придется хранить ключ к этому шифрованию где-нибудь легко доступным, например внутри исходного кода. Это лучше, чем ничего, но не оптимальное решение (как @Rook упомянул в своем ответе, это нарушает CWE-257).

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

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


person Elad    schedule 30.03.2011    source источник


Ответы (3)


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

Обратите внимание, что это не пароль - это общий секрет. Здесь есть фундаментальное различие - пароль обычно выбирается пользователем, тогда как общий секрет генерируется случайным образом и предоставляется пользователю (в этом контексте они часто называются «ключами API»).

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

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

person caf    schedule 31.03.2011
comment
Спасибо @caf. Уточнение разницы между паролями и общими секретами - хороший момент. Даже если мой сервис облажался, по крайней мере, я других не облажался :) - person Elad; 31.03.2011
comment
Асимметричная система, которую вы описываете, - хорошая схема! Но есть компромисс (он всегда есть, не так ли?). Проблема в том, что теперь нужно согласовать клиент и сервер. Это ящик Пандоры со странными ошибками, которые никто не поймет, пока вы не поймете, что они возникли не по порядку. Это также создает проблемы параллелизма (если, например, клиентское приложение имеет несколько экземпляров, обращающихся к API). Короче говоря, это добавляет много сложности ради безопасности. - person Elad; 31.03.2011
comment
+1 за разъяснение между «общим секретом» и «паролем». Этот ответ был именно тем, что я искал. - person Rafael Ibraim; 28.09.2011
comment
До сих пор не понимаю, что вы подразумеваете под порядковым номером. Разве это не должно действовать как разовый случай? как это избавляет от необходимости точно хранить общий секрет? - person frostymarvelous; 28.08.2013
comment
@frostymarvelous Подпись может быть сделана с использованием закрытого ключа и проверена с помощью открытого ключа. Если серверу известен открытый ключ клиента, он может проверить подпись для аутентификации клиента. Если подпись верна, вы можете быть уверены, что запрос пришел от клиента, а не от кого-либо еще, если только закрытый ключ клиента не был скомпрометирован. Номер нужен, чтобы подпись менялась каждый раз для каждого запроса к серверу. В противном случае кто-то мог схватить пакет и ... отправить его снова ›( - person Jeroen Landheer; 16.10.2014
comment
@Jeroen Landheer, похоже, работоспособное решение. Та же угроза безопасности применяется к собственным приложениям, поэтому я считаю это приемлемым риском. - person frostymarvelous; 16.10.2014

В идеале после входа пользователя в систему вы даете ему Cryptographic Nonce, который используется в качестве секретного ключа K HMAC на протяжении всего сеанса. Это безопасный подход, но не RESTful, потому что REST не имеет состояния. Идея кода аутентификации сообщения, выдаваемого при входе в систему, технически является формой состояния.

Шифрование паролей и их хранение в базе данных является нарушением CWE-257.

person rook    schedule 30.03.2011
comment
Спасибо, @Rook. Я должен был упомянуть, что мой API является RESTful, и поэтому я бы предпочел избежать шага входа в систему. Я соответствующим образом отредактировал свой вопрос. - person Elad; 30.03.2011
comment
@Elad Можно привести аргумент, что вы не можете быть действительно без гражданства и иметь аутентификацию, потому что имена пользователей и пароли являются формой состояния. Конечно, каждая реализация REST с аутентификацией будет идти на этот компромисс ради безопасности. - person rook; 31.03.2011
comment
Я считаю, что вы правы, но задал свой вопрос в надежде, что я ошибаюсь :) Я оставлю его открытым еще некоторое время на случай, если кто-то действительно найдет идеальное решение. - person Elad; 31.03.2011
comment
@Elad, это отличный вопрос, открытый для обсуждения и интерпретации. - person rook; 31.03.2011
comment
@Elad Этот пост заставил меня задуматься, поэтому мне пришлось спросить SO: stackoverflow.com/questions/5492683/ - person rook; 31.03.2011
comment
Извините за поздний вопрос. Но как криптографический одноразовый идентификатор, отправленный сервером, будет доставлен клиенту? Что произойдет, если кто-то посередине получит в руки одноразовый номер (при условии отсутствия SSL / TLS)? У меня именно эта проблема, которую я не могу решить. - person html_programmer; 30.05.2014

Я не уверен, что мне что-то здесь не хватает, но один из вариантов - использовать хешированный пароль в качестве симметричного ключа.

person Pushpendra    schedule 05.04.2013
comment
Тогда потребителю нужно будет знать, как вы хэшируете свои пароли, и если вы используете соли ....... думаю, теперь вы видите проблему. - person frostymarvelous; 28.08.2013
comment
в противном случае пользователю необходимо знать хэш, который будет использоваться для входа в систему. что не просто неудобство - person frostymarvelous; 28.08.2013