Пользователь
0,0
рейтинг
9 октября 2012 в 20:22

Разработка → Как работают одноразовые пароли tutorial

Вступление


Как показывает практика, существует определенное непонимание принципов работы одноразовых паролей (это те самые, которые используются в GMail, в спец. токенах платежных систем и так далее).

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

Хэш-функция


Хэш-функция позволяет взять любые данные любой длины и построить по ним короткий «цифровой отпечаток пальца». Длина значения хэш-функции не зависит от длины исходного текста; например, в случае популярного алгоритма SHA-1 длина этого отпечатка составляет 160 бит.

Чтобы понять, почему значение всегда имеет одинаковую длину и не зависит от исходного текста, можно упрощенно представить хэш-функцию в виде кодового замка с колесиками. Вначале мы выставляем все колесики в «ноль», затем идем по тексту и для каждой буквы прокручиваем колесики в соответствии с некоторыми правилами. То число, которое окажется на замке в конце, и есть значение хэш-функции. Примерами таких функций являются MD5, SHA-1, ГОСТ_Р_34.11-94.

Не придумывайте свои хэш-функции, используйте стандартные реализации (например, в случае Python):

import hashlib
print hashlib.sha1("Hello, Bob!").hexdigest()

Результат: 88192e3e2e83243887410897efd90287b8e453a7

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

import hashlib
print hashlib.sha1("Hello, Bob?").hexdigest()

Результат: cbad4b0e05703acf2e8572be7438830fe7f8ddf5


В связи с этим возникает естественное желание использовать хэш-функцию для контроля целостности сообщений, которые Алиса посылает Бобу: Алиса подсчитывает для каждого своего сообщения значение SHA-1 и вкладывает его в конверт; Боб, самостоятельно подсчитав SHA-1 текста, может сравнить свой результат с Алисиным и удостовериться, что сообщение не было изменено где-то по дороге.

Однако мы забыли о Меллори, который находится где-то между Алисой и Бобом, перехватывает их переписку и вскрывает конверты! Он вполне может изменить сообщение, после чего подсчитать для него SHA-1 и приложить к письму; Боб сверит значения и ничего не заметит.

Проверка подлинности


Подумав, Алиса и Боб при встрече договариваются, что при подсчете SHA-1 они будут временно дописывать к тексту секретное слово, например, «Secret» (конечно, в реальности Алиса и Боб решили использовать куда более длинное слово, чтобы его было сложно подобрать). Меллори не знает это слово, а следовательно, даже если изменит сообщение, то не сможет скорректировать его хэш, не так ли?

Пример:

import hashlib
print hashlib.sha1("Secret" + "Hello, Bob").hexdigest()

Результат: 99beeff3ef1971d2cb1be129f986739f6bcba8cc

К сожалению, тут есть проблемы. Да, Меллори не может изменить тело сообщения, но (раз он знает хэш от текущего текста) он всегда может дописать в конце «P.S. На самом деле все это чушь, нам пора расстаться, Боб» и просто досчитать хэш от остатка (вспомним аналогию с кодовым замком).

Чтобы защититься от этого, мы немного усложним нашу функцию:

print hashlib.sha1("Secret" + hashlib.sha1("Hello, Bob").hexdigest()).hexdigest()

Результат: 3f51e9fc540676bc3ce54367fd3e467f3299c743

Теперь дописывание чего-либо в конец сообщения полностью изменит исходные данные для «внешнего» вызова SHA-1 и Меллори остается вне игры.

Алиса и Боб только что придумали то, что называется HMAC (или hash-based message authentication code): основанный на хэш-функции код проверки подлинности сообщений. В реальности HMAC, принятый как стандарт RFC2104 выглядит чуть-чуть сложнее за счет выравнивания длины ключа, пары XOR'ов внутри, участия ключа во «внутреннем» хэше, но суть не меняется.

Не придумывайте свои реализации HMAC, используйте стандартные реализации, например, HMAC-SHA1:

import hmac
import hashlib
print hmac.new(key="Secret", msg="Hello, Bob", digestmod=hashlib.sha1).hexdigest()

Одноразовые пароли


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

Как можно было бы реализовать эту схему? Например, Алиса может сгененировать сотню случайных паролей и отдать копию Бобу. Когда Боб позвонит в следующий раз, он продиктует самый верхний пароль в списке, Алиса сверит его со своим, после чего оба вычеркнут его. При следующем звонке они используют очередной пароль и так далее, пока они не закончатся. Это не очень удобно: хранение списков, генерация новых паролей и так далее.

