Авторы этого материала приводят аргументы против стандартных механизмов шифрования ключа в OpenSSH.


Недавно злоумышленники использовали npm-пакет eslint-scope для кражи npm-токенов из домашних каталогов пользователей. В свете этого события мы занялись проверкой других подобных уязвимостей и задумались над тем, как снизить риски и последствия таких инцидентов.

У большинства из нас под рукой есть RSA SSH-ключ. Такой ключ наделяет владельца самыми разными привилегиями: как правило, он используется для доступа к production среде или в GitHub. В отличие от nmp-токенов SSH-ключи зашифрованы, и поэтому принято считать, что ничего страшного не произойдет, даже если они попадут не в те руки. Но так ли это на самом деле? Давайте узнаем.

user@work /tmp $ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): mykey
...
user@work /tmp $ head -n 5 mykey
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,CB973D5520E952B8D5A6B86716C6223F

+5ZVNE65kl8kwZ808e4+Y7Pr8IFstgoArpZJ/bkOs7rB9eAfYrx2CLBqLATk1RT/

Этот ключ зашифрован, о чем гласит одна из первых строк файла. Кроме того, в начале нет MII — base64-ключа кодирования, используемого в RSA. И, конечно же, в глаза бросается AES! Это ведь хорошо, так? И CBC, на первый взгляд, со случайным вектором инициализации. Кода аутентификации (MAC’а) нет. Ну и ладно, зато не будет никакой padding oracle атаки, верно?

Узнать, что на деле означает содержимое DEK-Info, не так-то просто. Поиск по ключевому слову «DEK-Info» в репозитории openssh-portable показывает только примеры ключей. Но суть здесь в том, что AES-ключ есть не что иное, как простой MD5-хеш (пароль || вектор инициализации [:8]). И это скверно, ведь передовые практики хранения паролей гласят, что пароли в чистом виде в силу своей низкой энтропии представляют собой плохой материал для шифрования. И, чтобы сделать его лучше, нужна затратная функция вроде Argon2. Но MD5, в отличие от последнего, легко поддается вычислению.

Единственный положительный момент в этой схеме в том, что соль помещается после пароля, поэтому вычислить промежуточное состояние MD5(IV[8:]) и подобрать пароли на его основании не получится. Но это слабое утешение, особенно в эпоху, когда нам доступны машины, выполняющие миллиарды вызовов MD5 в секунду, — больше, чем можно придумать паролей.

У вас может возникнуть вопрос, как OpenSSH дожил до такого. Увы, ответ прост: инструмент командной строки OpenSSL изначально использовал эту схему по умолчанию, и она попросту стала нормой.

В итоге справедливым становится замечание, что стандартные, зашифрованные паролем ключи ничем не лучше обычных незашифрованных просто потому, что механизм шифрования неэффективен. Однако мы высказались бы еще смелее — они хуже. И аргументировать это просто.

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

К самой паре RSA-ключей никаких претензий нет: весь вопрос лишь в методах симметричного шифрования приватного ключа. Провести описанную выше атаку, зная один лишь открытый ключ, нельзя.

Как можно исправить ситуацию?


В OpenSSH предусмотрен новый формат ключей, которым следует пользоваться. Под новым имеется в виду введенный в 2013 году. Этот формат использует bcrypt_pbkdf, по существу представляющий собой bcrypt с фиксированной сложностью, реализованный в рамках стандарта PBKDF2.

Удобно то, что вы автоматически получаете ключ нового формата при генерации Ed25519 ключей, поскольку старый формат SSH-ключа не поддерживает более новые типы ключей. Это довольно странно, ведь на самом деле нам вовсе не нужно, чтобы формат ключа определял, как работает Ed25519-сериализация, поскольку Ed25519 сам по себе задает работу сериализации. Но если уж нужна хорошая формирующая функция, то можно не заморачиваться с такими мелочами. В итоге один из ответов — ssh-keygen -t ed25519.

Если по соображениям совместимости необходимо придерживаться RSA, можно воспользоваться ssh-keygen -o. Таким образом, можно получить новый формат даже для старых типов ключей. Обновить старые ключи можно с помощью команды ssh-keygen -p -o -f имя ключа. Если ваши ключи живут на Yubikey или смарт-картах, то там эти поправки уже учтены.

Так или иначе, мы стремимся к более оптимальному выходу. С одной стороны, есть хороший пример aws-vault, в которой информация об учетных данных была перемещена с диска в связки ключей. Есть и иной подход: перемещение разработки в разделенные окружения. И, наконец, большинству стартапов следует рассмотреть отказ от длительного хранения SSH-ключей и переход на центр SSH-сертификации с ограниченным временем хранения ключей вкупе с системой единого входа. К сожалению, в случае с GitHub такой подход невозможен.

P. S. Трудно проверить эту информацию в авторитетном источнике, но, если нам не изменяет память, версионный параметр в приватных ключах OpenSSH формата PEM влияет только на способ шифрования. Однако это не играет никакой роли: проблема заключается в формирующей ключ функции, и, думаем, это еще один аргумент против обсуждения протоколов по частям. На эту тему будет отдельный пост в нашем блоге.

И напоследок — ссылка на полный ключ. Это на случай, если сегодня вы настроены что-нибудь взломать.

