0,0
рейтинг
29 мая 2013 в 17:24

Разработка → Вы опасно некомпетентны в криптографии из песочницы

От переводчика: Хоть посыл статьи Najaf Ali, переведённой ниже, и носит слегка рекламный оттенок («оставьте криптографию нам, экспертам»), но описанные в ней примеры показались мне довольно интересными и заслуживающими внимания.
Кроме того, никогда не будет лишним повторить прописную истину: не придумывайте свою крипто-защиту. И эта статья отлично иллюстрирует почему.


Есть четыре стадии компетентности:
  1. Не осознаваемая некомпетентность — когда вы не знаете, что вы некомпетентны и насколько обширна ваша некомпетентность.
  2. Осознаваемая некомпетентность — когда вы знаете о своей некомпетентности и знаете, какие шаги надо предпринять, чтобы улучшить ситуацию.
  3. Осознаваемая компетентность — когда вы хороши и вы знаете об этом. (Это классно!)
  4. Не осознаваемая компетентность — когда вы настолько хороши, что вы уже не знаете об этом.


Мы все начинаем с первой стадии, нравится нам это или нет. Ключ к переходу от стадии 1 к стадии 2 в любой области — делать много ошибок и получать ответную реакцию. Если вы получаете ответную реакцию, вы начинаете понимать, что вы сделали правильно, что вышло неправильно, и что вам стоит улучшить в следующий раз.

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

Вы можете научиться хорошо программировать непреднамеренно. Если ваш код не компилируется, не делает того, что вам от него требуется, или содержит легко обнаруживаемые баги, вы получаете немедленный отклик. Вы исправляете всё, и в следующий раз у вас уже получится лучше.

Криптографии невозможно научиться непреднамеренно. Если вы не проводите время за чтением материалов об уязвимостях и попытками их использовать, у ваших доморощенных механизмов защиты, основанных на криптографии, не много шансов против реальных атак.

Если вы не заплатите эксперту по безопасности, который знает как взламывать механизмы защиты, основанные на криптографии, вы не сможете узнать, что ваш код небезопасен. Атакующие, обходящие вашу защиту, тоже вам не помогут (в идеале, они смогут обойти её так, что вы никогда об этом не узнаете).

Ниже рассматривается несколько примеров неправильного использования криптографии. Задумайтесь, если бы вы не прочитали этот пост, смогли бы вы обнаружить эти ошибки в реальности?

Аутентификация API для вашего сайта обмена фотографиями



Аутентификация сообщений с помощью MD5 + секрет


Один сайт обмена фотографиями как-то аутентифицировал запросы к своему API по следующей схеме:
  • У пользователя имеются следующие реквизиты:
    • публичный user id, которым они идентифицируют себя (его безопасно пересылать прямым текстом)
    • общий с сервером секрет, которым они подписывают сообщения (должен храниться в тайне)
  • Пользователь делает запрос к API по HTTP (или HTTPS — не важно). Деструктивные изменения выполняются с помощью POST/GET запроса со специальными параметрами (например, { action: create, name: 'my-new-photo' } ).
  • Чтобы аутентифицировать сообщение, пользователь отправляет параметром свой user id и подписывает сообщение своим секретным ключом. Подпись — это MD5 от строки, состоящей из общего секрета и добавленных за ним следом пар ключ-значение.


Чтобы убедиться, что запрос действительно пришёл от указанного пользователя, сервер аналогично генерирует подпись для параметров запроса и секрета, который записан у него для этого пользователя.
Код для этого мог бы быть таким:

# НА КЛИЕНТЕ

require 'openssl'

## реквизиты пользователя
user_id = '42'
secret  = 'OKniSLvKZFkOhlo16RoTDg0D2v1QSBQvGll1hHflMeO77nWesPW+YiwUBy5a'

## параметры запроса, которые мы хотим отослать
params = { foo: 'bar', bar: 'baz', user_id: user_id }

## считаем MAC
message      = params.each.map { |key, value| "#{key}:#{value}" }.join('&')
params[:mac] = OpenSSL::Digest::MD5.hexdigest(secret + message)

## и потом отправляем запрос как-то так...
HTTP.post 'api.example.com/v3', params

# НА СЕРВЕРЕ

## получаем реквизиты пользователя из БД
user   = User.find(params[:user_id])
secret = user.secret

## получаем MAC из параметров запроса
challenge_mac = params.delete(:mac)

## вычисляем MAC заново, используя тот же метод, что и клиент
message        = params.each.map { |key, value| "#{key}:#{value}" }.join('&')
calculated_mac = OpenSSL::Digest::MD5.hexdigest(secret + message)

## сравниваем значения MAC из запроса и вычисленное заново
if challenge_mac == calculated_mac
  # пользователь успешно аутентифицирован - делаем, что он попросил
else
  # пользователь не аутентифицирован, ошибка
end


С базовыми знаниями о том, как работает MD5, это совершенно адекватная реализация аутентификации запросов к API. Выглядит вполне безопасно, так ведь? Вы уверены?

Оказывается, такая схема уязвима к так называемой "атаке увеличением длины сообщения" (Length extension attack).

Вкратце:
  • Если вы знаете значение md5('foo'), в силу способа вычисления MD5 можно очень легко посчитать значение md5('foobar'), даже не зная префикса 'foo'.
  • Так что если вы знаете значение md5('secretfoo:bar'), можно легко вычислить значение md5('secretfoo:bar&bar:baz'), даже не зная префикса 'secret'.
  • Это значит, что если у вас есть хотя бы одно подписанное сообщение, вы можете подделывать подписи для этого сообщения с любыми дополнительными параметрами в запросе. И проверка аутентификации по приведённой выше схеме будет проходить успешно.


Любой разработчик, который не знал об этом заранее, запросто бы попался. Разработчики Flickr, Vimeo и Remember the Milk использовали такой подход в своих продуктах (pdf).

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

Не убедил? Хорошо, давайте попробуем исправить этот пример и посмотрим, получится ли у нас сделать его безопасным…

Аутентификация сообщений с помощью HMAC


Ваш знакомый хакер рассказал вам об этой уязвимости и порекомендовал использовать Hash-based Message Authentication Code (HMAC) для аутентификации запросов.

Отлично! HMAC придуман как раз для нашего случая. Да и замена очень проста, практически один-в-один.

Наш код проверки подписи на сервере теперь может выглядеть так:

require 'openssl'

## получаем реквизиты пользователя из БД
user   = User.find(params[:user_id])
secret = user.secret

## получаем HMAC из параметров запроса
challenge_hmac = params.delete(:hmac)

## вычисляем HMAC
## на клиенте мы делаем то же самое, когда генерируем запрос
message         = params.each.map { |key, value| "#{key}:#{value}" }.join('&')
calculated_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('md5'), secret, message)

## сравниваем значения HMAC из запроса и вычисленное заново
if challenge_hmac == calculated_hmac
  # пользователь успешно аутентифицирован - делаем, что он попросил
else
  # пользователь не аутентифицирован, ошибка
end


Выглядит вполне безопасно, так ведь? Вы уверены?

Оказывается, что код выше уязвим к атаке по времени (Timing attack), которая позволяет подобрать правильный HMAC для заданного сообщения.