Лучше реализовать эту схему в виде алгоритма. Например, паролем является его номер по порядку, умноженный на секретное число. Пусть Алиса и Боб договорились, что секретным числом является 42; тогда первым паролем будет 42, вторым 84, третьим 126 и так далее. Меллори, не знающий алгоритма и секретного числа, никогда не догадается, какой пароль будет следующим!

Конечно, алгоритм лучше выбрать посложнее. Алиса вспоминает про HMAC и предлагает Бобу считать пароль номер N по формуле: HMAC(«Secret», номер-пароля). После этого им нужно договориться о ключе (в данном случае это «Secret»), зато потом Бобу нужно только помнить, какой по счету пароль он генерирует (например, двадцатый):

import hmac
import hashlib
print hmac.new(key="Secret", msg="20", digestmod=hashlib.sha1).hexdigest()

Результат: 393e9efaae1a687bc2dcc257c8e9e2a61f26fe4b

Впрочем, Бобу совсем не улыбается каждый раз диктовать такой длинный пароль. Они с Алисой договариваются, что будут использовать только его часть, например, последние 6 символов.

Некоторое время все идет хорошо. До момента, пока Бобу и Алисе не надоедает вести подсчет, какой по счету пароль они используют. Кто-то подсказывает им, что в качестве аргумента HMAC() вместо номера можно использовать все, к чему Алиса и Боб имеют одновременный доступ… например, текущее время!

Наши герои синхронизируют свои часы и договариваются, что будут в качестве аргумента HMAC() использовать unix time — количество секунд, прошедших с момента наступления эпохи UNIX (в UTC). Чтобы вводить пароль не торопясь, они решают разделить время на 30 секундные «окна»; таким образом, на протяжении 30 секунд действует один и тот же пароль. Естественно, Алиса, проверяющая пароли, в течение 30 секунд не позволяет использовать пароль повторно (просто запоминая его) и тем самым оставляет его по-настоящему «одноразовым».

Теперь пароль вычисляется по следующей формуле: HMAC(«Secret», unix_timestamp / 30).

Мы получили одноразовые пароли на основе текущего времени. Сгенерировать и проверить эти пароли может только тот, кто обладает ключом («Secret» в примере выше); иначе говоря, сервер и пользователь.

Следует отметить, что одноразовые пароли могут считаться и по другим алгоритмам; главное, чтобы алгоритм и секрет были известны обеим сторонам. Но т.к. у нас есть стандарт, дальше мы будем говорить именно о нем

OATH, TOTP, HOTP, RFC… WTF?


Итак, мы только что описали основные идеи, лежащие в основе:

1) HMAC, hash-based message authentication code: RFC2104
2) HOTP, hash-based one-time password: RFC4226
3) TOTP, time-based one-time password: RFC6238

Эти идеи — один из краеугольных камней инициативы Initiative For Open Authentication (OATH), направленной на стандартизацию методов аутентификации.

Двухэтапная аутентификация Google


Одноразовые пароли, основанные на времени (и подсчитываемые на основе алгоритма TOTP RFC 6238) используются также компанией Google в приложении Google Authenticator, которое можно установить на iOS, Android, BlackBerry. Это приложение автоматически генерирует одноразовые пароли раз в 30 секунд (в дополнение к основному паролю на Google Account). Это означает, что даже если Ваш основной пароль кто-то подглядит или перехватит, без очередного одноразового пароля в систему войти будет невозможно. Удобно.

ВНИМАНИЕ: Я НЕ НЕСУ НИКАКОЙ ОТВЕТСТВЕННОСТИ ЗА ВАШИ ДЕЙСТВИЯ С ВКЛЮЧЕНИЕМ И ВЫКЛЮЧЕНИЕМ ДВУХЭТАПНОЙ АУТЕНТИФИКАЦИИ GOOGLE; ВЫ СОГЛАСНЫ, ЧТО ВЫ ВЫПОЛНЯЕТЕ ИХ НА СВОЙ СТРАХ И РИСК.

