- PVSM.RU - https://www.pvsm.ru -
Мне действительно нравится уровень безопасности, предоставляемый U2F, но вместе с безопасностью, необходимо продумать и план восстановления. Потеря доступа к своим самым важным аккаунтам, если с основным U2F токеном что-то случится — серьезная проблема. В то же время, хотелось бы избежать использования бэкапа, который ставит под угрозу безопасность, предоставляемую U2F.
На сегодняшний день, образцовая практика — держать второй независимый U2F токен для бэкапа; этот токен должен быть добавлен вручную на каждый сервис и храниться в «безопасном» месте. Другая общепринятая практика — использовать не-U2F метод в качестве бэкапа (OTP, коды восстановления). Честно говоря, оба этих метода оставляют желать лучшего.
Это означает, что каждый раз при регистрации на каком-нибудь новом сервисе, мне нужно добавить оба моих токена. Это обстоятельство порождает ряд проблем:
На мой взгляд, эта «образцовая практика» не очень надежна, и довольно обременительна. Давайте посмотрим на другую распространенную практику.
OTP:
Коды восстановления:
Так что, подводя итоги, все эти методы неуниверсальны, обременительны и не слишком безопасны.
Теперь, после того как я достаточно раскритиковал существующее положение дел, я, наконец, скажу, что же я на самом деле хочу. Я действительно хочу иметь два U2F токена: основной и бэкапный, но они должны быть настроены определенным образом:
Прежде, чем мы обсудим техническую возможность этого в рамках U2F, я объясню почему это здорово, и как я это использую.
Если мы посмотрим на критику независимого бэкап-токена, изложенную выше, мы можем увидеть, что все недостатки этого метода оказываются устранены:
И если с основным токеном действительно случится что-то плохое, то я делаю следующее:
По крайней мере, для меня лично эта стратегия — отличный компромисс для высокого уровня безопасности и необременительного бэкапа. Это более безопасно и более надежно, чем любой другой метод.
Прежде чем мы можем говорить о реализации, мы должны на определенном уровне понимать, как работает U2F. Большинство производителей реализуют его следующим образом (не все из нижеприведенного присутствует в стандарте; некоторые вещи являются деталями реализаций, но большинство существующих реализаций, насколько мне известно, работает именно так):
В U2F-токене запрограммирован device_secret
, вместе с 32-битным counter
, который может быть только инкрементирован. Когда мы регистрируем U2F-токен на каком-либо сервисе, происходит следующее:
AppID
(фактически, доменное имя);nonce
), объединяет его с его с AppID
, пропускает все это через HMAC-SHA256 используя device_secret
в качестве ключа, и результирующий хеш становится приватным ключом для этого конкретного сервиса: service_private_key
;service_private_key
, генерируется публичный ключ service_public_key
;AppID
снова, объединяет его с service_private_key
, и снова пропускает через HMAC-SHA256 используя device_secret
в качестве ключа. Результат (MAC
), вместе с nonce
который был сгенерирован ранее, становится key_handle
;key_handle
и service_public_key
обратно браузеру, и браузер передает сервису, который сохраняет эти данные для будущих аутентификаций.Последующая аутентификация проходит следующим образом:
challenge
(случайно сгенерированные данные) и отправляет их браузеру вместе с key_handle
(который состоит из nonce
и MAC
). Браузер передает все это устройству, вместе с AppID
(т.е. доменным именем);nonce
и AppID
, генерирует service_private_key
тем же самым образом, которым он был сгенерирован при регистрации;MAC
тем же самым образом как и при регистрации, и сравнивая его с MAC
полученным от браузера, удостоверяется что nonce
не подменен, и следовательно, service_private_key
достоверен;counter
;challenge
, AppID
и counter
с помощью service_private_key
, и отправляет результирующую подпись (signature
) и counter
браузеру, который передает эти данные далее на сервис;signature
с помощью имеющегося у него после регистрации service_public_key
. Также, большинство сервисов проверяют, что counter
больше, чем предыдущее значение (если это не первая аутентификация). Цель этой проверки — сделать недоступным клонирование U2F-устройств. В итоге, если signature
совпадает и counter
больше, чем предыдущее значение, аутентификация считается успешно законченной, и сервис сохраняет новое значение counter
.Теперь давайте обозначим детали, которые имеют прямое отношение к дискуссии.
Первое — это то, что устройство не хранит service_private_key
для каждого сервиса: вместо этого, оно выводит service_private_key
каждый раз используя HMAC-SHA256. Это очень важно для нас: очевидно, если бы каждое устройство хранило бы уникальные ключи отдельно для каждого сервиса, то только это устройство могло бы осуществлять аутентификацию впоследствии.
Это, между прочем, не является требованием U2F: U2F не указывает, как именно ключи должны храниться, и некоторые ранние реализации U2F, действительно, хранили ключи для каждого сервиса в отдельности. Этот подход обладает тем недостатком, что количество сервисов, для которых устройство может быть использовано, ограничено. Деривация service_private_key
устраняет этот недостаток.
И второе — устройство имеет counter
, чтобы предотвратить клонирование.
На первый взгляд, может показаться, что этот counter
не позволяет нам реализовать обсуждаемую бэкап-стратегию (как минимум, мне так казалось, когда я пытался найти решение), однако на самом деле, он только помогает нам! Сейчас объясню.
Идея заключается в следующем: на этапе производства, запрограммировать два токена таким образом, что они оба имеют одинаковый device_secret
, но бэкап-токен нуждается в некоторой коррекции: вместо того, чтобы использовать counter
в чистом виде (как делают обычные токены), он должен добавить некоторую большую константу к counter
. Например, половина 32-битного диапазона, т.е. примерно 2 000 000 000
, выглядит разумно: я вряд ли исчерпаю такое количество аутентификаций за всю жизнь.
Фактически, это все. Просто и эффективно.
Имея два токена запрограммированных таким образом, я прячу бэкап-токен в какое-нибудь реально труднодоступное место, и никогда его не трогаю. Если что-то ужасное случается и я утрачиваю доступ к основному токену, я таки добираюсь до бэкап-токена, и сразу могу использовать его на всех сервисах, где я регистрировал основной токен, т.к. бэкап имеет тот же самый device_secret
, и его counter
начинается с реально большого числа, до которого я не доберусь на протяжение всей жизни.
Также, обращаю внимание, что я не предлагаю делать токены клонируемыми. Два токена, хотя и обладают одинаковым device_secret
, имеют разные счетчики, и после программирования device_secret
не должно быть способа получить его обратно из устройства или каким-либо другим образом создать клон.
Внимательный читатель может заметить, что имеется следующая проблема безопасности: что если злоумышленник получает доступ к основному токену и как-либо инициирует 2 000 000 000 аутентификаций? Тогда он получает доступ к сервису даже после того, как бэкап-токен был использован на этом сервисе.
К счастью, эта проблема имеет простое решение. В любом случае, счетчик должен быть реализован аппаратно (предположительно, на некотором криптопроцессоре), и для безопасной реализации этот аппаратный счетчик должен иметь диапазон меньше, чем 32 бита. Например, на ATECC508A [1] счетчики могут считать только до 2097151, так что, устанавливая константу, добавляемую к счетчику, в любое значение большее чем максимальное значение счетчика, мы можем быть уверены, что основной токен никогда не сможет досчитать до счетчика в бэкап-токене.
Для пояснения: допустим, что на нашем U2F-токене используется ATECC508A, и обозначим счетчик внутри ATECC508A как hw_counter
. Тогда:
hw_counter
;hw_counter + 2000000000
.Обратите внимание, что мы не модифицируем реальный hw_counter
внутри криптопроцессора; он по-прежнему будет считать от 0 до 2097151. Вместо этого, каждый раз когда нам нужно получить значение счетчика, мы считываем hw_counter
из ATECC508A, потом добавляем нашу константу, и возвращаем (для дальнейших вычислений для U2F).
Таким образом, диапазон значений счетчика в основном токене будет [0, 2097151], тогда как диапазон значение счетчика в бэкап-токене будет [2000000000, 2002097151]. Тот факт, что эти диапазоны не пересекаются, обеспечивает аннулирование основного токена при использовании бэкапа (если сервис использует counter
; основные сервисы, которые я проверил, используют его).
Ни один из производителей U2F-токенов, о которых я знаю, не поддерживает требуемой кастомизации на сегодняшний день. Но к счастью, существует open-source реализация U2F-токена: SoloKeys [2].
Я писал свою оригинальную статью (на англ) год назад, и эта часть несколько устарела: тогда SoloKeys был на стадии прототипирования, а я использовал предыдущую итерацию проекта: u2f-zero [3]. Поэтому переводить эту часть я сейчас не буду, поскольку единственный способ получить u2f-zero устройство — это спаять его самостоятельно, и заниматься этим вряд ли целесообразно (хотя на гитхабе есть инструкции).
Тем не менее, все подробности необходимой модификaции u2f-zero приведены в моей оригинальной статье на англ [4].
Когда дойдут руки до solokeys, я напишу и инструкцию по его модификации.
Так или иначе, это — единственный известный мне на сегодня способ получить рабочий U2F-токен с надежным бэкапом. Проверка нескольких сервисов (как минимум google и github) показала, что он работает: зарегистрировав основной токен на сервисе, мы можем также использовать бэкап, и после первого использования бэкапа, основной токен перестает работать. Awwwwwww. <3
Несмотря на то, что эта бэкап-стратегия крута, я не настолько уверен в ее конкретной реализации, посредством u2f-zero или solokey. Этот путь — единственный способ получить желаемое, так что этим путем я и пошел; но если предположить, что злоумышленник получает физический доступ к U2F-устройству, я не уверен, что взлом устройства (т.е. получение device_secret
из него) будет настолько же сложным, каким бы он был в случае Yubikey или других основных производителей. Авторы solokey заявляют, что «уровень безопасности такой же, как в современном автомобильном ключе», но я не проводил экспертиз чтобы это подтвердить.
Тем не менее, честно говоря, я не очень об этом беспокоюсь. Если злоумышленник просто крадет токен без намерения его вернуть, то тогда сложность его взлома не играет роли, т.к. злоумышленник может просто использовать этот токен для доступа в аккаунт и, например, просто отозвать этот токен и добавить другой. Однако, для этого я должен иметь и другие серьезные проблемы безопасности, т.к. U2F-токен — это только второй фактор.
Так что, единственный сценарий, при котором solokey может быть менее безопасным, чем что-то другое — это когда злоумышленник пытается получить доступ к устройству в течение краткого промежутка времени, получить device_secret
из него, и вернуть устройство обратно, незаметно для меня. Для этого, ему необходимо прочитать содержимое флеши микроконтроллере (или RAM в правильный момент), и это не очень тривиально.
Принимая во внимание все факторы, я считаю, что для меня лично иметь надежный бэкап — это гораздо более важно, чем иметь сверхбезопасную аппаратную реализацию U2F-устройства. Вероятность проблем с такой безопасной реализацией и отсутствием хорошего бэкапа выше, чем вероятность проблем с u2f-zero (solokey) и бэкапом.
Рассмотренная стратегия бэкапа выигрывает у альтернатив на всех измерениях: она универсальна, более безопасна и более надежна, чем любые другие способы.
Я буду рад, если хотя бы один из основных производителей реализует это в своих продуктах, но уверенности пока нет. Один парень из поддержки Yubico, James A., даже сказал мне, что реализовать бэкап так, как мне нужно, «is not possible with the way U2F is designed», и после того, как я изложил детали реализации, он просто перестал отвечать.
К счастью, это оказалось не настолько невозможно, насколько считает Yubico.
Автор: dimonomid
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/open-source/335296
Ссылки в тексте:
[1] ATECC508A: http://ww1.microchip.com/downloads/en/DeviceDoc/20005927A.pdf
[2] SoloKeys: https://solokeys.com/
[3] u2f-zero: https://github.com/conorpp/u2f-zero
[4] моей оригинальной статье на англ: https://dmitryfrank.com/articles/backup_u2f_token#actual_implementation
[5] Reliable, Secure and Universal Backup for U2F Token: https://dmitryfrank.com/articles/backup_u2f_token
[6] Источник: https://habr.com/ru/post/474300/?utm_source=habrahabr&utm_medium=rss&utm_campaign=474300
Нажмите здесь для печати.