Вкратце:
  • Для заданного сообщения попробуйте отправить его с HMAC, состоящего из многократного повторения одного и того же символа. Повторите это для каждого символа ASCII — 'aaaa...', 'bbbb...' и т.д.
  • Замеряйте время обработки каждого запроса. Поскольку сравнение строк работает чуть-чуть дольше, если первые символы совпадают, сообщение, которое будет обрабатываться дольше остальных, будет содержать правильный первый символ HMAC.
  • Шум от задержек можно сглаживать двумя способами:
    • Выполните каждый запрос по паре сотен или даже тысяч раз и используйте среднее время
    • Запускайте свой атакующий код в том же дата-центре, где находится атакуемое приложение. Если определить дата-центр не получается, в худшем случае, вы можете арендовать по серверу у каждого крупного провайдера и выяснить на каком из них будет существенно меньший пинг до цели.
  • Когда вы вычислили первый символ, повторите запросы с совпадающими символами от второго и далее. Например, если подобранный первый символ — 'x', отсылайте запросы с HMAC 'xaaa...', 'xbbb...' и т.д.
  • Продолжайте, пока не получите HMAC целиком.


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

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

И снова давайте продолжим и попробуем сделать этот код более безопасным…

Проверка HMAC нечувствительным ко времени способом


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

Для сравнения строк мы можем воспользоваться тем фактом, что XOR любого байта с самим собой даст 0. Всё, что нам надо сделать — применить операцию XOR к каждой паре соответствующих байтов из строк A и B, сложить получившиеся результаты и вернуть true, если сумма равна 0, и false в противном случае.
На Ruby это могло бы выглядеть как-то так:

require 'openssl'

## функция сравнения строк, нечувствительная ко времени
def secure_equals?(a, b)
  return false if a.length != b.length
  a.bytes.zip(b.bytes).inject(0) { |sum, (a, b)| sum |= a ^ b } == 0
end


## получаем реквизиты пользователя из БД
user   = User.find(params[:user_id])
secret = user.secret

## получаем HMAC из параметров запроса
challenge_hmac = params.delete(:hmac)

## вычисляем HMAC
## на клиенте мы делаем то же самое, когда генерируем запрос
message         = params.each.map { |key, value| "#{key}:#{value}" }.join('&')
calculated_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('md5'), secret, message)

## сравниваем значения HMAC из запроса и вычисленное заново
if secure_equals?(challenge_hmac, calculated_hmac)
  # пользователь успешно аутентифицирован - делаем, что он попросил
else
  # пользователь не аутентифицирован, ошибка
end


Выглядит вполне безопасно, так ведь? Вы уверены?

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

Избавьте себя от проблем. Не используйте криптографию. Это плутоний. Есть миллионы способов наделать ошибок и всего несколько ценных способов сделать всё правильно.

P.S. Если вам никак не обойтись без проверки HMAC вручную и у вас есть доступ к модулю activesupport, вы можете осуществлять нечувствительное ко времени сравнение с помощью ActiveSupport::MessageVerifier. Не пишите его с нуля. И ради всего святого не копируйте мою реализацию выше.

P.P.S. Всё ещё не убеждены? Пройдите Matasano Crypto Challenges — может, хотя бы они изменят ваше мнение. Я не прошёл ещё и половины, и мне уже пришлось связаться с двумя прошлыми клиентами, чтобы исправить у них ошибки с криптографией.

