Лучший подход для генерации ключа API

89

Итак, сейчас есть множество различных сервисов, Google API, Twitter API, Facebook API и т. Д.

У каждой службы есть ключ API, например:

AIzaSyClzfrOzB818x55FASHvX4JuGQciR9lv7q

Все ключи различаются по длине и символам, которые они содержат. Мне интересно, какой подход лучше всего для создания ключа API?

Я не прошу конкретный язык, просто общий подход к созданию ключей, должны ли они быть шифрованием деталей пользовательского приложения, или хешем, или хешем случайной строки и т. Д. Стоит ли нам беспокоиться о хеш-алгоритме (MSD, SHA1, bcrypt) и т. Д.?

Изменить: я поговорил с несколькими друзьями (электронная почта / твиттер), и они рекомендовали просто использовать GUID с удаленными дефисами.

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

Фил
источник
Я ответил более подробно здесь .. генерация ключей и использование их как hmac auth
Аншу Кумар

Ответы:

58

Используйте генератор случайных чисел, предназначенный для криптографии. Затем кодируйте число по системе base-64.

Это пример C #:

var key = new byte[32];
using (var generator = RandomNumberGenerator.Create())
    generator.GetBytes(key);
string apiKey = Convert.ToBase64String(key);
Эдвард Брей
источник
2
Это не очень безопасно, злоумышленник, получивший доступ к вашей базе данных, может получить ключ. Было бы лучше сгенерировать ключ как хэш чего-то уникального для пользователя (например, соль) в сочетании с секретом сервера.
Джеймс Вежба,
13
Хранение случайно сгенерированного ключа API имеет те же характеристики безопасности, что и хранение хешированного пароля. В большинстве случаев это нормально. Как вы предлагаете, можно рассматривать случайно сгенерированное число как соль и хешировать его с секретом сервера; однако, поступая таким образом, вы несете накладные расходы на хэш при каждой проверке. Также нет способа аннулировать секрет сервера без аннулирования всех ключей API.
Эдвард Брей,
Хорошее решение, но вам, вероятно, нужно ключевое слово var перед apiKey :) var apiKey = Convert.ToBase64String (key);
Давид Ойа
@ JohnM2 Верно. Я оставлял его открытым, так как apiKeyмог быть объявлен в другом месте. Я добавил тип для наглядности.
Эдвард Брей
4
@JamesWierzba, если атакованные уже есть в вашей базе данных, то их незащищенный доступ к вашему API, вероятно, меньше всего вас беспокоит ...
Джон Стори,
29

Ключи API должны иметь следующие свойства:

  • однозначно идентифицировать авторизованного пользователя API - «ключевую» часть «API-ключа»
  • аутентифицировать этого пользователя - невозможно угадать / подделать
  • могут быть отозваны, если пользователь плохо себя ведет - обычно они вводят ключ в базу данных, из которой можно удалить запись.

Обычно у вас есть тысячи или миллионы ключей API, а не миллиарды, поэтому им не нужно:

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

Таким образом, один из способов создания ключа API - это взять две части информации:

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

и подписать их, используя личный секрет.

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

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


или хеш случайной строки

Хеширование не предотвращает подделку. Подписание - это гарантия того, что ключ пришел от вас.

Майк Сэмюэл
источник
3
Требуется ли этап подписи вашего алгоритма, если ключ API, представленный клиентом, проверяется по базе данных уже зарегистрированных ключей API на сервере, предоставляющем API? Похоже, что подпись здесь будет лишней, если ключи предоставляет сервер.
sappenin
3
@sappenin, да. Если вы храните на сервере нераспознаваемый ключ, то вам не нужно предотвращать подделку. Часто запросы API обрабатывается любым из фермы машин - сервер является одним из многих серверов. Проверка подписи может выполняться на любом компьютере без обращения к базе данных и обратно, что в некоторых случаях может избежать состояния гонки.
Майк Сэмюэл
1
@MikeSamuel, если ключ API подписан, и вы не совершаете круговой обход базы данных, что произойдет, если ключ отозван, но все еще используется для доступа к API?
Abhyudit Jain 01
@AbhyuditJain, В любой распределенной системе вам нужен согласованный порядок сообщений (аннулирование происходит до последующего использования отозванных учетных данных) или другие способы ограничения неоднозначности. Некоторые системы не обрабатывают каждый запрос и обратно - если узел кэширует тот факт, что ключ находился в базе данных в течение 10 минут, остается только 10 минут. окно, в котором злоумышленник может злоупотребить отозванными учетными данными. Однако возможна путаница: пользователь отменяет учетные данные, затем проверяет, что они отозваны, и удивляется, потому что незакрепленные сеансы заставляют два запроса переходить на разные узлы.
Майк Сэмюэл
5

Я использую UUID в нижнем регистре без тире.

Генерация проста, поскольку она встроена в большинство языков.

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

Адам Ральф
источник
15
Не думайте, что UUID трудно угадать; они не должны использоваться в качестве средств безопасности (спецификация UUID RFC4122, раздел 6 ). Ключ API требует безопасного случайного числа, но UUID нельзя надежно определить .
Эдвард Брей
3
@EdwardBrey, что насчет UUID uuid = UUID.randomUUID();Java? Вы хотите сказать, что случайного выбора недостаточно?
Micro,
8
@MicroR Случайный UUID является безопасным, только если генератор случайных чисел, используемый для его создания, является криптографически безопасным и достаточно 128 бит. Хотя UUID RFC не требует безопасного генератора случайных чисел, в данной реализации его можно использовать бесплатно. В случае randomUUID в документации API конкретно указано , что он использует «криптографически стойкий генератор псевдослучайных чисел». Так что эта конкретная реализация безопасна для 128-битного ключа API.
Эдвард Брей,
4

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

public static string CreateApiKey()
{
    var bytes = new byte[256 / 8];
    using (var random = RandomNumberGenerator.Create())
        random.GetBytes(bytes);
    return ToBase62String(bytes);
}

static string ToBase62String(byte[] toConvert)
{
    const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    BigInteger dividend = new BigInteger(toConvert);
    var builder = new StringBuilder();
    while (dividend != 0) {
        dividend = BigInteger.DivRem(dividend, alphabet.Length, out BigInteger remainder);
        builder.Insert(0, alphabet[Math.Abs(((int)remainder))]);
    }
    return builder.ToString();
}
Эдвард Брей
источник
1

Ключ API должен быть случайным значением. Достаточно случайный, чтобы его нельзя было предсказать. Он не должен содержать никаких сведений о пользователе или учетной записи, для которых он предназначен. Использование UUID - хорошая идея, если вы уверены, что созданные идентификаторы случайны.

Например, более ранние версии Windows выдавали предсказуемые идентификаторы GUID, но это старая история.

Дэйв Ван ден Эйнде
источник
4
Windows 2000 перешла на GUID с использованием случайных чисел . Однако нет никакой гарантии, что случайные числа невозможно предсказать. Например, если злоумышленник создает для себя несколько ключей API, можно будет определить будущее случайное число, которое будет использоваться для генерации ключа API другого пользователя. В общем, не следует рассматривать UUID как надежно неразличимые .
Эдвард Брей