Одна вещь, о которой я не упоминал здесь, хотя это является расширением ответа Маркуса Адамса, заключается в том, что вы не должны использовать одну часть информации для идентификации и аутентификации пользователя, если есть возможность временных атак , которые могут используйте разницу во времени ответа, чтобы угадать, насколько далеко зашло сравнение строк.
Если вы используете систему, которая использует «ключ» для поиска пользователя или учетных данных, эту информацию можно постепенно угадывать с течением времени, отправляя тысячи запросов и исследуя время, которое требуется вашей базе данных, чтобы найти (или нет найти) запись. Это особенно верно, если «ключ» хранится в виде открытого текста вместо одностороннего хеширования ключа. Вы хотели бы хранить ключи пользователей в виде открытого текста или симметрично зашифрованных, если вам нужно снова отобразить ключ для пользователя.
Имея вторую часть информации или «секрет», вы можете сначала найти пользователя или учетные данные с помощью «ключа», который может быть уязвим для временной атаки, а затем использовать безопасную по времени функцию сравнения, чтобы проверить значение секрет".
Вот реализация этой функции в Python:
https://github.com/python/cpython/blob/cd8295ff758891f21084a6a5ad3403d35dda38f7/Modules/_operator.c#L727
И он представлен в hmac
библиотеке (и, возможно, в других):
https://docs.python.org/3/library/hmac.html#hmac.compare_digest
Здесь следует отметить одну вещь: я не думаю, что этот вид атаки будет работать со значениями, которые хешируются или зашифрованы перед поиском, потому что сравниваемые значения изменяются случайным образом каждый раз, когда изменяется символ во входной строке. Я нашел хорошее объяснение этого здесь .
Тогда решениями для хранения ключей API будут:
- Используйте отдельный ключ и секрет, используйте ключ для поиска записи и используйте безопасное по времени сравнение для проверки секрета. Это позволяет вам снова показать пользователю ключ и секрет.
- Используйте отдельный ключ и секрет, используйте симметричное детерминированное шифрование секрета и выполняйте обычное сравнение зашифрованных секретов. Это позволяет вам снова показать пользователю ключ и секрет и избавить вас от необходимости выполнять безопасное по времени сравнение.
- Используйте отдельный ключ и секрет, отобразите секрет, хеш и сохраните его, затем выполните обычное сравнение хешированного секрета. Это избавляет от необходимости использовать двустороннее шифрование и дает дополнительное преимущество, заключающееся в сохранении секретности в случае взлома системы. У него есть обратная сторона: вы не можете снова показать секрет пользователю.
- Используйте один ключ , один раз покажите его пользователю, хешируйте его, а затем выполните обычный поиск хешированного или зашифрованного ключа. Здесь используется один ключ, но он не может быть снова показан пользователю. Имеет преимущество хранения ключей в безопасности в случае взлома системы.
- Используйте один ключ , один раз покажите его пользователю, зашифруйте его и выполните обычный поиск зашифрованного секрета. Может быть снова показан пользователю, но ценой уязвимости ключей в случае взлома системы.
Из них я считаю, что 3 - лучший баланс безопасности и удобства. Я видел, как это реализовано на многих сайтах при выдаче ключей.
Кроме того, я приглашаю любых реальных экспертов по безопасности критиковать этот ответ. Я просто хотел вынести это как еще одну тему для обсуждения.