Об авторе: Najaf Ali — технический консультант из Лондона. Занимается разработкой программного обеспечения для стартапов и мелкого и среднего бизнеса. Пишет о технологиях, предпринимательстве и разработке ПО.
Дмитрий Черняченко @sabio
карма
35,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

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

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

  • +1
    Годно. Спасибо.
  • +29
    Атака по времени? Удаленно различить задержку, вызванную парой инструкций процессора, это он серьезно?
    • 0
      Статистика и не такое умеет.
      • +9
        Сомневаюсь. Такую атаку можно провести только выполняя её непосредственно на атакуемой машине, а иначе разброс времени сетевых задержек нивелируют время сравнения строк, которое может занимать лишь наносекунды.
        • +5
          В статье Википедии ссылаются на успешный пример атаки по времени по сети в 2003 (pdf). Пишут, правда, что «сетевое расстояние в эксперименте было маленьким», что бы это ни значило.
          • 0
            На атакуемом хосте могут быть запущены десятки процессов, которые непредсказуемо отберут такты процессора. Прерывания, сетевая активность других клиентов и т.д. и т.п. В лабораторных условиях вычислить возможно, в реали — уверен нет смысла пытаться
            • +8
              Всё, о чём вы пишете — это и есть упомянутые в статье «шумы».
              С ними можно пробовать бороться. И два наиболее очевидных способа как раз указаны.

              Подобраться физически как можно ближе к атакуемому серверу, запускать подбор, когда большинство пользователей спят, повторять запросы тысячи раз, чтобы отфильтровать все случайные девиации за счёт усреднения — всё это вполне может обеспечить атаке успех.
              • 0
                Я не располагаю достаточными знаниями и ресурсами (в силу того, что я мобильный девелопер), но если бы напишете статью с проверкой подобной схемы — уверен, она выйдет в топ первой страницы хабра.
                • 0
                  Не попадет.
                  А перепечатывать учебники по статистике на хабр… ну фиг знает.
            • +2
              В обсуждении оригинальной статьи приводят ещё ссылку на вот это видео с Blackhat 2010:
              www.youtube.com/watch?v=9i9jhPo9jTM
              • 0
                Про скорости сравнения строк — 44:53 «All these appeared to be exploitable over the LAN, but not Internet — we're dealing with 43 to 44 nanoseconds average». Это в Java, которая оказалась в 40 раз медленне других — C, питон, руби. А если сервер на ruby?
                Вобщем непонятно, реально ли вообще такой эксплойт провести (особенно если исходники того, что на сервере, не доступны атакующему, и он не знает что там и как сравнивается и при каких условиях).
                Вспоминается XKCD: xkcd.ru/538/
          • –1
            localhost!
            • +2
              Вы неправы. В pdf говорится, что атакующая и атакуемая машины находились в разных зданиях, и между ними были три роутера и несколько свитчей.

              Ссылка, которую я привёл выше, к сожалению, не работает. Вот правильная — Remote timing attacks are practical
          • 0
            «сетевое расстояние в эксперименте было маленьким» — конечно, это означает что был маленький пинг
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Балансировщик случайно одного и того же клиента намеренно не перенаправляет на одну и ту же ноду? А то иначе встаёт проблема синхронизации локального состояния нод…
    • +12
      На coursera есть совершенно потрясающий курс основ криптографии стэнфордского университета, там и не такие вещи рассказывали. Если тема интересна — не проходите мимо.
      • +4
        Очень хороший курс, в тему топика, автор почти в каждой лекции говорил не изобретать алгоритм шифрования самому, а использовать проверенные надежные.
      • 0
        а можно ссылку на сам курс?
        • +5
          Предполагаю, что этот
          • +3
            Именно.А 15 июля ожидается вторая часть.
          • +3
            Обещал себе не брать курсы на лето, ибо нефиг дома сидеть за компом. Но prof. Dan Boneh так складно говорил…
    • –7
      Да. Но, только MD5 — плохой пример, вот md5(unix) думаю подойдёт.
    • +15
      Согласен с вами, звучит не серьезно. Хоть посыл в статье и правильный, но примеры доставляют…
      Накидал код в сях, связь с серваком по сокетам (асинк), локалка 1Gb.
      Перебор «HMAC» от AAAAAA до ZZZZZZ, при правильном ZZZZZZ.
      Разброс в ответе на реквест дельта от 70нс до 220нс, при 1-10% нагруженой сетке.
      Разброс локально (localhost->localhost) дельта от 50нс до 85нс. Причем совершенно не важно сколько букв правильно. Даже тенденции нет.
      И это при том, что сервак ничего кроме перебора не делал (multithreaded).
      • +1
        Переложил на CPU cycle counter через rdtsc, чтобы исключить колебания по времени (неточность таймера). Разбег от 5 до 18, кому интересно под спойлером парочка значений — для правильного последнее. Все localhost…
        Тест single thread
        cycle---dif
        957323843---6
        957323856---5
        957323875---5
        957323886---5
        957323896---5
        957323907---5
        957323917---5
        957323928---5
        957323938---6
        957323949---5
        957323960---5
        957323970---5
        957323981---5
        957323991---6
        957324002---5
        957324012---5
        957324023---5
        957324033---6
        957324044---5
        957324055---5
        957324065---5
        957324075---5
        957324086---15
        957324106---6
        957324117---5
        957324128---5
        957324139---5
        957324150---5
        957324161---5
        957324172---5
        957324183---5
        957324193---6
        957324205---5
        957324215---6
        957324226---5
        957324237---5
        957324248---5
        957324259---5
        957324269---6
        957324280---6
        957324291---5
        957324302---5
        957324312---6
        957324323---5
        957324334---5
        957324344---6
        957324355---5
        957324366---6
        957324377---5
        957324388---5
        957324399---5
        957324410---5
        957324420---6
        957324431---5
        957324442---5
        957324453---5
        957324464---5
        957324475---5
        957324486---5
        957324496---6
        957324507---5
        957324517---5
        957324528---5
        957324539---5
        957324550---5
        957324560---6
        957324571---5
        957324582---13
        957324600---5
        957324610---6
        957324621---5
        957324631---5
        957324642---5
        957324652---5
        957324662---6
        957324673---5
        957324683---5
        957324693---6
        957324704---5
        957324714---5
        957324725---5
        957324735---6
        957324746---5
        957324756---5
        957324767---5
        957324777---5
        957324787---5
        957324798---5
        957324808---5
        957324819---5
        957324839---5
        957324849---6
        957324860---5
        957324871---5
        957324881---6
        957324892---5
        957324903---5
        957324913---6
        957324924---5

        Тест 5 threads
        cycle---dif
        1368424031---10
        1368424048---9
        1368424065---8
        1368424080---8
        1368424096---8
        1368424111---8
        1368424126---9
        1368424142---8
        1368424158---7
        1368424173---8
        1368424188---8
        1368424204---8
        1368424219---8
        1368424234---8
        1368424250---7
        1368424265---8
        1368424280---8
        1368424295---9
        1368424311---8
        1368424327---7
        1368424342---8
        1368424357---7
        1368424372---8
        1368424387---8
        1368424402---9
        1368424418---8
        1368424433---8
        1368424448---8
        1368424464---8
        1368424479---8
        1368424494---8
        1368424510---7
        1368424525---8
        1368424540---8
        1368424555---8
        1368424570---8
        1368424585---8
        1368424600---8
        1368424616---7
        1368424631---7
        1368424646---8
        1368424661---8
        1368424676---8
        1368424692---8
        1368424707---8
        1368424722---8
        1368424737---8
        1368424752---8
        1368424768---8
        1368424783---8
        1368424798---8
        1368424813---8
        1368424828---8
        1368424844---8
        1368424859---8
        1368424874---8
        1368424890---7
        1368424905---7
        1368424920---8
        1368424935---8
        1368424950---8
        1368424965---8
        1368424981---7
        1368424996---8
        1368425011---8
        1368425026---8
        1368425041---8
        1368425056---8
        1368425072---7
        1368425087---8
        1368425102---8
        1368425117---8
        1368425132---8
        1368425147---8
        1368425163---7
        1368425177---8
        1368425192---8
        1368425208---8
        1368425223---8
        1368425238---8
        1368425253---8
        1368425269---8
        1368425284---8
        1368425299---8
        1368425314---8
        1368425329---8
        1368425344---8
        1368425360---8
        1368425375---8
        1368425390---8
        1368425405---8
        1368425421---8
        1368425436---8
        1368425451---8
        1368425466---8
        1368425481---8
        1368425497---7
        1368425512---8
        1368425527---8
      • +3
        Упростил перебор:
        Cравненивал через strcmp, stricmp и даже string::operator ==
        В выдаче:
        1ST_NOT_EQ — сравнивает AAA...AAA (64-е раза А) c ZZZ...ZZZ (64-е раза Z), хотя после первой неправильной буквы return -1
        ALL_EQUAL — сравнивает ZZZ...ZZZ c ZZZ...ZZZ, соответственно return 0
        Результаты теста
        1ST_NOT_EQ---3973032722---8
        ALL_EQUAL----3973032736---8
        1ST_NOT_EQ---3973032750---7
        ALL_EQUAL----3973032764---7
        1ST_NOT_EQ---3973032777---8
        ALL_EQUAL----3973032791---7
        1ST_NOT_EQ---3973032804---7
        ALL_EQUAL----3973032817---7
        1ST_NOT_EQ---3973032831---7
        ALL_EQUAL----3973032844---7
        1ST_NOT_EQ---3973032858---7
        ALL_EQUAL----3973032871---7
        1ST_NOT_EQ---3973032884---7
        ALL_EQUAL----3973032897---7
        1ST_NOT_EQ---3973032911---6
        ALL_EQUAL----3973032923---7
        1ST_NOT_EQ---3973032937---7
        ALL_EQUAL----3973032950---7
        1ST_NOT_EQ---3973032963---7
        ALL_EQUAL----3973032977---7
        1ST_NOT_EQ---3973032990---7
        ALL_EQUAL----3973033003---6
        1ST_NOT_EQ---3973033016---7
        ALL_EQUAL----3973033028---7
        1ST_NOT_EQ---3973033042---6
        ALL_EQUAL----3973033055---6
        1ST_NOT_EQ---3973033068---7
        ALL_EQUAL----3973033082---7
        1ST_NOT_EQ---3973033095---7
        ALL_EQUAL----3973033110---7
        1ST_NOT_EQ---3973033123---7
        ALL_EQUAL----3973033137---7
        1ST_NOT_EQ---3973033151---8
        ALL_EQUAL----3973033165---7
        1ST_NOT_EQ---3973033178---7
        ALL_EQUAL----3973033191---7
        1ST_NOT_EQ---3973033205---7
        ALL_EQUAL----3973033218---7
        1ST_NOT_EQ---3973033231---7
        ALL_EQUAL----3973033244---6
        1ST_NOT_EQ---3973033257---6
        ALL_EQUAL----3973033269---7
        1ST_NOT_EQ---3973033282---7
        ALL_EQUAL----3973033296---7
        1ST_NOT_EQ---3973033309---7
        ALL_EQUAL----3973033322---7
        1ST_NOT_EQ---3973033336---6
        ALL_EQUAL----3973033348---7
        1ST_NOT_EQ---3973033362---7
        ALL_EQUAL----3973033375---8
        1ST_NOT_EQ---3973033389---28
        ALL_EQUAL----3973033427---10
        1ST_NOT_EQ---3973033445---8
        ALL_EQUAL----3973033459---8
        1ST_NOT_EQ---3973033473---8
        ALL_EQUAL----3973033487---7
        1ST_NOT_EQ---3973033501---7
        ALL_EQUAL----3973033516---7
        1ST_NOT_EQ---3973033530---7
        ALL_EQUAL----3973033543---7
        1ST_NOT_EQ---3973033556---7
        ALL_EQUAL----3973033569---7
        1ST_NOT_EQ---3973033582---7
        ALL_EQUAL----3973033595---7
        1ST_NOT_EQ---3973033608---7
        ALL_EQUAL----3973033621---8
        1ST_NOT_EQ---3973033635---7
        ALL_EQUAL----3973033648---7
        1ST_NOT_EQ---3973033661---7
        ALL_EQUAL----3973033674---7
        1ST_NOT_EQ---3973033687---7
        ALL_EQUAL----3973033700---7
        1ST_NOT_EQ---3973033714---6
        ALL_EQUAL----3973033727---7
        1ST_NOT_EQ---3973033740---7
        ALL_EQUAL----3973033753---6
        1ST_NOT_EQ---3973033766---7
        ALL_EQUAL----3973033779---6
        1ST_NOT_EQ---3973033792---7
        ALL_EQUAL----3973033804---7
        1ST_NOT_EQ---3973033818---7
        ALL_EQUAL----3973033831---7
        1ST_NOT_EQ---3973033844---7
        ALL_EQUAL----3973033857---7
        1ST_NOT_EQ---3973033870---7
        ALL_EQUAL----3973033883---7
        1ST_NOT_EQ---3973033896---7
        ALL_EQUAL----3973033910---7
        1ST_NOT_EQ---3973033923---6
        ALL_EQUAL----3973033936---7
        1ST_NOT_EQ---3973033949---7
        ALL_EQUAL----3973033962---7
        1ST_NOT_EQ---3973033975---7
        ALL_EQUAL----3973033988---7
        1ST_NOT_EQ---3973034001---7
        ALL_EQUAL----3973034014---7
        1ST_NOT_EQ---3973034027---7
        ALL_EQUAL----3973034040---7
        

        Может кто все-таки подскажет каким-таким способом я могу словить разницу во временной задержке? Может использовать реал-тайм систему, другой таймер, еще что-либо?
        Кроме того объясните мне, как подобрать 64 байта, если я даже для первого не вижу задержки.
        • +13
          Да никаким, очевидно же, что автор просто пугает и навязывает идею о том, что вы «не компетентны в криптографии».

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

          Но за выполнением этого кода стоит еще много вещей — потоки исполнения (и их переключение планировщиком) (и наверняка адекватный сервер держит пул потоков, и мы даже не узнаем в какой момент наш запрос будет передан воркеру), cache miss, ошибки предсказателя переходов, состояние конвейера ЦПУ, загрузка шины, аллокатор памяти — это только первое что пришло в голову.

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

          А плодить лишние XOR'ы чтоб «никто не отследил» это какое-то извращение.
          • 0
            Все так, просто чтобы озвучить «риторичность» вопроса — полностью асинхронный пул на ринг буферах который делал только «перебор», без контекст-свич (8 цпу), блокировок и ко. Время замера от коннекта и реквеста, т.е. только ответ. Без сети — ибо localhost. Т.е. не «шумим».
            Как-то так.
          • 0
            Судя по фамилии, писал индус, а они любят громкие слова и интриги с расследованиями.
  • +11
    Если вы знаете значение md5('foo'), в силу способа вычисления MD5 можно очень легко посчитать значение md5('foobar'), даже не зная префикса 'foo'.

    Только не foo, а блок из 512*k бит, ну или «foo»+padding. И будет, соответственно, не «foobar», а «foo»+padding+«bar»
    • +6
      Вы, безусловно, правы. А автор (не думаю, что он об этом не знал), видимо, решил спрятать это за своим «Вкратце».
      Деталь, в общем-то, существенная. И в некоторых случаях вполне может сделать атаку невозможной.
    • 0
      А можете продемонстрировать? Я никак не могу понять этот тип атак.
      • 0
        В Википедии есть пример — en.wikipedia.org/wiki/Length_extension_attack#cite_note-1
        • 0
          Я видел его. И по ссылкам там ходил. Там примеры куцые какие-то везде. Я хочу воспроизвести эту атаку. Вы можете мне помочь?

          Например, пусть у нас есть урл:

          site.com/?param=protected&sig=9bf4a1a22db9b764a36af4746864b847
          

          Здесь sig = md5(key+protected). Я хочу получить:

          site.com/?param=hacked
          

          Как мне это сделать?
          • 0
            Именно так — не получится.
            Суть Length extension в том, что к сообщению добавляются новые байты. Заменить исходные не выйдет.
            Т.е. в лучшем случае, у вас получится что-то вроде
            site.com/?param=protected\x80\x00\x00...\x00\xNN&param=hacked
            • 0
              а мне кажется сам пример некорректным. Нелья непосредственно подписываемый запрос добавлять подпись этого запроса. Можно поместить запрос в контейнер, который подписать и уже вместе с контейнером посылать подпись
              • 0
                А что такое этот «контейнер» в контексте, например, HTTP запроса?

                Предлагаете придумать какой-то свой формат передачи параметров и заново реализовывать формирование запроса на клиенте и парсинг на сервере? А также обработку специальных случаев (мульти-значения, искейпинг) и пр. Т.е., по сути, отказаться от всех удобств работы с параметрами, предоставляемых используемым фреймворком (никаких больше params[:user_id] «из коробки»).
            • 0
              Суть Length extension в том, что к сообщению добавляются новые байты. Заменить исходные не выйдет.

              Это я понял.

              Т.е. в лучшем случае, у вас получится что-то вроде
              site.com/?param=protected\x80\x00\x00...\x00\xNN¶m=hacked

              Именно. Как мне этого добиться?
              • +2
                Пришлось сдуть пыль с компилятора для C (на удивление, ни в одном более высокоуровневом языке нельзя подсунуть в MD5 начальный контекст).

                Я немного изменил формат передачи сообщения серверу, чтобы пример был более наглядным.

                    MD5_CTX seed;
                
                // оригинальный запрос к серверу:
                // example.com/?msg=status:protected&sig=039f220db17cebe441bc4d34e6683963
                
                    MD5_Init(&seed);
                    // секрет + тело сообщения
                    char *msg = "900150983cd24fb0d6963d7d28e17f72" \
                        "status:protected";
                    printf("%s\n", md5(msg, strlen(msg), &seed));
                    // = 039f220db17cebe441bc4d34e6683963 - это и есть подпись для исходного сообщения
                
                    MD5_Init(&seed);
                    // та самая исходная подпись, порезанная на части
                    seed.A = 0x0d229f03;
                    seed.B = 0xe4eb7cb1;
                    seed.C = 0x344dbc41;
                    seed.D = 0x633968e6;
                    // длина исходного сообщения с секретом в битах, округлённая вверх до кратного 512 значения
                    seed.Nl = 512;
                    seed.Nh = 0;
                
                    // вычисляем новый хэш (= подпись), исходя из начального значения и нашей "добавки"
                    char *extension = ",status:hacked";
                    printf("%s\n", md5(extension, strlen(extension), &seed));
                    // = 164bc99c345fd21e1e810bf9a41222b
                
                // подделанный запрос к серверу:
                // example.com/?msg=status:protected%80%00%00%00%00%00%00%00%80%01%00%00%00%00%00%00,status:hacked&sig=164bc99c345fd21e1e810bf9a41222b
                
                    // а вот так будет считать хэш сервер
                    // я специально порезал строку на части, чтобы лучше была видна структура:
                    // секрет, исходное сообщение, паддинг, длина исходного сообщения с секретом, "добавка"
                    MD5_Init(&seed);
                    char *msg2 = "900150983cd24fb0d6963d7d28e17f72" \
                        "status:protected" \
                        "\x80\x00\x00\x00\x00\x00\x00\x00" \
                        "\x80\x01\x00\x00\x00\x00\x00\x00" \
                        ",status:hacked";
                    printf("%s\n", md5(msg2, 78, &seed));    // strlen() тут не сработает - потому 78
                    // = 164bc99c345fd21e1e810bf9a41222b - наша подпись подошла!
                


                Единственный оставшийся здесь момент — неизвестная длина секрета. Но она, скорее всего, либо одинаковая для всех пользователей, либо варьируется не сильно.
                • 0
                  Огромное Вам спасибо! Вы мне очень помогли — многое стало на свои места.

                  P.S. У Вас новый хеш обрезан. Должен быть 164bc99c345fd21e1e810bf9a41222b9.
          • 0
            sig — подпись чего именно?
            • 0
              sig = md5(секретный_ключ+значение_параметра_param). Ключ и значение параметра соединяются простой конкатенацией без разделителя.
  • 0
    Есть другие интересные примеры в несовершенстве криптографических систем.
    Например, шифрование DVD, WPA, padding attacks.
  • +2
    «Атака по времени», с ума сойти… Это уже не криптография, это уже социальная инженерия какая-то. Короче, я некомпетентен, но я в восторге.
    • +5
      Есть целый класс атак, называемых Side Channel Attack (атак по побочным каналам). Вряд ли это социальная инженерия, это атаки, которые опираются не только на математическую базу, но и на аппаратные (физические) специфики реализации и работы каждой конкретной железки.
      Очень интересны атаки по энергопотреблению, если реализация «лобовая», то такой атакой из черного ящика очень хорошо ключики асимметричных алгоритмов вытаскиваются.
    • +3
      Была замечательная статья (ссылку потерял) когда взламывали зашифрованный VoIP канал. Суть в том, что в канале использовалась технология VAD (Voice Activity Detection) которая уменьшает трафик когда речь не передаётся. По колебаниям объёма трафика, которые совершенно одинаковы как в открытом, так и и в шифрованном сообщении, оказалось возможным распознавать какие звуки произносят (всё по полной программе — строится скрытая марковская модель и так далее). То есть, удалось узнать текст, который зачитывается по зашифрованному каналу, вообще без взлома криптосистемы.
      • +4
        Уж простите, но я на это скажу «бред».
        VAD останавливает передачу пакетов при снижении уровня громкости до определенного уровня. Кстати, его уже почти никто не использует — даже с генерацией комфортного шума эта технология очень раздражает собеседников. Колебания трафика от VAD — «вкл-выкл».
        Во время передачи пакетов размер у всех пакетов всегда один и тот же. Я допускаю, что есть исключения, но я о них никогда не слышал. Скажем, 10 байт на 10мс у весьма эффективного g.729.
        Допустим, можно выделить конкретный RTP поток из шифрованного туннеля и с абсолютной точностью узнать размер каждого из пакетов. Допустим, используется кодек, у которого размер пакета меняется в зависимости от «насыщенности» звуковой среды. Вы получаете цепочку цифр вида 20-20-20-17-15-12-20 (байт). Если вы построите модель, позволяющую из этой информации получить содержимое разговора, для переноса которого задействуется несравнимо больше битов информации (160 бит против, скажем, 6 бит) — смело патентуйте, будете миллиардером.
      • +6
        Вы, видимо, вот эту работу имели в виду: software.imdea.org/%7Ebkoepf/papers/esorics10.pdf
        Но там речь идёт всё же об идентификации говорящего (по характерному отпечатку пауз от VAD), а не о раскрытии текста.
        Вот ещё одна аналогичная работа 2007 года с тестами на SIP и Skype — etd.ohiolink.edu/send-pdf.cgi/Lu%20Yuanchao.pdf?csu1260222271

        Но интереснее всего, пожалуй, опубликованная в 2011 работа, в которой пишут о распознавании фраз в зашифрованном потоке.
        Они использовали не следы от VAD, а информацию о фонетическом произношении слов. При этом ни образцов голоса, ни образцов произношения входящих во фразу слов алгоритму не требуется. А атака стала возможной из-за комбинации VBR сжатия и сохраняющего длину шифрования.
        www.cs.unc.edu/%7Efabian/papers/tissec2010.pdf
        • 0
          Видимо в памяти перемешались две этих статьи :)

          Спасибо.
      • +4
        Когда я на почте на флоте служил ямщиком
        Короче, связистом я служил в ракетном полку.
        У нас там закрытый канал связи все время был чем-то занят: либо действительно что-то полезное передавал, либо что-то бесполезное, но суть как раз и была в том, чтобы затруднить обнаружение сообщения в канале связи.
        • 0
          Как в общих чертах генерировали бесполезные сообщения?
          • 0
            Не буду врать — не знаю. Нам (солдатам/сержантам) так глубоко в систему заныривать не давали.
  • 0
    del
  • 0
    Против атаки по времени зачем делать xor, если можно сделать sleep на случайное число миллисекунд?
    • 0
      На целое случайное число миллисекунд, ага. В принципе можно, наверное, но это костыль уже.
      • 0
        В мультизадачной среде SLEEP никогда не даст целое число миллисекунд, обычно это означает ОТ заданного количества миллисекунд и выше, конец такой задержки зависит еще и от выполнения других задач.
        Плохо то что на серверах с высокой нагрузкой таким способом не отделаешься — тормозить все начнет безбожно.
    • +12
      если повторить один и тот же запрос тыщу раз, то ваш рандом будет в среднем одинаковый
      • 0
        Если делать sleep на случайное число миллисекунд от 1000 до 2000, то на повторение запроса 1000 раз уйдет не менее 20 минут. А 1000 раз — это мало. И это только один запрос, нам надо больше, намного больше…
        • 0
          Можно повторять запрос в несколько (десяток, сотен, тысяч) потоков
          • 0
            Это сложно отловить на стороне сервера? И эта многопоточность никак не скажется на времени, которое требуется серверу для обработки запроса?
            • 0
              Что проверять на стороне сервера? Брутфорс через ботнет?
            • +4
              Отлавливать это на стороне сервера — это костыль. Попытка закрыть несовершенство криптосистемы какими-то дополнительными средствами, которые к собственно криптографии никакого отношения не имеют. Вы получите странный, трудно поддерживаемый код.

              Вместо разработки костылей проще и надёжнее взять готовую нормальную систему.
              • –2
                Я не очень рассылшал, основной посыл статьи «Не используйте доморощенные решения, возьмите готовую нормальную систему» или «Не занимайтесь криптографией сами, наймите специалиста»?

                Это какие-то два разных заключения…
              • 0
                «Готовой нормальной системой» надо ещё уметь пользоваться.

                Берём какой-нть Triple DES, неправильно выбираем константу режима шифрования (ECB вместо CBC) — и получаем шифровку, которую можно спокойно резать на части и собирать новое сообщение из нескольких исходных — www.codinghorror.com/blog/2009/05/why-isnt-my-encryption-encrypting.html
    • +23
      Против атаки по времени зачем делать xor, если можно сделать sleep на случайное число миллисекунд?

      Вы опасно некомпетентны в криптографии)
      • 0
        А что если сервер будет сначала помещать ответ в очередь, а извлекать его, только если текущее время кратно, к примеру, 100мс?

        А что если сервер под такой атакой по времени тупо просядет? Как атакующая сторона справится с этим?

        А что если отвечать со все увеличивающимся на случайную величину временем, если предыдущий запрос оказался невалидным?

        Я опасно некомпетентен в криптографии? (:
      • 0
        Да еще вчера понял, что это сильно усложнит, но не сделает невозможным статистический анализ. Как реальное практическое решение, лежащее за пределами чистой криптографии — оно неплохое. Но если находиться только в рамках криптографии — то да, я сморозил ерунду.

        P.S. Под sleep можно понимать не только задержку на целое количество миллисекунд, но и какую-нибудь реализацию выполнения «мусорных» команд, занимающих такты процессора.
  • 0
    Подпись — это MD5 от строки, состоящей из общего секрета и добавленных за ним следом пар ключ-значение.

    Хмм… мне казалось, что обычно под подписью подразумевается, зашифрованное секретом, MD5 от строки с данными, а не MD5 от секрета + данные. Хотя моя компетентность находится где то между 1 и 2, так что я не претендую :)
    • +1
      Ваш способ требует два криптопримитива (хеш и симметричный шифр).
      Чисто академически интереснее решение, как можно более простое, но не менее надёжное.
      • +2
        Но статья об использовании криптографии в реальной жизни, а не в академических изысканиях.

        Просто ход рассуждения автора немного странный, сначала описывается не совсем стандартный подход для реализации подписи (по крайней мере на мой взгляд), а потом показывает его несовершенство и в результате делает вывод, что для использования криптографии нужно подробно разбираться в алгоритмах, возможных атаках и т.д. В реальности в подавляющем большинстве случаев достаточно знать некий набор «криптографических паттернов» (типовые задачи и типовые способы их решения). А если уж пошел изобретать велосипед, тогда да, нужно уметь разбираться в деталях, иначе ССЗБ.
        • 0
          Ограничения здесь не обязательно искусственные.
          Например, в некоторых странах есть запрет на экспорт сильной криптографии. И вам могут просто не дать выпустить на международный рынок продукт, оборудованный «по последнему слову криптограферской моды».

          Да и статья ведь немного не о том. В ней не говорится, что решения не существует. Основной её мотив в том, что даже в самых известных подходах и алгоритмах (как в общем-то, и в любом «паттерне», о которых вы пишете) могут быть лазейки, о которых вы не знаете.

          Я уверен, что до определённого времени вариант MD5(key | message) был вполне себе «паттерном». Примеры Flickr, Vimeo и RTM нам на это как бы намекают.
  • –1
    Поэтому используйте Кетчак или Sha256|512 :D
    • 0
      Разные хеш-функции погоды не сделают. Речь идет о несовершенстве алгоритма их использования
      • +1
        Для Keccak (sha3) length extension attack не работает (по крайней мере в лоб).
  • +2
    Интересно, а атаке Length extension attack подвержена смена местами данных и секрета (постфиксное положение секрета)?
    • +1
      Конкретно от угрозы length extension это избавит.

      Но у варианта H(message | key) есть свои проблемы. В частности, поскольку message (а значит и его хэш) взломщику известен, он может найти такое сообщение message2, которое будет давать то же самое значение хэша: H(message) = H(message2). А значит и после приклеивания в конце ключа хэши будут совпадать. Т.е. подпись от message можно будет использовать для отправки message2.

      Вариант H(key | message | key) — лучше. Но и у него были обнаружены уязвимости, даже когда ключ-префикс и ключ-суффикс различаются.

      Упоминаемый в статье HMAC использует, на самом деле, схему H(key1 | H(key2 | message)), где key1 и key2 особым образом получаются из исходного ключа.
      • +3
        Ну все-таки, найти два разных сообщения, дающих одинаковый хэш весьма затруднительно насколько я знаю.)
        • +1
          Ну да, это не совсем 2+2.
          Но в случае всё того же MD5 — вполне реально: en.wikipedia.org/wiki/Md5#Collision_vulnerabilities
          • +1
            В случае MD5 вполне реально найти два СЛУЧАЙНЫХ блока, дающих одинаковый хэш, отличающихся всего на пару битов. А мы говорим о подборе пары для заведомо известного блока, а такое даже MD5 себе не позволяет)
            • 0
              Вы по ссылке-то ходили?
              В 2005 исследователи смогли создать пару PostScript документов, имеющих одинаковый MD5-хэш, а также пару X.509 сертификатов с совпадающим хэшем.
              А в 2008 обычный SSL-сертификат был успешно превращён в рабочий CA-сертификат якобы от имени того же самого провайдера.
              • +2
                То о чем вы говорите элементарно сделать, имея два блока, порождающих коллизию. Я просто когда то интересовался этим вопросом. Если интересно, вот мой давний пост о том как это можно сделать самостоятельно habrahabr.ru/post/113127/
                • 0
                  Хорошо, с парами файлов всё понятно.
                  Но как по-вашему они сертификат меняли? Или вы думаете, что там они тоже сначала сгенерировали пару _случайных_ блоков?

                  Пишут, кстати, что для взлома использовали кластер из примерно 200 Sony PS3, который работал 3 дня. И это в 2008 году! Спустя два года после описанного в вашем посте:
                  в 2006 году чешский криптограф Властимил Клима предложил для поиска коллизий новый метод, позволяющий найти разную пару случайных 128 байтных блоков с одной md5 суммой на персональном компьютере меньше чем за минуту

                  Думаете, им просто хотелось кластер погонять?
                  • +1
                    Ох, ну давайте разбираться по порядку.)

                    Во-первых, есть так называемая collision resistance, а есть preimage resistance. И хотя эти понятия легко перепутать, отличия весьма существенны.
                    Так, collision resistance это способность хэш-функции противостоять случайным коллизиям, т.е. применительно к MD5 ни о какой collision resistance говорить не приходится.
                    А вот preimage resistance это уже как раз то, о чем мы с вами в данный момент спорим, т.е. способность найти по данному x такое x`, что x`!=x, но H(x`)=H(x). Так вот такие атаки на MD5 известны, конечно но они очень затратны. По той же ссылки, которую вы любезно привели выше, написано:
                    In April 2009, a preimage attack against MD5 was published that breaks MD5's preimage resistance. This attack is only theoretical, with a computational complexity of 2123.4 for full preimage.

                    Сами понимаете, тут никакой кластер не спасет.

                    Во-вторых, атаки на collision resistance подразделяются в свою очередь на два вида Classical collision attack и Chosen-prefix collision attack. Так вот, атаки первого типа на MD5 не требуют практически никаких усилий. Коллизии находятся даже на не самом сильном ПК за считанные секунды. К сожалению они неприменимы для подделки SSL-сертификатов, в силу особой структуры файлов.
                    Но к счастью или несчастью, как посмотреть, есть еще Chosen-prefix collision attack, сложность которой для MD5 составляет примерно 250.
                    Так что нет, я не думаю что им просто захотелось погонять кластер.) Просто, продемонстрировали практическую реализацию Chosen-prefix collision attack.
                    • 0
                      Вернёмся теперь к тому, с чего это ветка начиналась.
                      У вредителя есть сообщение message1 и его валидная подпись. На основании этого он генерирует новое сообщение message2, которому будет подходить та же самая подпись.
                      Ну оставит он неизменным тот же самый «chosen-prefix», а потом (за счёт 250) добавит к нему какой-то мусорный параметр, в который спрячет все лишние байты, а вместе с ним ещё один параметр типа «action=delete».

                      Вот и получается, что перехватив сообщение message1 = «action:add ...» его можно превратить в message2 = «action:add… action:delete ...», имеющее ту же самую подпись.
                      Что это, как не та самая проблема схемы H(message | key), о которой я писал выше?
                      • +1
                        В H(message|key) как раз этой проблемы нет. Она есть в схеме (key|message), где мессадж можно растягивать, т.к. он последний.

                        «В частности, поскольку message (а значит и его хэш) взломщику известен, он может найти такое сообщение message2, которое будет давать то же самое значение хэша: H(message) = H(message2). А значит и после приклеивания в конце ключа хэши будут совпадать»

                        а это preimage, а не (chosen-prefix) collision
                        • 0
                          Постойте. Речь не шла о length extension attack на H(key | message)
                          Там ведь и коллизий-то никаких не надо — растянул сообщение, обновил хэш и отправил на сервер (уже с _новой_ подписью)

                          А в схеме H(message | key) как раз за счёт коллизий можно подменить message, оставив подпись без изменений. О том и речь была:
                          Конкретно от угрозы length extension это избавит. Но у варианта H(message | key) есть свои проблемы. ...


                          (может быть, я некорректно назвал это «chosen prefix»-ом в предыдущем своём комментарии)
                          • +1
                            Да, вы некорректно назвали это «chosen prefix», т.к. это классический пример preimage взлома. Создать пару для message, такую что H(message)=H(message2). Это уже не коллизия, это именно второй прообраз. Мне кажется вы не улавливаете разницу.
                          • +1
                            вот именно об этом и речь, вы назвали это chosen prefix, а это «chosen postfix», т.к. ключ в конце. И атака превращается в preimage, которая сложная как 2^123
                            • 0
                              Вы правы. Разобрался с chosen prefix / preimage. Спасибо.

                              Но, насколько я понимаю, это всё равно не отменяет уязвимости схемы H(message | key):
                              appending the key using MAC = H(message | key), suffers from the problem that an attacker who can find a collision in the (unkeyed) hash function has a collision in the MAC

                              Именно о ней я и писал в самом начале этой ветки.
                              Собственно, потому в HMAC и выбрали другую схему — H(key1 | H(key2 | message))
  • 0
    посыл статьи… носит слегка рекламный оттенок («оставьте криптографию нам, экспертам»)

    в комментариях к статье автор явно говорит, что сам не является экспертом по криптографии.
    • +4
      Но можем ли мы слепо веровать, что это его собственный посыл, или же скорее мы опасно некомпетентны в маркетинговом FUD?
  • +13
    Инетерсно, но сильно преувеличено эмоционально. В безопасности меня не устает поражать гигантская пропасть между теоретической криптографией в вакууме и каким-нибудь практическим экспертом вроде Ganesh Krishnan, который с докторской степенью и 20-ю годами работы в качестве главного безопасника не где-нибудь, а в Yahoo и LinkedIn, умудрялся тупо хранить пароли без соли.

    Акцент на теоретические атаки любопытен, но уж никак не на уровне заламывания рук «не используйте, это плутоний», «ради всего святого» и т.д. На практике же надо будет всегда сопоставлять стоимость защиты информации со стоимостью самой информации, учитывать тот факт, что нанятым специалистом (хорошо-хорошо, я сам не прикоснусь к криптографии, обещаю!) окажется Кришнан, и что бой хакеров и защитников не вчера начался и не завтра завершится. Сбои и дыры в конкретных реализациях алгоритмов и на специфических платформах, промахи «специалистов», тупые пользователи и неадекватные полиси, раскручиваемые социальной инженерией, были всегда, а сказать «вы не знаете всего» можно было бы и покороче.
    • +1
      Да, да, и еще раз да!
      Всегда возмущало, когда код написаный для некоторого клиента, перед заливкой на его сервера сертифицируется, проверяется аудитором, и т.д. и т.п. На каждом этапе от теста до продакщн тебе все вставляют палки в колеса начиная от простого админа DB, заканчивая их CIO или CSO.

      Потом приезжаешь ты к клиенту в командировку, и что: стандартный браузер IE7, где есть огнелис, он говорит что flash устарел до такой степени, что лиса уже его отключила, а пароли вообще сохраняются в винде…
      • +1
        Ну как тут не вспомнить требования одной милой компании по полной отчетности о процессе разработки ПО, включая ISO 13485, FDA Part 11, HIPAA и прочие КПСС, валидации партнерами всего софта вплоть до Экселя включительно (!!!)… и неубиваемый внутренний корпоративный стандарт на использование IE 6. Стоял 2010 год…
  • +1
    -удален, дошло про сравнение-
  • 0
    В принципе, все это уже далеко не откровения. Но напоминать себе об этом никогда не лишне, а то бывают иногда сомнительные мысли чего-нибудь своего этакого изобрести.
    P.S. кто еще на Matasano Crypto Challenges подписался?)
    • +1
      йа :) решил за пару часов первый пак, жду второй ^_^
  • +2
    Очередное доказательство аксиом:

    1. Криптография по-прежнему остаётся больше математикой, нежели программированием.
    2. Стойкость криптографии смягчается безмозглостью пользователей.


    Самое печальное (и это отмечается в статье), что некомпетентность в вопросах криптографии имеет место даже в компаниях-гигантах вроде Yahoo и LinkedIn. Придётся сформулировать и запомнить третью аксиому: (не)серьёзность отношения к криптографии не зависит от именитости компании.
    • 0
      А иногда рядом даже known_hosts лежит
      • 0
        del
    • 0
      Гениально! Я про безмозглость пользователей
  • 0
    Против атаки по времени – можно сравнивать хеши 2х строк.
  • 0
    А если сделать вот так:
    md5(md5($secret.'foo:bar').$secret.'foo:bar');

    Сам я не имея никаких знаний о криптографии уже 3 года использую: md5($this->o['secret_part1']. md5($mes. $this->o['secret_part2']. $this->id));
    $this->o['secret_part1'] и $this->o['secret_part2'] естественно уникальны и строго зависят от $this->id.
    Да я некомпетентен в вопросах криптографии и много чего еще незнаю. Но параноик внутри меня всегда не доверял простому хешу md5 в условиях растущей производительности вычислительных машин.
    • 0
      Навскидку:
      1) если вы результат вычисления сравниваете обычным == — timing attack, получите и распишитесь
      2) пример у вас в первом абзаце выглядит лучше*, чем тот вариант, что вы «используете уже 3 года»
      если в вашем внутреннем вызове md5($mes. $this->o['secret_part2']. $this->id) заменить значение $mes на другое, имеющее такой же хэш, подпись по-прежнему будет валидной

      * в общем-то, защищённый от extension-атак HMAC использует очень похожую схему: H(key1 | H(key2 | message))
      • 0
        сравниванию ===, но там еще ждать ответа от базы данных и в месте с пингом думаю сложно тайминг атаку сделать. Хотя, прочитав вашу статью руки чешутся, на локальном проекте попробовать сделать)). В моем случае $mes — это короткое 32битное сообщение разделенное спец символами с проверкой валидации, передается строкой в 16 байт. Поэтому не думаю кому-то подфартит с секретными ключами настолько, чтобы провести атаку и пройти валидацию.

        Благодарен за ответ, обязательно буду использовать ваш опыт.
        PS. вот сейчас подумал. Ведь так как md5 хеш имеет ограниченную длину, то вероятность получить нужный нам $mes(тот при котором подпись останется такойже) простым перебором функции md5(md5($secret1.$mes).$secret2.$mes') с разным сообщением примерна равна и md5($mes. $this->o['secret_part2']. $this->id) Разниться только в нужных мощностях для перебора.
        • 0
          … думаю сложно тайминг атаку сделать. Хотя, прочитав вашу статью руки чешутся, на локальном проекте попробовать сделать
          см. выше и ветку вниз — не могу даже тенденцию выявить.
  • +3
    Мы обычно к HMAC добавляем таймстемп. Во-первых защищает от отложенных атак (reply), во-вторых защищает от бесконечного брутфорса.

    А предложение «не используйте криптографию если вы не бог в ней» неконструктивно. Лучше не идеальная криптография, чем никакая. (аналогично: лучше взламываемая железная дверь, чем пустой дверной проём — хоть сквозняка не будет).
    • +2
      Вопрос в иллюзии безопасности. Если двери нет, то вы не будете хранить в квартире что-то ценное. Если есть железная дверь, которую откроет ногтем любой школьник, но вы об этом не знаете — это опаснее, это расслабляет.
      • 0
        А вот моё бытовое мнение состоит в том, что лучше иметь фанерную дверь, чем её отсутствие. Хотя бы потому, что зайти и взять — это один состав, а открыть ногтём и взять — другой. (воровство и грабёж).

        С криптографией так же. Зайти и взять по http плейнтекстом — это одно (не наказуемо, «по понятиям»). Взломать шифр — это уже откровенная агрессия.

        Да и сравните сложность «пойти и взять» с «арендовать сервер в том же ДЦ и устроить гигантскую timing-атаку».

        Криптографическое «всё или ничего» спокойно сглаживается «мне 500 грамм того, и вот того, и ещё сверху столько же», после чего система становится более-менее для практических целей защищённой.
        • +1
          Зайти и взять по http плейнтекстом — это одно (не наказуемо, «по понятиям»).

          Статья та же, что и при атаке на TLS. Никого не волнует метод, важен лишь результат. Если у вас была распахнута стальная дверь, в доме никого не было, к вам зашли и вынесли телевизор, то это то же самое, что и «взломать замок».
          арендовать сервер в том же ДЦ и устроить гигантскую timing-атаку

          Ну не верю я в практическую применимость данного класса атак.
          • +2
            Нет, кража и кража со взлом — разные статьи. Так же как кража и воровство и грабёж — разные статьи с разной степенью ответственности.

            Я вообще про другое говорил. Иметь какую-то криптографию лучше, чем передавать секрет плейнтекстом.
    • 0
      Мы обычно к HMAC добавляем таймстемп. Во-первых защищает от отложенных атак (reply), во-вторых защищает от бесконечного брутфорса.

      И часы синхронизируете по защищённому протоколу? Всё как положено?

      Не совсем понятно, как это защищает от «бесконечного брутфорса»?
      Насколько я понял, вы говорите о том, что клиент добавляет к запросу временную метку, а сервер пропускает только те запросы, у которых метка не сильно отличается от ожидаемой? (HMAC здесь тогда особо и ни при чём)
      Что мешает «брутфорс-клиенту» обновлять эту метку в своих запросах с какой-то периодичностью? (лишь бы попадала в «окно сравнения»)
      • 0
        Что-то я торможу, извините. Конечно, «брутфорс-клиент» не может обновить временную метку, потому что она подписана вместе со всем сообщением (тем самым HMAC).
      • 0
        В нашем случае HMAC-сообщение используется для доказательства, что А и Б — это одно и то же. Читай — А запросил (по доверенному каналу), мы выдали подписанное. Б предъявил, мы ему поверили.

        Интрига ещё сложнее, с учётом, что сообщение для А выдаёт один сервис, а проверяет у Б — другой. Между обоими сервисами общий секрет, плюс более-менее синхронизированное время.

        Часы синхронизируются с местечкового NTP по серой сетке, с которого синхронизируются все dom0, а виртуалки доверяют гипервизору, на котором запущены.
      • 0
        И часы синхронизируете по защищённому протоколу? Всё как положено?
        Мне кажется, что мы можем допустить рассинхронизацию часов между клиентом и сервером на некоторое количество минут m. Если клиентский timestamp вылезает за этот порог, то считаем его невалидным. Если не вылезает, то проверяем список HMAC запросов от этого клиента за последние 2m минут. Если нашли совпадение, то это replay-атака. Если не нашли, то можем выполнить запрос.
  • +1
    вот прям браво!

    за время своего аспирантства чего только не видел… и ведь верят люди, что изобрели «принципиально новый алгоритм» и седые дядьки им в ответ головой кивают. Шнайер метко называет это «псевдонаучной болтовней»: некомпетентный изобретатель + некомпетентный слушатель = восторженная околонаучная возня.

    «не знаешь — не лезь», в граните бы высек эти слова.
    • +1
      Как вы думаете, Тьюринг, который ломал Энигму — он «знал»? или «полез не зная»? Любой другой учёный — он знал или нет?

      Первые два абзаца — ок, нет вопросов, последний абзац — песнь из разряда «не раскачивайте лодку» и «всё уже изобретено до вас».
      • –1
        отец криптографии не мог «не знать».
        хороший, кстати, пример со взломом: «знаешь»? — докажи! сломай что-нибудь! ну или хотя бы какое-нибудь доказательство приведи, что тебя стоит слушать.
        • +3
          Отец криптографии очевидно не знал как устроена энигма и остальные немецкие шифры. И узнавал только сильно в процессе. Любое познание начинается с «не знаю» и «хочу знать».

          Грубо говоря, откуда появилось то знание, которым теперь жирно потряхивают эксперты? Было спущено на каменных скрижалях по вертикали власти? Приснилось пророку и с тех пор все строго следуют?

          Некомпетентность одно. Желание узнать, придумать что-то новое и разобраться — совсем другое.
          • 0
            почему вы говорите «знать» == «знать энигму», а не «знать» == «понимать криптографию»? мне казалось, я достаточно ясно ввел контекст «знания».

            Желание — оно, безусловно, похвально, однако «не знаешь — не лезь» не значило «не знаешь — не старайся узнать».
            На пальцах:
            Знаешь? точно? посмотри сюда, сюда и сюда и подумай еще раз.
            Все еще считаешь, что умеешь? Ну тогда не обижайся.
  • +7
    Почему здесь сказали про абсолютно нереализуемую в этом случае тайминг атаку и забыли про очень даже реальную replay атаку? Если схема аутентификации не предусматривает создания сессии с пронумерованными сообщениями, атакующий может запомнить и повторить действия клиента. Это во многих случаях эквивалентно получению доступа.
  • 0
    Коль пошла такая пьянка, хочу снова попробовать обратиться к экспертам, если здесь такие присутствуют.

    Есть такая OpenSource программа MyTetra. Данные пользователя могут храниться на открытых для всех на чтение бесплатных Git-репозитариях, например на GitHub.com. Приватные данные шифруются с помощью самописной библиотеки RC5Simple.

    Схема шифрования следующая:

    Данные шифруются по алгоритму RC5-32/12/16 c CBC-режимом сцепления (библиотека RC5Simple), ключ генерируется на основе пароля с солью, пропущенного через алгоритм PBKDF2 на 1000 раундов с длиной ключа 160 бит. Для генерации ключа шифрования в 128 бит, от результата берется MD5 сумма. Каждая запись шифруется с уникальным инициализирующим вектором.

    Хотелось бы оценить две вещи:

    1. Правильность реализации RC5. Сама библиотека RC5Simple имеет всего 20Кб C++ кода. Хотелось бы провести ревизию кода/алгоритма специалистом, который разбирается в вопросе.

    2. Оценка стойкости данного метода шифрования. Меня беспокоит тот факт, что открытый текст всех зашифрованных записей одинаковый — обычный HTML заголовок. В принципе, он конечно перемешивается за счет инициализирующего вектора (другими словами — соли), но вектор хранится ведь в открытом виде. Поэтому, сверху накладывается PBKDF2. Достаточно ли этого всего для того, чтобы при хорошем пароле на современных средних мощностях невозможно было подобрать пароль ну пусть в течении 100 лет?

    Я уже обращался к экспертам на Хабаре:
    http://habrahabr.ru/post/124090/#comment_4089407
    но в ответ была только отписка.
    • +2
      Я не эксперт, но на месте эксперта мне было бы лень…
  • 0
    Мне интересно, а насколько профессиональные криптографы математики?

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