image

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


  1. dartraiden
    10.08.2018 22:19
    +1

    В качестве решения: ключ можно положить в KeePass (где шифрование реализовано на должном уровне, в том числе использутеся Argon2), а плагин KeeFox будет выступать в качестве агента. И автоматически расшифровывать ключ, что избавляет от необходимости делать пароль лёгким для запоминания. Убивается сразу два зайца: пароль от ключа можно сделать сложным, а сам ключ надёжно защищён, даже если при шифровании ключа и использовался MD5-хэш, и хранится в нестандартном месте.

    image
    У тебя не украдут ключ, если он не лежит в /home/.ssh/


    1. powerman
      10.08.2018 23:22
      +3

      Мне никогда не нравилась идея открыть доступ к KeePass сторонним приложениям (вроде KeeFox). Начиная с того, что они работают с KeePass по сети (пусть даже через localhost или unix-сокет, но это значит что любое приложение запущенное на этой машине тоже может подключиться), и заканчивая тем, что пользователь теряет контроль над тем, когда и какие именно пароли запрашиваются из KeePass.


      Я предпочитаю чтобы плагин в браузере просто изменял заголовок окна браузера так, чтобы KeePass мог сам автоматически подобрать подходящую запись и передать пароль в окно браузера самостоятельно. Да тоже не фонтан, текущая страница может попробовать подменить заголовок окна именно в тот момент, когда я активирую горячей кнопкой KeePass (чтобы получить пароль от другого сайта), плюс иногда передаваемые KeePass символы пароля попадают не в строку ввода пароля, а куда попало. Но, всё-равно, как по мне этот способ безопаснее.


      1. aim
        11.08.2018 00:31

        но если говорить о ssh сам keepassxc умеет быть ssh-agent.


        1. powerman
          11.08.2018 01:49

          Ну, как я понял из доки, не столько быть ssh-agent, сколько управлять им — добавлять в него ключи из базы и удалять их по таймауту/закрытию базы. В принципе, это выглядит неплохой идеей (правда, только для десктопа).


      1. dartraiden
        11.08.2018 12:03

        Извиняюсь, вместо KeeFox там должно быть KeeAgent. KeeFox это о другом.


    1. Daniyar94
      11.08.2018 00:27
      +1

      Зашифровать зашифрованный ключ который защищён паролем


      We need to go deeper


      1. herrjemand
        11.08.2018 00:37

        Приватный ассиметричный ключ который симметрично зашифрован с использованием в качестве ключа пароль.


        Password -> AES(RSAPrivKey) -> encrypted key


    1. dartraiden
      11.08.2018 12:02

      Поправка, вместо KeeFox читать KeeAgent (как обычно, думаешь о другом и на автомате это пишешь...)


  1. lassana
    11.08.2018 01:19
    +1

    Это уже было в Сипсонах: habr.com/post/181320/#comment_9852056 Стоит всё же отметить, что даже в 2018 не все приложения поддерживают новый формат приватного ключа.


  1. Sly_tom_cat
    11.08.2018 01:22
    +2

    Автор мог бы пояснить чем именно «слабость» MD5 делает «Шифрование ключа по умолчанию в OpenSSH хуже его отсутствия»?

    Ну можете вы быстро считать MD5 и что?
    Вам же пароль надо брутфорсить, а нужную (для правильной расшифровки) MD5 вы же не можете получить.
    Что вам толку от такого каким хешем пароль в ключ преобразуется? Вам же пароль надо подбирать.
    И тут уже не важно как быстро считается хеш. Если вы получили шифрованный приватный ключ, то дальше вам нужно брутфорсить пароль и медленнее он в ключ преобразовывается или быстрее — роль играет незначительную. Ну будете вы три дня пароль подбирать вместо пары часов — велика ли разница?

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


    1. powerman
      11.08.2018 02:07

      Ну будете вы три дня пароль подбирать вместо пары часов — велика ли разница?

      На самом деле разница довольно велика — у Argon2 скорость порядка 100/сек, а у MD5 порядка 10-100 миллиардов/сек. Это разница в 9 порядков, превращающая 1-секундный взлом MD5 в 30 лет.


    1. kITerE
      11.08.2018 09:38

      Автор мог бы пояснить чем именно «слабость» MD5 делает «Шифрование ключа по умолчанию в OpenSSH хуже его отсутствия»?

      По тексту автор утверждает, что есть хороший шанс восстановить пароль шифрования в открытом виде:


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

      Что приведет к тому, что будет скомпрометирован не только ключ SSH, о и другие аккаунты, которые имеют тот же пароль.


      1. firk
        11.08.2018 10:02
        +1

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


        1. powerman
          11.08.2018 13:01

          В этом есть доля истины, но на практике не зря используют многоуровневые защиты — если пробили один уровень это даёт какой-то доступ, но вовсе не обязательно полный. Если каким-то образом получили возможность читать любые файлы юзера (включая файлы в ~/.ssh/), это ещё не означает возможность запустить кейлоггер.


        1. arheops
          11.08.2018 15:20

          Разница есть, поскольку не все пароли вы можете/захотите поменять после компроментации ключа
          И это еще не рассматриваем вариант, когда вы не знаете о краже.


        1. saipr
          12.08.2018 20:50
          +1

          А почему не воспользоваться токеном PKCS#11 с неизвлекаемым ключом и т.п.!


  1. kITerE
    11.08.2018 09:38

    .


  1. Taras-proger
    12.08.2018 08:57

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


    1. iroln
      12.08.2018 11:57

      Вы можете запомнить 4096 битный ключ, ну или хотя бы 2048? :)


  1. saipr
    12.08.2018 20:46

    Как можно исправить ситуацию?

    Воспользоваться ssh с ГОСТ-ами