Forward Secrecy и Perfect Forward Secrecy

Forward Secrecy и Perfect Forward Secrecy - есть ли различие и в чём оно?

Терминологические вопросы в IT – штука очень серьёзная; незнание или некорректная трактовка каких-либо терминов может привести к принятию неправильных решений и прочим неприятностям.

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

Поговорим немножко о термине Forward Secrecy и тонкостях использования оного.

Необходимость Forward Secrecy

Представьте себе простую ситуацию – сервер платёжной системы, предоставляющий HTTPS-доступ к личному кабинету.

Этот сервер принимает входящие подключения по SSL/TLS, отрабатывая стандартную логику генерации ключа для каждой сессии – берёт premaster secret (48 байт, создаётся клиентом и шифруется открытым ключом сервера) и случайные значения, которые посылаются друг другу сторонами в Client Hello и Server Hello, и формирует master secret.

Механизм работает отлично – исходя из тезиса клиента

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

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

Проблема только в том, что если кто-то плохой будет записывать весь трафик сессий – от первого и до (необязательно, впрочем – достаточно и “от первого”) последнего пакета, то разовый доступ к закрытому ключу сервера предоставит возможность расшифровать весь трафик – штатно, без криптоанализа.

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

Мне попадались на практике отличные примеры ведущего к такой ситуации разгильдяйства – например, когда сервер, на котором ранее была система с HTTPS-доступом, к примеру CAS от Exchange Server, после переквалификации на другую роль просто передавался другому администратору – с удалением Exchange, но без “зачистки” от старых сертификатов и закрытых ключей оных в локальном хранилище. Соответственно, система хранит просроченные сертификаты бесконечно – мало ли, что ими потребуется расшифровать? – ну а новый администратор (любой с правами локального админа) получает возможность доступа к ключевой паре (у которой, чтобы не заморачиваться на кластер CAS, стоит признак “закрытый ключ доступен для штатного экспорта”). Ну и если озаботился сохранением начала SSL/TLS-сессий, хотя бы за последний месяц, то может провернуть операцию по расшифровке.

В чём корень проблемы?

В том, что ключевая пара в данном сценарии используется для двух целей сразу – и для подтверждения подлинности сервера – “сервер, а докажи, что тот x.509v3-сертификат, который ты мне показываешь – твой, а не просто откуда-то скачанный”, и для совместной генерации master secret.

Поэтому использование схемы с “чистым RSA для TLS/SSL” достаточно быстро было вытеснено ситуацией, когда подтверждение подлинности сервера завязано на сертификат, а вот совместный секрет создаётся алгоритмами DH / ECDH.

Решило ли это проблему целиком и полностью? Нет.

Диффи, Хеллман и их друзья

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

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

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

Случай, когда при DH-обмене используется тот же ключ, что и указан в сертификате – откровенно редок и называется FDH, или Fixed Diffie-Hellman. Да, можно напрячься и сделать сертификат не с RSA-ключом, а с DH-параметрами – такое возможно, но непрактично, т.к. сертификат этот будет непригоден для операций подписи, поэтому на практике это не используется – просто зафиксируем, что Fixed Diffie-Hellman будет, безусловно, быстрее, но в силу уменьшенного функционала и безопасности (общедоступные в загружаемом кем угодно сертификате постоянные параметры, притом с фиксированным сроком устаревания – синхронно с сертификатом – снижают безопасность системы) использоваться не будет.

В случае же динамического выбора ключевой информации при начале каждой сессии возникает вопрос – как именно это организовать?

Можно каждый раз предлагать одни и те же параметры, а можно и сэкономить силы – делать это не всегда.

Второй вариант понятен по преимуществам – он будет явно быстрее в плане установки сессии, но будет проседать по безопасности сразу в двух вопросах – предлагаться будут параметры из какого-то заранее созданного пула (и созданные по какой-то закономерности, предположительно всегда известной потенциальному атакующему), а кроме того – получится, что компрометация ключа конкретной сессии S1 может скомпрометировать данные уже завершённой ранее сессии S2, или подставить под угрозу данные ещё не начатой сессии S3.

Что делать?

Forward Secrecy

Сам по себе термин появляется достаточно давно, и обозначает не конкретную технологию, а признак, связанный с организацией безопасного соединения. Соединение считается подпадающим под forward secrecy в том случае, когда:

  • Используется какой-либо алгоритм с открытым ключом (RSA, DH – без разницы);
  • После работы алгоритма с открытым ключом создаётся материал для секретного ключа;
  • Этот ключ не является плодом какого-либо детерминированного алгоритма;