На самом деле там нет ничего страшного (есть инструкции, есть trusted-компьютеры, есть резервные коды и т.д.), но если от души постараться, бездумно нажимая на кнопки, то вполне можно лишиться доступа к своему аккаунту. И все же: если не готовы экспериментировать, не трогайте Gmail; просто скачайте приложение Google Authenticator на телефон, вручную добавьте «ключ по времени» (например, «a abc def abc def abc» или просто отсканируйте QR-код ниже).

Для начала нам нужно получить секретный ключ, который используется для создания одноразового пароля. Его можно посмотреть на странице добавления Google Authenticator'а в настройках аккаунта, он находится под QR-кодом:



Обратите внимание, что если двухэтапная аутентификация уже включена, то старый ключ узнать нельзя: придется удалить старый и сгенерировать новый. Это несложно; главное, не забыть сразу обновить ключ и в Google Authenticator, если Вы им пользуетесь.

Ключ закодирован в Base32 (для удобства уберите пробелы и переведите буквы в верхний регистр).

Программа, которая подсчитывает текущий одноразовый пароль:

import time
import hmac
import hashlib
import base64

### TOTP-key (get it from Google)
secret = base64.b32decode("AABCDEFABCDEFABC")

### Calc counter from UNIX time (see RFC6238) 
counter = long(time.time() / 30)

### Use counter as 8 byte array
bytes=bytearray()
for i in reversed(range(0, 8)):
  bytes.insert(0, counter & 0xff)
  counter >>= 8

### Calculate HMAC-SHA1(secret, counter)
hs = bytearray(hmac.new(secret, bytes, hashlib.sha1).digest())

### Truncate result (see RFC4226)
n = hs[-1] & 0xF
result = (hs[n] << 24 | hs[n+1] << 16 | hs[n+2] << 8 | hs[n+3]) & 0x7fffffff

### Print last 6 digits
print str(result)[-6:]

Теперь можно положить рядом запущенный Google Authenticator и сравнить значения.

upd: пример реализации на JavaScript (спасибо roman_pro)

upd #2: если хотите поиграть с 2-step verification от dropbox, в конце секрета допишите "======" (это необходимо для паддинга и корректной работы base32-декодера).

