В статье разобрано 3 реализованных в коде механизма авторизации узла Пандоры: хэш-загадке, которая ограждает от DoS-атак, электронной подписи, которая идентифицирует узел-собеседник, и картинке-загадке, которая отсеивает спамеров и ботов.
Речь пойдёт о бан-листе, системе штрафов, а также ускоренной авторизации по сеансовому ключу после обрыва связи. Для начала взглянем на общую диаграмму сеанса связи:
При авторизации охотник, как инициатор соединения, подвергается большему числу проверок, нежели слушатель. Стадии авторизации проходят под диктовку слушателя, а охотник должен строго отвечать на запросы слушателя. На этапе авторизации идёт синхронный обмен.
Сеанс начинается с фразы приветствия охотника:
hello = {:version=>0, :mode=>0, :mykey=>my_key, :tokey=>out_key, :addr=>my_callback}
В которой присутствует версия протокола, опции обмена, панхэш ключа на этом узле, панхэш ключа на том узле, ip-адрес для обратного подключения.
Слушатель проверяет приветствие. Если параметры приветствия допустимы (протокол согласован, режимы поддерживаются, панхэш ключа охотника не значится, как имеющий отрицательное доверие), то можно переходить к шагам авторизации.
Для этого слушатель генерирует случайную 256-символьную фразу, в которой последний байт заменяется на длину в битах требуемой «отгадки», например, 14 бит.
Задача охотника – добавить такое число к фразе, первые 14 бит суммарного хэша sha1 которого совпадут с фразой-загадкой. В виде формулы можно выразить так:
F & M = sha1(F+A) & M,
где F – фраза,
M – битовая маска требуемой длины, например 011111111111111b,
A – блок (число), который необходимо найти, чтобы выполнилось тождество.
Так как аналитического решения у уравнения с хэшем нет, то охотнику приходится перебором подставлять числа и высчитывать хэш для каждого блока (фраза+число) до тех пор, пока решение не будет найдено. Время поиска зависит от требуемой длины маски и удачи. При 14 битах на среднем ноутбуке среднее время поиска равняется около 2 секунд (иногда больше, иногда меньше). Вы можете увеличить или уменьшить размер маски, регулируя этим самым требовательность к охотнику.
Также в предпоследнем байте фразы задаётся минимальное время задержки, которое охотник должен ждать, если нашёл отгадку слишком быстро.
По умолчанию в Пандоре заданы такие параметры:
puzzle_bit_length = 14
puzzle_sec_delay = 2
Если охотник честно отработал и нашёл отгадку, а слушатель проверил отгадку, то авторизация переходит на следующий этап. Если охотник отказывается выполнять работу или присылает неверную отгадку, то штрафуется добавлением во временный бан-лист. Вот часть кода, которая отвечает за генерацию загадки, нахождение (простым перебором) и проверку отгадки:
# Generate random phrase
# RU: Сгенерировать случайную фразу
def get_sphrase(init=false)
phrase = params['sphrase'] if not init
if init or (not phrase)
phrase = OpenSSL::Random.random_bytes(256)
params['sphrase'] = phrase
init = true
end
[phrase, init]
end
phrase, init = get_sphrase(true)
phrase[-1] = puzzle_bit_length.chr
phrase[-2] = puzzle_sec_delay.chr
# Find sha1-solution
# RU: Находит sha1-загадку
def self.find_sha1_solution(phrase)
res = nil
lenbit = phrase[phrase.size-1].ord
len = lenbit/8
puzzle = phrase[0, len]
tailbyte = nil
drift = lenbit - len*8
if drift>0
tailmask = 0xFF >> (8-drift)
tailbyte = (phrase[len].ord & tailmask) if tailmask>0
end
i = 0
while (not res) and (i<0xffffffff -="" 1="" add="" and="" check="" def="" drift="" end="" hash="" i="" if="" len="" lenbit="phrase[phrase.size-1].ord" not="" offer="=puzzle)" or="" ord="" phrase="" puzzle="phrase[0," res="false" ru:="" self.check_sha1_solution="" sha1-="" sha1-solution="" tailbyte="nil" tailmask="">0
tailmask = 0xFF >> (8-drift)
tailbyte = (phrase[len].ord & tailmask) if tailmask>0
end
hash = Digest::SHA1.digest(phrase+add)
offer = hash[0, len]
if (offer==puzzle) and ((not tailbyte) or ((hash[len].ord & tailmask)==tailbyte))
res = true
end
res
end
Заметьте, затраты на проверку через единственный вызов хэш-функции требуют гораздо меньше вычислительных ресурсов, чем многократный вызов хэш-функций для поиска отгадки. Но это в «нормальном» режиме.
Охотник-злоумышленник может подсунуть левую отгадку, желая нагрузить слушателя. В этом случае ресурсные потери слушателя будут больше, чем у охотника. Но важно понимать, что потери на генерацию фразы (которую, кстати, можно генерять не чаще одной в минуту для всех) и одиночный вызов хэш-функции не сопоставимы с теми потерями, которые бы понёс слушатель от охотника на следующих этапах авторизации. Поэтому хэш-загадка является первой проверкой охотника «на вшивость», при этом требует минимум «крови» слушателя.
Прохождение хэш-загадки может быть пропущено, если соединение поступает с известного ip-адреса или с узла, приславшего приветствие с заданными критериями (например, с панхэшем известного ключа). Все перепетии момента могут быть заданы на узле.
Хэш-загадка, например, вообще может быть отключена, но ЭЦП обоюдо обязательна: как для охотника, так и для слушателя.
0xffffffff>
Проверка ключа происходит так. Слушатель посылает охотнику фразу (по умолчанию, если была стадия хэш-загадки, то фраза не высылается, а используется та же самая), охотник берёт хэш sha2-384bit от фразы, подписывает своим ключом и высылает подпись слушателю:
rphrase = OpenSSL::Digest::SHA384.digest(rphrase)
sign = PandoraCrypto.make_sign(@rkey, rphrase)
Слушатель тоже берёт хэш от фразы и сверяет подпись:
res = PandoraCrypto.verify_sign(@skey, OpenSSL::Digest::SHA384.digest(params['sphrase']), rsign)
Если совпала, то происходит обратная проверка. Теперь уже охотник шлёт фразу слушателю, а слушатель подписывает её хэш и высылает охотнику.
Обратите внимание: подписывается не сама фраза, а хэш от фразы!
Дело в том, что ключи могут использоваться не только для авторизации узлов, но и для подписания записей. В этом случае, охотник (или слушатель) под видом «случайной» фразы может подсунуть какую-нибудь рабочую запись (Ключ, Человек, Договор, Проект и т. д.) и таким образом подставить владельца ключа (слушателя или охотника), который даже не будет подозревать, что его правом подписи воспользовались втёмную! В случае же когда подписывается хэш, а не сама фраза, такая манипуляция невозможна. В то же время обеспечивается надёжная проверка на владение закрытым ключом.
Если охотник прислал некорректную подпись, то он не подтверждает владение закрытым ключом. Сеанс связи обрывается, а IP-адрес наказывается штрафом.
Если охотник прислал корректную подпись, то он подтверждает владение закрытым ключом. В этом случае, если доверие к открытому ключу было оказано ранее, то сеанс связи переходит на стадию авторизации слушателя также с помощью подписи и, далее, на стадию обмена данными.
Если охотник прислал корректную подпись, но доверие к его ключу не было указано ранее, то он подвергается следующей проверке - капче.
captcha_length = 4
captcha_attempts = 2
trust_for_captchaed = true
Если длина капчи captcha_length больше нуля (по умолчанию равна 4), то слушатель, используя библиотеки Cairo и Gdk, генерирует картинку с заданным числом символов и предлагает охотнику её отгадать. Если человек на узле-охотнике отгадал картинку, то ему присваивается временное доверие trust = 0.01 (на период сеанса связи). Такой механизм даёт право писать новичкам с неподтверждёнными ключами, но при этом ограждает от ботов и спам-рассылок.
Если же длина капчи задана 0, то охотнику будет выдано сообщение:
«Ключ на подтверждении»
До тех пор, пока на слушателе не проставят доверие ключу охотника, сеанс связи будет обрываться на данном этапе.
узел ведёт список недавно подключавшихся ip-адресов и отмечает, кто как себя вёл.
Если недавно авторизация прошла нормально, то при повторном подключении этапы хэш-загадки и капчи могут быть пропущены, потребуется только подпись.
Если ip-адрес вел себя «неприлично», то накапливается длительность бана:
1) подключился и отключился без трафика - 1 мин
2) подключился, отправил приветствие, но не ответил на хэш-загадку - 2 мин
3) прислал отгадку раньше срока - 30 сек
4) прислал неверную отгадку - 10 мин
5) прислал отгадку в срок и верную, но отключился, не прислав подписи - 2 мин
6) прислал неверную подпись - 10 мин
7) прислал неверную капчу, исчерпав все (2 по умочанию) попытки - 2 мин
8) подключился повторно раньше чем через 5 мин, хотя был ответ "ключ на рассмотрении" - 5 мин
9) замолчал во время связи на этапе авторизации и не отвечает более 1 мин - 20 сек
10) прислал неадекватный сегмент для текущей стадии - 5 мин
11) авторизация с одного ip со вторым неизвестным ключом с интервалом менее 2 ч - 15 мин
12) с одного ip может быть не более 2х неавторизованных сессий
При нарушении в бан-лист заносятся адрес и время разбанивания (текущее время плюс срок нарушения).
Если нарушение повторяется, то время разбанивания отодвигается вперёд на дополнительный штрафной срок.
Если приходит подключение с адреса, который находится в бан-листе и текущее время не достигло времени разбанивания, то соединение разрывается. Также в unix-системах возможна связка Пандоры и фаервола (iptables, например), при которой отброс (reject) ip будет происходить на системном уровне, избавляя приложение от инициализации tcp (или udp) сокета.
Сеансовый ключ будет использоваться для шифрования трафика между узлами. Но кроме этого, в случае обрыва и переподключения охотник может предложить слушателю пройти авторизацию на недавно использованном сеансовом ключе. Симметричная криптография работает на порядок быстрее, а следовательно в сокращённой авторизации можно пропустить проверку хэш-загадки, основных ключей и капчи, мгновенно восстанавливая утерянную сессию. Кроме того, панхэш сессионного ключа меняется гораздо чаще (так как сессионные ключи периодически меняются, например один раз в час), а это затруднит злоумышленнику представлять себя от имени известного узла, так как панхэши сессионных ключей нигде не публикуются в отличие от публичных, которые общедоступны в сети.
Речь пойдёт о бан-листе, системе штрафов, а также ускоренной авторизации по сеансовому ключу после обрыва связи. Для начала взглянем на общую диаграмму сеанса связи:
При авторизации охотник, как инициатор соединения, подвергается большему числу проверок, нежели слушатель. Стадии авторизации проходят под диктовку слушателя, а охотник должен строго отвечать на запросы слушателя. На этапе авторизации идёт синхронный обмен.
Сеанс начинается с фразы приветствия охотника:
hello = {:version=>0, :mode=>0, :mykey=>my_key, :tokey=>out_key, :addr=>my_callback}
В которой присутствует версия протокола, опции обмена, панхэш ключа на этом узле, панхэш ключа на том узле, ip-адрес для обратного подключения.
Слушатель проверяет приветствие. Если параметры приветствия допустимы (протокол согласован, режимы поддерживаются, панхэш ключа охотника не значится, как имеющий отрицательное доверие), то можно переходить к шагам авторизации.
1. Хэш-загадка (puzzle)
Первым делом нужно заставить охотника совершить некоторый объём работы (концепция Proof-of-work). Это снижает способности охотника к DoS-атаке слушателя, которая, как вы знаете, заключается в ассиметричном расходовании ресурсов. С помощью хэш-загадки слушатель смещает баланс в свою сторону.Для этого слушатель генерирует случайную 256-символьную фразу, в которой последний байт заменяется на длину в битах требуемой «отгадки», например, 14 бит.
Задача охотника – добавить такое число к фразе, первые 14 бит суммарного хэша sha1 которого совпадут с фразой-загадкой. В виде формулы можно выразить так:
F & M = sha1(F+A) & M,
где F – фраза,
M – битовая маска требуемой длины, например 011111111111111b,
A – блок (число), который необходимо найти, чтобы выполнилось тождество.
Так как аналитического решения у уравнения с хэшем нет, то охотнику приходится перебором подставлять числа и высчитывать хэш для каждого блока (фраза+число) до тех пор, пока решение не будет найдено. Время поиска зависит от требуемой длины маски и удачи. При 14 битах на среднем ноутбуке среднее время поиска равняется около 2 секунд (иногда больше, иногда меньше). Вы можете увеличить или уменьшить размер маски, регулируя этим самым требовательность к охотнику.
Также в предпоследнем байте фразы задаётся минимальное время задержки, которое охотник должен ждать, если нашёл отгадку слишком быстро.
По умолчанию в Пандоре заданы такие параметры:
puzzle_bit_length = 14
puzzle_sec_delay = 2
Если охотник честно отработал и нашёл отгадку, а слушатель проверил отгадку, то авторизация переходит на следующий этап. Если охотник отказывается выполнять работу или присылает неверную отгадку, то штрафуется добавлением во временный бан-лист. Вот часть кода, которая отвечает за генерацию загадки, нахождение (простым перебором) и проверку отгадки:
# Generate random phrase
# RU: Сгенерировать случайную фразу
def get_sphrase(init=false)
phrase = params['sphrase'] if not init
if init or (not phrase)
phrase = OpenSSL::Random.random_bytes(256)
params['sphrase'] = phrase
init = true
end
[phrase, init]
end
phrase, init = get_sphrase(true)
phrase[-1] = puzzle_bit_length.chr
phrase[-2] = puzzle_sec_delay.chr
# Find sha1-solution
# RU: Находит sha1-загадку
def self.find_sha1_solution(phrase)
res = nil
lenbit = phrase[phrase.size-1].ord
len = lenbit/8
puzzle = phrase[0, len]
tailbyte = nil
drift = lenbit - len*8
if drift>0
tailmask = 0xFF >> (8-drift)
tailbyte = (phrase[len].ord & tailmask) if tailmask>0
end
i = 0
while (not res) and (i<0xffffffff -="" 1="" add="" and="" check="" def="" drift="" end="" hash="" i="" if="" len="" lenbit="phrase[phrase.size-1].ord" not="" offer="=puzzle)" or="" ord="" phrase="" puzzle="phrase[0," res="false" ru:="" self.check_sha1_solution="" sha1-="" sha1-solution="" tailbyte="nil" tailmask="">0
tailmask = 0xFF >> (8-drift)
tailbyte = (phrase[len].ord & tailmask) if tailmask>0
end
hash = Digest::SHA1.digest(phrase+add)
offer = hash[0, len]
if (offer==puzzle) and ((not tailbyte) or ((hash[len].ord & tailmask)==tailbyte))
res = true
end
res
end
Заметьте, затраты на проверку через единственный вызов хэш-функции требуют гораздо меньше вычислительных ресурсов, чем многократный вызов хэш-функций для поиска отгадки. Но это в «нормальном» режиме.
Охотник-злоумышленник может подсунуть левую отгадку, желая нагрузить слушателя. В этом случае ресурсные потери слушателя будут больше, чем у охотника. Но важно понимать, что потери на генерацию фразы (которую, кстати, можно генерять не чаще одной в минуту для всех) и одиночный вызов хэш-функции не сопоставимы с теми потерями, которые бы понёс слушатель от охотника на следующих этапах авторизации. Поэтому хэш-загадка является первой проверкой охотника «на вшивость», при этом требует минимум «крови» слушателя.
Прохождение хэш-загадки может быть пропущено, если соединение поступает с известного ip-адреса или с узла, приславшего приветствие с заданными критериями (например, с панхэшем известного ключа). Все перепетии момента могут быть заданы на узле.
Хэш-загадка, например, вообще может быть отключена, но ЭЦП обоюдо обязательна: как для охотника, так и для слушателя.
0xffffffff>
2. Электронная подпись (sign)
В этом месте нужно заметить, что хэш-загадка была придумана, чтобы безопасно подойти к шагу обмена ключами, анкетами и началу «взрослой» криптографии. Ведь принятие Ключа, регистрация Узла и непосредственно сама проверка подписи требуют ощутимые ресурсы слушателя. Дополнительной мерой защиты может служить ограничение на число принимаемых неизвестных (не просчитываемых в системе доверия слушателя) ключей с одного ip-адреса за заданную единицу времени. Например, в сутки не более 3-х неизвестных ключей с одного ip.Проверка ключа происходит так. Слушатель посылает охотнику фразу (по умолчанию, если была стадия хэш-загадки, то фраза не высылается, а используется та же самая), охотник берёт хэш sha2-384bit от фразы, подписывает своим ключом и высылает подпись слушателю:
rphrase = OpenSSL::Digest::SHA384.digest(rphrase)
sign = PandoraCrypto.make_sign(@rkey, rphrase)
Слушатель тоже берёт хэш от фразы и сверяет подпись:
res = PandoraCrypto.verify_sign(@skey, OpenSSL::Digest::SHA384.digest(params['sphrase']), rsign)
Если совпала, то происходит обратная проверка. Теперь уже охотник шлёт фразу слушателю, а слушатель подписывает её хэш и высылает охотнику.
Обратите внимание: подписывается не сама фраза, а хэш от фразы!
Дело в том, что ключи могут использоваться не только для авторизации узлов, но и для подписания записей. В этом случае, охотник (или слушатель) под видом «случайной» фразы может подсунуть какую-нибудь рабочую запись (Ключ, Человек, Договор, Проект и т. д.) и таким образом подставить владельца ключа (слушателя или охотника), который даже не будет подозревать, что его правом подписи воспользовались втёмную! В случае же когда подписывается хэш, а не сама фраза, такая манипуляция невозможна. В то же время обеспечивается надёжная проверка на владение закрытым ключом.
Если охотник прислал некорректную подпись, то он не подтверждает владение закрытым ключом. Сеанс связи обрывается, а IP-адрес наказывается штрафом.
Если охотник прислал корректную подпись, то он подтверждает владение закрытым ключом. В этом случае, если доверие к открытому ключу было оказано ранее, то сеанс связи переходит на стадию авторизации слушателя также с помощью подписи и, далее, на стадию обмена данными.
Если охотник прислал корректную подпись, но доверие к его ключу не было указано ранее, то он подвергается следующей проверке - капче.
3. Картинка-загадка (captcha)
Если ключ охотника из предыдущей стадии не имеет доверия слушателя, то дальнейший ход сеанса связи зависит от настроек для капчи:captcha_length = 4
captcha_attempts = 2
trust_for_captchaed = true
Если длина капчи captcha_length больше нуля (по умолчанию равна 4), то слушатель, используя библиотеки Cairo и Gdk, генерирует картинку с заданным числом символов и предлагает охотнику её отгадать. Если человек на узле-охотнике отгадал картинку, то ему присваивается временное доверие trust = 0.01 (на период сеанса связи). Такой механизм даёт право писать новичкам с неподтверждёнными ключами, но при этом ограждает от ботов и спам-рассылок.
Если же длина капчи задана 0, то охотнику будет выдано сообщение:
«Ключ на подтверждении»
До тех пор, пока на слушателе не проставят доверие ключу охотника, сеанс связи будет обрываться на данном этапе.
4. Активный бан-лист
В настоящий момент бан-лист не реализован в коде, но видится он примерно так:узел ведёт список недавно подключавшихся ip-адресов и отмечает, кто как себя вёл.
Если недавно авторизация прошла нормально, то при повторном подключении этапы хэш-загадки и капчи могут быть пропущены, потребуется только подпись.
Если ip-адрес вел себя «неприлично», то накапливается длительность бана:
1) подключился и отключился без трафика - 1 мин
2) подключился, отправил приветствие, но не ответил на хэш-загадку - 2 мин
3) прислал отгадку раньше срока - 30 сек
4) прислал неверную отгадку - 10 мин
5) прислал отгадку в срок и верную, но отключился, не прислав подписи - 2 мин
6) прислал неверную подпись - 10 мин
7) прислал неверную капчу, исчерпав все (2 по умочанию) попытки - 2 мин
8) подключился повторно раньше чем через 5 мин, хотя был ответ "ключ на рассмотрении" - 5 мин
9) замолчал во время связи на этапе авторизации и не отвечает более 1 мин - 20 сек
10) прислал неадекватный сегмент для текущей стадии - 5 мин
11) авторизация с одного ip со вторым неизвестным ключом с интервалом менее 2 ч - 15 мин
12) с одного ip может быть не более 2х неавторизованных сессий
При нарушении в бан-лист заносятся адрес и время разбанивания (текущее время плюс срок нарушения).
Если нарушение повторяется, то время разбанивания отодвигается вперёд на дополнительный штрафной срок.
Если приходит подключение с адреса, который находится в бан-листе и текущее время не достигло времени разбанивания, то соединение разрывается. Также в unix-системах возможна связка Пандоры и фаервола (iptables, например), при которой отброс (reject) ip будет происходить на системном уровне, избавляя приложение от инициализации tcp (или udp) сокета.
5. Ускоренное восстановление сеанса
В настоящий момент авторизация производится на открытых ключах ассиметричного алгоритма RSA. Но в будущем запланирована генерация закрытых сеансовых ключей для симметричного алгоритма, например Blowfish.Сеансовый ключ будет использоваться для шифрования трафика между узлами. Но кроме этого, в случае обрыва и переподключения охотник может предложить слушателю пройти авторизацию на недавно использованном сеансовом ключе. Симметричная криптография работает на порядок быстрее, а следовательно в сокращённой авторизации можно пропустить проверку хэш-загадки, основных ключей и капчи, мгновенно восстанавливая утерянную сессию. Кроме того, панхэш сессионного ключа меняется гораздо чаще (так как сессионные ключи периодически меняются, например один раз в час), а это затруднит злоумышленнику представлять себя от имени известного узла, так как панхэши сессионных ключей нигде не публикуются в отличие от публичных, которые общедоступны в сети.
Комментариев нет :
Отправить комментарий