Суть использования алгоритмов с открытым ключом в общедоступных СПД понятна – нет возможности быстро и надёжно обменяться секретными ключами вне данных каналов связи. Но после подтверждения подлинности, при помощи таких алгоритмов, возникает задача “как обеспечить безопасность конкретной сессии, защитив передаваемые данные”. Соответственно, нужно придумать схему, как разово что-то такое сделать, чтобы получился материал для создания ключа защиты. Почему материал? Потому что скорее всего ключей надо будет много, т.к. их надо регулярно менять. Поэтому нужна некая битовая строка, которая послужит стартом для этого процесса. В частном случае она и будет первым и единственным ключом для используемого криптоалгоритма с секретным ключом.

Требование “не является плодом какого-либо детерминированного алгоритма” сразу отсекает неочевидную кучу простых вариантов вида “давайте возьмём хэш от айпишника подключающегося и открытого ключа, и это будет master secret, тогда ключи у разных подключающихся будут вроде разные, а алгоритм будет быстрый и простой”, и отсекает все варианты “сессионный ключ вычислим из некоего главного ключа”.

Методов создания сессионной ключевой информации – множество, и под Forward Secrecy будут попадать те из них, кто будет создавать для каждой сессии новый ключ, из которого не будут вычислимы ключи предыдущих/последующих сессий.

Perfect Forward Secrecy

Но стоп – скажет внимательный читатель – одно дело “каждый раз для сессии вся информация будет придумываться с нуля”, а другое – “чтобы из придуманного каким-то образом ключа были не вычислимы предыдущие/последующие”. Возможно, отдельно существующий термин Perfect Forward Secrecy как раз описывает “самую максимально безопасную схему”, когда “с нуля” придумывается абсолютно всё?

Нет, данный термин является лишь ещё одним (скажем честно – менее удачным, т.к. громко звучащим на пустом месте) названием той же Forward Secrecy.

Более того – если разбираться в деталях, то в любом случае “генерировать для каждой сессии полностью случайные данные для DH-общения” не будет реализовываться на 100%. У алгоритма в любом случае будет известное основание – обычно это двойка или пятёрка, плюс известное большое простое число, которое сервер берёт из заранее созданного пула таких простых чисел. Можно лишь улучшить ситуацию, регулярно (да хотя бы разово, да) пересоздавая такие числа – но на 100% “для каждой сессии с нуля” ничего не получится в силу математической сложности проблемы “проверить новосозданное огромное предположительно простое число на простоту”.

Поэтому на данный момент можно считать PFS и Forward Secrecy синонимами.

Совместное использование ключей и секретов

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

  • Кластеризация – когда у N серверов, выполняющих роль front end, идентичный сертификат, а следовательно идентичный кэш ключей сессий либо механизм оперативного обмена данными этих сессий;
  • Session Tickets – например, по RFC 5077, когда для быстрого восстановления прерванной сессии сервер хранит криптографические параметры всех ускоряемых сессий в каком-то хранилище, защищаемом специальным общим ключом (см. мою статью про настройку Session Tickets на Windows Server);

В каждом из этих случаев экземпляров ключей получается много, но PFS необязательно считается “не работающим” – всё зависит от специфики хранения ключевого материала. Я специально явно подчёркиваю разницу между:

Компрометация ключа конкретной сессии S1 может скомпрометировать данные уже завершённой ранее сессии S2, или подставить под угрозу данные ещё не начатой сессии S3;

и

У любой сессии 100% ключевого материала создаётся “с нуля”.

Это – разное. Если утечка ключевой информации на одном из узлов кластера никак не влияет на другие сессии – ни на один бит не сокращая пространство для линейного криптоанализа, например – то PFS работает, хоть ключ и во многих копиях. Если материал session tickets хранится в оперативной памяти сервера – или на диске, но используемый для расшифровки ключ – в оперативной памяти, то PFS работает. Если же ключ хранится на той же системе постоянного хранения, что и зашифрованные им закэшированные session tickets – то по сути это не PFS.

Особый случай – wPFS, Weak perfect forward secrecy

Вообще, сама комбинация “weak, но perfect” уже звучит дурацки.

Weak perfect forward secrecy – это попытка определить частичный PFS, в котором если долгоиграющие ключи/исходные биты были компрометированы, то PFS выполняется для части уже прошедших сессий – только для тех, с которыми злоумышленник не взаимодействовал ранее.