Выводы


  1. Одноразовые пароли — это несложно.
  2. Стандарты — это хорошо.
  3. При желании их можно встроить и в свое приложение (используйте готовые библиотеки; например, из исходников того же Google Authenticator'a).
Вадим Колонцов @vk2
карма
98,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (72)

  • 0
    Спасибо, очень интересно и понятно.
    К сожалению, тут есть проблемы. Да, Меллори не может изменить тело сообщения, но (раз он знает хэш от текущего текста) он всегда может дописать в конце «P.S. На самом деле все это чушь, нам пора расстаться, Боб» и просто досчитать хэш от остатка (вспомним аналогию с кодовым замком).

    А что если добавлять секрет в конец?
    • +5
      Тут другая проблема, уже немного глубже. Если мы имеем просто hash(msg + key), то в случае нахождения коллизии в функции hash, позволяющей найти msg2, для которого hash(msg) == hash(msg2), то окажется, что HMAC тоже скомпроментирован, ибо hash(msg2 + key) == hash(msg + key). Двойное хэширование позволяет этого избежать (а если посмотреть стандарт HMAC, то там внутри все чуть сложнее, чем я писал,.к. ключ задействован как во «внутреннем» хэше, так и во «внешнем», см. вики по ссылкам). В целом считается, что HMAC значительно более устойчив к атакам на поиск коллизий, чем хэш-функция, которая внутри него используется.
      • +3
        Поясните подробнее, пожалуйста.
        Не понимаю, почему
        >он всегда может дописать в конце «P.S. На самом деле все это чушь, нам пора расстаться, Боб» и просто досчитать хэш от остатка
        Как досчитать верный хэш, не зная секрета?
        • +4
          А что хэшу этот секрет? Это всего лишь кусок входных данных. Для хэш-функции все равно, данные это или секрет.

          Грубо говоря, у «движка» MD5, SHA1 etc есть понятие «внутреннего состояния» (которое в случае Си хранится в структуре типа SHA_CTX, в случае Python в объекте и т.д.). Когда мы хотим взять хэш, мы сначала выставляем колесики на кодовом замке из моего примера в условный «ноль» (в реальности там стандартные константы), затем берем из входного потока данные и в соответствии с ними крутим-вертим колесики (меняем «внутреннее состояние»). Когда данные кончились, мы просто берем получившиеся цифры на колесиках («внутреннее состояние») и печатаем их — это и есть результат хэш-функции.

          Но кто мешает мне выставить при инициализации колесики (слово дурацкое) не в «ноль», а в иное состояние, которое соответствует уже подсчитанному хэшу? Никто. После этого я продолжаю их крутить в соответствии с оставшимися данными, и в итоге получаю хэш, где учтен и секрет, и первоначальное сообщение, и то, что я добавил.

          Посмотрите псевдокод SHA-1. Я все упростил, т.к. есть понятие 512-битных входных блоков и паддинга, но в целом все так.
          • 0
            хэш-значение сообщения изменится, если его части переставить местами
            • 0
              Здесь нет переставления местами, HeadFore задает вопрос о статье, а не о том, о чем спрашивал mhspace.
              • 0
                здесь есть переставление местами. Вы говорите «учтен и секрет и первоначальное сообщение и то, что я добавил», как будто этого достаточно. Между тем важен так же порядок. Если они будут учтены не в том порядке, такую атаку сразу заметят
                • 0
                  Где переставление?

                  hash(secret + msg)
                  hash(secret + msg + hello_from_mallory).
                  • 0
                    переставление будет тогда, когда секрет добавляется в конце, с чего и начался вопрос
                    • +1
                      Да я Вас понимаю :) Вопрос действительно начался с переставления, но в следующем вопросе от другого автора уже прозвучала фраза про «возможность досчета хэша» с цитатой из статьи, так что я отвечал ему как на новый вопрос. Если это не следовало явно из треда — то это моя вина, не сделал это понятным.
                      • 0
                        ну, до истины докопались — и ладно. Пишите еще :)
                • 0
                  (про «порядок» я согласен, но в примере из статьи порядок как раз не меняется)
          • 0
            Всё, ясно, спасибо. А есть хэш-функции без внутреннего состояния, стойкие к такому виду атаки? (про спасение добавлением хэша от сообщения я понял, просто интересно)
            • 0
              Да, конечно. Тут ведь идея в том, что по хэшу мы можем восстановить внутреннее состояние и продолжить считать хэш. Так давайте не будем включать в хэш внутреннее состояние полностью! Так, например, работает SHA-384 (очевидно, что если в итоговом хэше отсутствуют ряд внутренних переменных, то взломщику и взять их неоткуда). Но я не криптоаналитик, это большая и сложная тема.
        • 0
          Никак (ну, т.е. никак, если не иметь под рукой нужной коллизии), гражданин напутал. В т.ч. и с терминами — называть эти вещи электронной подписью некорректно
          • 0
            Про коллизии см выше, а что касается подписи… Подпись позволяет проверить искажение информации и быть уверенным в авторстве документа. В данном случае приведен пример примитивной, но все же подписи.

            • 0
              отличительная особенность подписи — авторство и неотказуемость. Подпись асимметрична и «индивидуальна». У вас же схема с симметричным секретом.
              • 0
                С этим не спорю. Давайте поставлю оговорку и кавычки… Это единственное место в статье, где мельком упомянута подпись, просто к слову, т.к. HMAC здесь не для этого. Цели рассказать на пальцах о цифровых подписях не было, как Вы понимаете. Ну да не будем вводить в заблуждение, Вы правы.
        • НЛО прилетело и опубликовало эту надпись здесь
          • НЛО прилетело и опубликовало эту надпись здесь
          • +1
            Вторая серия :)
            Прочитайте внимательно; выше я отвечаю на два разных вопроса. Упомянутая Вами атака относится к случаю hash(key + message), о ней идет речь в статье и в вопросе HeadFore. В вопросе же mhspace речь идет о hash(message + key), где возможная атака идет совершенно иным образом (и да, там в тему коллизии). Но все равно спасибо за комментарий.
            • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    Спасибо, как то думал сам разобраться с этим механизмом но все руки не доходили. Насчет использования текущего времени в Google Authenticator, если в телефоне часы отстают то коды будут неверными, или программа берет данные о unix time из сети, а не из системных часов?
    • 0
      Берет из системных часов. Если время сильно рассинхронизировано, могут быть проблемы. Но телефоны обычно синхронизируются с операторами связи и т.д.
      • 0
        Вот как раз у операторов часики могут врать, Мегафон например — целых 2 минуты разницы.
        • 0
          Мне кажется, что две минуты — это нереально много. Это нормально для Мегафона?

          Конечно, сервер может на всякий случай принимать значения из предыдущего и следующего окна… Я не очень внимательно читал тот же PAM-модуль от Google, но там, кажется, есть фишка «подстройки» под сдвиг времени у пользователя (когда ясно, что пароли генерируются верные, просто с опережением или с запозданием).
          • +1
            Нормально или нет, не знаю, но я отказался от синхронизации с оператором.
            За стать спасибо, интересно, после прочтения стало понятно, почему Альфа-ключ у одноименного банка работает через раз.
      • 0
        Кажется, последнии версии Google Authenticator имеют свою систему синхронизации времени. Народ на нее ругается. Наверное, у них нет Мегафона :)
    • +1
      В Google Authenticator есть функция синхронизации времени с интернетом. Если часы идут не правильно — то верно, коды генерируемые — не валидны. В любом случае в случае с Google, в случае затруднения с Google Authenticator приложением, можно запросить гугл прислать код по SMS на указанный ранее номер телефона. (кто не указал — ССЗБ).
      • 0
        Кстати, если я не ошибаюсь, то в iOS они почему-то синхронизацию времени не встроили.
        • 0
          В iOS есть опция «Set Automatically» в «Date & Time». Насколько я понимаю, это как раз автоматическая синхронизация времени.
          Я не вникал в подробности, но думаю что это синхронизация через интернет, а не через оператора, что в данном случае влиять не должно.
          • 0
            Из фразы «You may be able to set the date, time, and time zone automatically if this is supported by your wireless carrier… If the date, time, or time zone is incorrect and Set Automatically is already on, please notify your cellular provider (например, тут) я всегда делал вывод, что синхронизация идет не через NTP, а через сотового оператора. В принципе, логично.
            • 0
              Интересная выписка. Похоже, они и так и сяк могут. Возможно, провайдер просто имеет более высокий приоритет.
              У меня есть iPhone, который не подключен ни к какому провайдеру. Только что попробовал сменить время на неправильное, но оно выставилось обратно на правильное как только я поставил «Set Automatically. Подключен был только через WiFi.
              А ведь есть еще iPad без модема и iPod Touch. Там, походу, только через WiFi это и делается.
              • 0
                Про автосинхронизацию времени на iPod Touch — поймали. Я думал, там просто нет этого пункта, посмотрел на wi-fi ipad — таки-есть. Будет интересно понять, как это делается.

                (то, что все они умеют брать время с iTunes — это понятно).
  • +1
    Спасибо, очень доходчиво излагаете!

    /*… таким образом, на протяжении 30 секунд действует один и тот же пароль…
    теперь пароль вычисляется по следующей формуле: HMAC(«Secret», unix_timestamp / 30) */


    Чтобы соблюсти «окно», не следует ли использовать другое вычисление вычисление?

    unix_timestamp - unix_timestamp % 30

    Да и так оба могут оказаться в разных окнах…
    • 0
      Нам нужно, чтобы в течение 30 секунд мы подавали на вход функции одно и то же число. Проще всего использовать целочисленное деление. Вы его проимитировали через модуль :)
      • 0
        (ествественно, проимитировали не деление, но постоянное число; но в стандарте TOTP нужно именно делить)
  • +7
    По неподтвержденным данным, Алиса и Боб — самые популярные имена детей у криптографов
    • +1
      Для меня было сюрпризом, что в некоторых русскоязычных источниках реально используют имена Антон, Борис и… Зиновий (в роли злоумышленника).
  • 0
    Спасибо, статья очень занимательная.

    Т.к. автор говорит именно об авторизации, то и приложения являются сетевыми. А что если в качестве секретного ключа, использовать IP. Я сейчас говорю именно об авторизации.

    Хотел бы узнать мнение Хабровчан на счет системы, которую недавно написал.

    Немного кратко, есть десктопное приложение, которое аутентифицируется у сервера, передавая банальную пару логин-пароль.

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

    На клиенте узнается, какой его внешний IP, сервер при подключении уже тоже знает какой клиентский IP. Клиент создает хэш на основе пароля, ip и немного приправ (соли) и отправляет на сервер. Как вы могли заметить, на каждый IP хэш будет уникальным. Сервер находит в БД по логину запись и получает пароль, пароль хэшируется с такими-же параметрами и сравнивается. Даже если злоумышленник узнает хэш, он не сможет авторизироваться по банальной причине не совпадения хэша на сервере.

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

    Что Вы скажете о безопасности как со стороны перехвата, так и со стороны взлома сервера? Вообще стоит ли использовать такую вещицу?
    • 0
      Ох… Для начала:
      1. Как определить внешний IP адрес? А что, если ответ сервера будет перехвачен и подменен?
      2. Как быть с роуминг пользователями? Сейчас один IP, через час — другой.
      3. Как быть с прокси серверами, за которыми сидят сотни пользователей и внешний IP один на всех?
      • 0
        1) На клиенте нету никакой важной и закрытой информации, ее можно получить разобрав код (в моем случае, это десктопное приложение на JS в связке с node-webkit). Вся важная инфа на сервере, и чтоб получить к ней доступ, нужно для начала авторизироваться у этого сервера.

        Внешний IP определяется клиентом, с помощью обычного подключения к какому-то серверу (google, ваш сервер или вообще какой-то рандомный и рабочий) и вытаскивает с помощью сокетов адрес свой адрес. Вот как пример на NodeJS socket.address().address. По мотивам записи stackoverflow.com/questions/3653065/get-local-ip-address-in-node-js от пользователя Jimbly.

        2) Авторизация происходит один раз на одно туннельное соединение. Я не уверен, но кажется должен произойти разрыв соединения (работаем по сокетам), т.к. к серверу подключен клиент с одним адресом, а тут бац у него адрес изменился.

        3) Тут я тоже задумывался и пришел к выводу, что под этим хэшом можно авторизироваться любому пользователю, находящимся на одном IP адресе. Если это публичный и открытый прокси сервер, то тут уже нужно как-то говорить пользователю, что это может быть небезопасно, но и то не решение. Может чем-то подскажите?
        • 0
          Может чем-то подскажите?

          Да: не изобретайте своих алгоритмов аутентификации/шифрования :-) Всегда есть 100500 вариантов, о которых не подумали, но они могут скомпрометировать всю систему.

          Кстати, я не до конца понял: а зачем иметь возможность расшифровать пароль на стороне сервера? Ваша ведь цель использовть ip как соль, что мешает сделать классическим способом hash(ip + hash(password))?
          • 0
            Хммм. действительно, чего это я долбался с самим паролем, если можно просто взять его хэш.
      • 0
        Плюс серые и динамические IP.
    • 0
      OMG. Что мешало просто взять https, чтобы канал не был открытым?

      Ну и вообще в WS-Security все уже давно придумано.
  • 0
    прочитал заголовок: «Как работают однозначные пароли» )))
  • +1
    В целом это самые основы криптографии, но все равно интересно, спасибо.
  • 0
    опять эти Боб и Алиса, брр…
  • +1
    До кучки — тут есть наглядная реализация этого алгоритма на javascript
    • 0
      Да, вроде нормально написано :)
      Добавил на всякий случай ссылку под python-вариантом. Спасибо!
  • 0
    Лучше колесиков условная необратимость шифрование объясняется на примере производой некой функции, если мне не изменяет память. Если изменяет — поправьте пожалуйста.
  • 0
    Стоит, наверное, добавить, что на принимающей стороне такой одноразовый пароль надо проверять не только с учётом текущего 30-секундного интервала, но также предыдущего и следующего.

    Вот почему:
    1) Допустим, время у Алисы hh:mm:29 и counter получился равным xxx0. Пока она вычисляла хэши и пересылала пароль Бобу, у того уже наступило hh:mm:31. Соответственно, Боб получит значение counter = xxx1, и пароль Алисы окажется не верным.

    2) Допустим, часы у Боба отстают на 3 секунды. В hh:03:00 Алиса посчитала и отправила пароль. Боб же получил его, когда на его часах всё ещё hh:02:59. В этой ситуации его counter уже будет отставать на 1. И пароль снова окажется не верным.

    Фактически, это расширит «окно» действия пароля до 90 секунд.
    Чтобы снизить влияние этого фактора, можно ввести дополнительные проверки — учитывать предыдущий/следующий интервалы, только если текущее время близко к ним.
    Скажем, проверять предыдущий, если time.time() % 30 < 5, а следующий, если time.time() % 30 > 25.
    • 0
      Да, я чуть выше писал, что libpam от Google пытается вычислять «сбитое» время и параметр сдвига «skew», там как раз доп. проверки и все такое.

      Вы совершенно правы.
  • 0
    Раз уж в килобайте 1024 байта, то почему бы окно не взять в 32 секунды вместо 30? Делить все же легче
    • 0
      Мы же пишем для людей, а не для компьютеров :)
  • 0
    В случае, когда вместе с секретом используется последовательный номер звонка (пример из статьи) — этот номер не известен никому, кроме Боба и Алисы. Когда мы эту неизвестную заменяем временем, причем синхронизированным с общедоступным и точным источником, у нас одна из составляющих становится известной, причем в любой момент времени? Или я чего-то не понимаю. Если мы каким-то образом узнали секрет, какие проблемы будут со временем, тем более с точным, а не каким-то нашим, экслюзивно-неточным, но общим для Боба и Алисы (сервера и клиента).
    • 0
      Если секрет узнали — то проблем никаких. Поэтому его (секрет) нужно хранить и лелеять. Например, если его извлечь из Google Authenticator'а, то я смогу генерировать коды, все верно (ну, там для входа еще нужен пароль, но будем считать, что я его и так знал).
      • 0
        Тогда вся эта затея с одноразовыми паролями описанной реализации сводится к простой задаче, давно решенной другими способами — избежать передачи пароля в открытом виде + избежать повторного использования аутентификационных данных (что бы они из себя не представляли) повторно в случае перехвата. Та же старая-добрая digest аутентификация, в которой вместо времени — высылаемый сервером клиенту уникальный код. По-хорошему раскрытие секрета не должно приводить к ситуации «проблем никаких». Они, проблемы, должны быть для злоумышленника.
        "… вместо номера можно использовать все, к чему Алиса и Боб имеют одновременный доступ… например, текущее время!" Вот тут тонкость. Одно дело, когда используется то, к чему только Боб и Алиса имеют одновременный доступ, тогда утечка секрета еще не полный крах, я правильно понимаю? Это что-то еще надо скомпрометировать. А когда это время, да еще точное — оно доступно всем. Так что как-то хило. Для двухфакторной аутентификации например, утечка пароля не приводит к «проблем никаких». Ни секрет без токена, ни токен без секрета еще ничего не дают. Нужно и то и то. Когда токен — время, все как-то несколько профанируется.
        • 0
          Вот поэтому OTP и не (практически) используется как самостоятельная аутентификация, а (обычно) добавляется к уже существующей.
        • 0
          Вы сами ответили — «двухфакторная авторизация». Одноразовые пароли (например, с помощью токена или google authenticator'a) — лишь часть ее. Кроме того, в случае токенов с клиента почти невозможно вытащить секрет (хотя возможна компроментация сервера, конечно). Да и гонять стоит по SSL, чтобы уменьшить вероятность перехвата. Главное, что одноразовые пароли, которые генерируются НЕ НА ТОМ КОМПЬЮТЕРЕ, который используется для ввода пароля, реально уменьшают вероятность перехвата. Кроме того, в случае TOTP, мы передаем не полный хэш, а лишь выборку из 6 цифр, а значит, атакующему при брутфорсе придется перепробовать очень большое количество хэшей, в которых эти цифры есть.
  • 0
    Похоже на digest аутентификацию, где в качестве nonces — время. Только nonces еще надо перехватить, этому может препятствовать защищенное соединение. А время — оно всем известно. Это к тому, что если будет перехвачено множество атунтификаций для анализа, одна из составляющих — время, будет известно.
    • 0
      Перехватите хоть миллион SHA-1 кодов, если Вы не знаете, что там внутри, как Вы сможете это понять? Я могу хэшировать произведения Ленина, а могу анекдоты про Рабиновича. Тот факт, что я дописывал к ним время, никак не поможет Вам узнать секрет.
      • 0
        «Теперь пароль вычисляется по следующей формуле: HMAC(«Secret», unix_timestamp / 30).»

        Не вижу тут места для произведений Ленина или анекдотов Рабиновича :)
        Если я наперехватывал достаточное кол-во подобных HMAC, в которых секрет мне неизвестен (но он постоянен), а время/30 — известно и соответствует времени перехвата, это меня нисколько не приближает к раскрытию секрета? Я не утверждаю, спрашиваю вас как специалиста. Мне, как неспециалисту, это напоминает ситуацию со взломом WEP, время/30 — отдаленно напоминает вектор инициализации. Там он в открытом виде, тут формально нет, но реально при перехвате известен.
        Думаю, что если действительно перехватить миллион таких HMAC, где вторая составляющая формулы будет известна (unix_timestamp / 30) — поиск секрета это несколько облегчит, нет?
        • 0
          Да, это не приближает к раскрытию секрета. Дело в том, что по внешнему виду результата хэш-функции нельзя даже предположить, от какого документа она взята — от «Войны и мира», фразы «Секрет» или от дистрибутива Ubuntu. Кроме того, хэш функция обладает лавинным эффектом — даже от смены одного бита результат меняется полностью. А здесь она вообще используется два раза (hash(key ^ pad1 + hash(key ^ pad2 + message)).

          Поэтому знание того, что где-то в аргументе хэш-функции было число «123456» никак не поможет Вам восстановить остальное (в данном случае секрет). Размер секрета, кстати, неограничен.
          • 0
            1. Перехватывая трафик я как бы провожу целенаправленную атаку и знаю, что я перехватываю.
            2. Поможет. Мне облегчается брутфорс. Если бы я не знал unix_timestamp / 30, мне бы пришлось брутфорсить и этот аргумент, когда же я его знаю — брут только секрета, если он короткий и простой (это редкость разве?) задача вполне себе выполнима. Особенно если у меня ботнет с мощными видюхами. Да даже просто — ботнет (или корпоративная ЛВС средних размеров :)).
          • 0
            «Размер секрета, кстати, неограничен» — это понятно, но много ли народа сочинение Ленина будет использовать в кач-ве секрета? Склонность пользователей к простым паролям хорошо известна. В случае, когда «123456» нам неизвестно, да еще к тому же это не «123456» а нечто более разрядное — брут гораздо менее реален, даже при коротком и простом секрете.
            А вообще я не спорю, просто слабым местом статьи мне кажется именно то, что сначала на примерах Боба и Алисы даются общие принципы, а потом приводится реализация со временем, как нечто на практике эти принципы воплощающее. Про Боба и Алису было сказано — нечто доступное только им, для временной же реализации ни гу-гу про то, что это только не выполнено, что упрощает ситуацию, делает метод гораздо менее защищенным и ограничивает его применение.
          • 0
            ru.wikipedia.org/wiki/HMAC#.D0.91.D0.B5.D0.B7.D0.BE.D0.BF.D0.B0.D1.81.D0.BD.D0.BE.D1.81.D1.82.D1.8C

            «Так как злоумышленник знает алгоритм хеширования и начальные условия, злоумышленник может создать хэш-код для каждого из сообщений. Однако, при атаке HMAC, злоумышленник не сможет генерировать пару :(сообщение — код) в удаленном(оффлайн) режиме, так как злоумышленник не знает Key. Таким образом, злоумышленник должен следить за последовательностью сообщений, порождаемых HMAC с тем же ключом и выполнять атаку на них. Для хэш-кода длиной 128 бит, для этого требуется 2^{64} блоков или 2^{72} битов сгенерированных с помощью того же ключа. Для 1-Гбит соединения, нужно было бы следить за потоком сообщений, если он не изменят Key, в течение 150 000 лет, чтобы добиться успеха. Таким образом, если скорость имеет значение, это вполне приемлемо использовать MD5, а не SHA-1 в качестве встроенных хэш-функции для HMAC.»

            150 000 лет это конечно очень много, да тем более HMAC должны генерится с такой скоростью большой. Но чисто формально доверие к фразе «Перехватите хоть миллион SHA-1 кодов» падает. Миллиона не хватит, а 10^в какой-то большой степени — да. Хотя это уже не из разряда практики.
            • 0
              Вы меня обогнали с ответом.
              В принципе, да, возможен брутфорс.
              Но слишком много возможных вариантов для перебора.
              Да и потом, мы говорили про использование HMAC для одноразовых паролей, здесь такого трафика просто не будет.
  • 0
    Написал 2FA-приложение для Pebble (https://getpebble.com), скоро будет новая статья )
  • 0
    Отличная статья, написанная языком для простых людей. А есть немного более сложный вариант для одноразовых паролей, чтобы был не общий секрет, а пара public\private? Меньше всего хотелось бы чтобы злоумышленник после разового взлома ресурса получил доступ к секрету. Понятно что ресурсам типа google.com это не грозит, но есть и более простые

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.