То есть если предположить, что существует сверххитрый злоумышленник, который может вмешиваться в процедуру установки сессий, и выборочно “подгаживать” – например, влияя на согласование ключей, или выбор алгоритма (такой класс атак реально существует, например в локальных сетях согласование NTLMv2 умышленно “сбивают” внесёнными ошибками до согласования LM, который потом просто взломать), то wPFS описывает систему, которая гарантирует PFS, но для сессий, с которыми такого взаимодействия не было. По сути прошедшие сессии делятся на “которые уберегли” и “которые не уберегли”, и вторые объявляются уязвимыми, но все равно весь процесс называется PFS.

Лучше всего не считать такую ситуацию forward secrecy, уж тем более – не perfect.

DH vs EDH, ECDH vs EECDH/ECDHE

Оба использующихся сейчас варианта алгоритма Диффи-Хеллмана – что обычный (prime field) DH, что базирующийся на вычислениях с эллиптическими кривыми (ECDH) имеют субварианты с ещё одной буквой Е, переводящейся как “эфемерный”. В варианте DH это обычно записывается как EDH (или DHE, что ошибочно иногда трактуется как Diffie-Hellman Exchange), в варианте ECDH – как EECDH или ECDHE. Сути местоположение добавленной буквы Е не меняет.

Иногда данные варианты трактуются замечательно – опять же, как в рассмотренном ранее случае, им присваивается способность “на 100% генерить полностью новые данные для DH-обмена”. Это, безусловно, не так – по сути, данная “эфемерная” модификация лишь сообщает, что серверная сторона предположительно никуда не будет сохранять и использовать в дальнейшем ключевую информацию для данной конкретной сессии. Очевидно, что клиент не может приказать серверу хранить ключевую информацию каким-либо образом – поэтому наличие в SSL/TLS cipher suite указания “использовать EDH”, по сути, является рекомендацией.

Что ж, с терминологией и теорией разобрались – перейдём к практическим рекомендациям.

Что делать на практике?

Не используйте анонимный, “без аутентификации”, DH. Этот существующий в теории вариант “пропустить подтверждение подлинности сервера и начать генерить общий секрет с чем-то, что с другой стороны провода” на практике бесполезен и вреден. Ситуация “безопасный канал поставить можно и нужно, а вот как-то подтвердить подлинность того, с кем устанавливаем – нет, пропустим этот шаг” не должна возникать вообще, поэтому явно отключите ADH. В случае, например, использования библиотеки openssl надо будет указать в строке “какие криптографические алгоритмы можно использовать” символы “!aDH”;

Не используйте DH с “фиксированным” ключом – т.е. избегайте сертификатов, в которых ключ для DH-обмена вписан в сам сертификат. Это, в принципе, и так редкость, но в реальности, используя, например, Microsoft’овский CA можно вполне выпустить такой и подписать его ключом доверенного CA, после чего использовать для какого-нибудь SSL/TLS-сервиса. Дополнительной мерой безопасности будет явное удаление из списка поддерживаемых cipher suite’ов всех, начинающихся с DH_RSA_ – это можно сделать, например, через ATcmd, как описано в статье про тюнинг SSL/TLS на Windows Server;

Используйте только достаточно стойкие (не менее 2048 бит) и “не встроенные” DH-группы – например, создайте их для безопасных подключений к вашему web-серверу командой openssl dhparam -out dh2k.pem 2048 или, если хочется сразу 4096-битные, openssl dhparam -out dh4k.pem 4096; в случае генерации модулей для sshd будет чуть иначе, что-то типа:
ssh-keygen -G m2k.draft -b 2048
ssh-keygen -T m2k -f m2k.draft

Вы не можете повлиять на то, что часть параметров DH-обмена известна нарушителю – но можете, как минимум, серьёзно усложнить ситуацию, сделав регулярную перегенерацию ключевого материала для DH-обмена.

Как узнать, поддерживается ли PFS моим сервером

Есть удобный инструмент от SSLLabs, которым можно протестировать свой сервер и увидеть, какие cipher suites предлагаются для согласования – а уже из этого будет сделан вывод, поддерживается ли PFS и обязателен ли он. Увы, в том случае, если вашему серверу необходимо принимать входящие подключения по протоколу TLS 1.0, то обязательной для поддержки будет комбинация TLS_RSA_WITH_3DES_EDE_CBC_SHA, что приведёт к ситуации “PFS есть, но не во всех случаях”. Это некритично, т.к. решается приоритезацией cipher suites, и обычные клиенты будут согласовывать быстрые и надёжные EECDH-варианты.

Главный совет же будет простым – старайтесь использовать PFS везде, где только можно. Надеюсь, что после данной заметки представление вида “PFS = эллиптические кривые” или “PFS само по себе получается от обновления TLS” изменилось и расширилось.

Удач!

Вернуться к полному списку статей Knowledge Base @ Advanced Training

Ruslan V. Karmanov

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