21 June 2018

Белые пятна в работе с SSH

Блог компании Lodoss Team corporate blogInformation Security
SSH — очень мощный и гибкий инструмент, но, как показывает практика, не все понимают, как он работает, и правильно его используют. Слово Secure входит в аббревиатуру SSH и является одним из ключевых аспектов протокола, но часто именно безопасности уделяется недостаточное внимание. В этой статье я хочу рассказать о нескольких типичных ошибках при работе с SSH, а также о моментах, о которых часто забывают.

image

Существует несколько способов аутентификации пользователя:

  1. По паролю — стандартный механизм, но не самый надежный, я не буду заострять на нем внимание
  2. По ключу — самый надежный способ аутентификации, но это при условии, если правильно его использовать
  3. По IP адресу — режим совместимости. Привожу просто для справки, сомневаюсь, что кто-то будет использовать его в трезвом уме.

Асимметричное шифрование


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

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

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

Публичный ключ пользователя необходимо добавить в файл $HOME/.ssh/authorized_keys
В этом файле может быть несколько публичных ключей — все они будут работать.

image

Защита от MITM атак


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

Одним из основных нововведений во второй версии протокола стала встроенная защита от MITM-атак.

image

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

За MITM-защиту на стороне клиента (т.е. за то, чтобы определить, что ты подключаешься именно к своему серверу) отвечает параметр StrictHostKeyChecking и файл known_hosts.

Публичные ключи серверов хранятся в файлах:
$HOME/.ssh/known_hosts — пользовательский файл,
/etc/ssh/ssh_known_hosts— системный файл.

Каждая запись в known_hosts является строкой, содержащей поля и использующей пробел в качестве разделителя:

  1. одно или несколько имен серверов или IP-адресов, разделенных запятыми
  2. тип ключа
  3. открытый ключ
  4. любые дополнительные комментарии

Именно здесь происходит «идентификация» сервера

StrictHostKeyChecking=no|ask|yes 

Значения могут быть следующие:

  • no — подключение произойдет в любом случае, максимум мы получим предупреждение о том, что соединение небезопасно. Часто можно встретить во всяких автоматических скриптах типа автотестов и автодеплоев, тут и кроется основная проблема — если скрипт отработает нормально, то пользователь (администратор) и не заметит неладное, а заражение/компрометация произойдет.
  • ask — спрашивать при новом подключении (значение по-умолчанию), если записи еще нет в known_hosts, и, в случае согласия с подключением добавляется новая запись
  • yes — выполнять проверку всегда, самый безопасный вариант, который настоятельно рекомендую использовать в автоматизированных системах. Подключение производится только при наличии записи в known_hosts

Что нам это дает:

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

Если со вторым пунктом все понятно, то как быть с первым?

Тут можно выделить как минимум три варианта:

  1. Довериться случаю и согласиться на добавление нового ключа сервера
  2. Добавить флаг VisualHostKey=key, при этом клиент отобразит визуальное представление открытого ключа сервера, это представление уникально и облегчит запоминание ключа
  3. Флешка или открытые источники: это вариант для параноиков, однако, если компрометация системы может стоить десятков тысяч долларов, то это не лишне.

image
Использование визуализации открытого ключа

image
Если открытый ключ изменился

Сообщение о смене ключа может происходить по нескольким причинам:

  1. Изменился адрес сервера
  2. Изменился ключ сервера, например, при переустановке системы
  3. MITM атака

Из моей практики 1 и 2 варианты в сумме стремятся к 100%, но и пункт 3 нельзя исключать тоже.

Вот пример лайфхака, который объединяет в себе веб- и ssh-подходы

curl https://server.com/ssh_fingerprint | tee -a ~/.ssh/known_hosts

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

ssh-keyscan example.com

Однако, в первом варианте мы дополнительно проверяем подлинность полученного ключа при помощи HTTPS.

В критически важных CI/CD использование комбинации

ssh-keyscan example.com >> ~/.ssh/known_hosts

во время инициализации совместно с

StrictHostKeyChecking=yes

позволит надежно защититься от MITM-атак.

Множество ключей


Часто можно встретить такую картину:

image

Для каждого сервера на клиенте сгенерированы свои пары ключей, и в $HOME/.ssh/ творится полнейший беспорядок.

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

Однако, как показывает практика, этим руководствуются пользователи в последнюю очередь, зачастую они просто выполняют все инструкции по шагам:


ssh-keygen
...
...

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

image

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

Кстати, о защите ключей


image

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

Что тут можно выделить:

  1. Хранить ключ надо в безопасном месте, в идеале это шифрованное хранилище
  2. Никому не предоставлять доступ к ключу, скомпрометированный ключ компрометирует все системы где разрешен доступ по нему
  3. Шифрование приватного ключа увеличивает безопасность, т.к. при потере ключа необходимо еще получить пароль для его дешифрации, либо потратить время и вычислительные мощности на его взлом.
  4. Не забывать про установку корректных прав на файл ключа 400, кстати, это распространенная ошибка, когда клиент отказывается использовать ключ

Изменить или добавить пароль можно при помощи команды

ssh-keygen -p

SSH-Agent


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

  1. Скопировать свой приватный ключ на сервер
  2. Сгенерировать новую пару ключей и прописать ее в качестве варианта доступа
  3. Использовать ssh-agent

Из моей практики, пользователи в 90% случаев выбирают первые два варианта, а, как мы уже говорили: нет ничего более страшного, чем компрометация ключа.

Я же рекомендую воспользоваться третьим способом

image

SSH-agent хранит секретные ключи и, когда нужно, пользуется ими. Программа (например, ssh), когда ей понадобится воспользоваться секретным ключом, не делает этого сама, а обращается к ssh-agent 'у, который, в свою очередь, уже сам пользуется известными только ему данными о секретных ключах. Таким образом, секретные ключи не разглашаются никому, даже программам, принадлежащим самому пользователю.

Команда ssh-agent создает файл-сокет с именем /tmp/ssh-XXXXXXXX/agent.ppid, через который осуществляется взаимодействие с агентом. Всем дочерним процессам агент при помощи переменных окружения SSH_AUTH_SOCK (в которой хранится имя файла-сокета) и SSH_AGENT_PID (в которой хранится идентификатор процесс агента) сообщает информацию о том, как с ним можно связаться.

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

SSH_AUTH_SOCK=/tmp/ssh-XXt4pHNr/agent.5087; export SSH_AUTH_SOCK;
SSH_AGENT_PID=5088; export SSH_AGENT_PID;
echo Agent pid 5088;

При указании ключа -c агент использует синтаксис C Shell. По умолчанию (и при явном указании ключа -s) используется синтаксис Bourne Shell. Эти переменные следует установить в текущем командном интерпретаторе, поэтому обычно вызов ssh-agent комбинируется с командой eval.

$ eval `ssh-agent`
Agent pid 5088

Агент работает до тех пор, пока не будет явно завершен сигналом либо вызовом

$ ssh-agent -k

Список известных агенту секретных ключей можно посмотреть той же командой ssh-add с ключом командной строки -l. Команда сообщит и отпечаток для каждого ключа.

$ ssh-add -l
1024 46:88:64:82:a7:f9:aa:ea:3b:21:9e:aa:75:be:35:80 /home/user/.ssh/id_rsa (RSA)

Пример работы с ssh-agent можно увидеть ниже

image

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

Локальный конфиг


Если бы каждый раз, когда я наблюдал за набором строки вида

ssh -p 2022 user@server.com -o StrictHostKeyChecking=yes -i ~/.ssh/server.pem

мне давали рубль, то я бы уже давно жил в Сочи.

На локальной машине можно найти файл $HOME/.ssh/config

Host server
	User root
	HostName 192.168.0.3
	IdentityFile ~/.ssh/server.pem

Host example.com
	User admin
	IdentityFile ~/.ssh/production.pem

Соответственно, можно прописать большинство параметров для хоста в этот файл и не вводить каждый раз.

Как еще можно улучшить безопасность


1. При использовании аутентификации по ключу не забывайте, что остается еще пароль, и, как бы вы ни защищали свой приватный ключ, злоумышленник сможет подобрать ваш пароль qwerty и войти в систему без необходимости похищать ключ

Можно выделить несколько решений:

  • Задать очень сложный пароль, который будет сложно подобрать и хранить его в защищенном месте. Это позволит иметь альтернативный способ входа.
  • Удалить пароль совсем, но тогда могут возникнуть проблемы с sudo и прочим
  • Запретить на сервере аутентификацию по паролю, собственно, как правило, для пользователя root это и запрещается. Остается запретить для всех пользователей.

PasswordAuthentication no в /etc/ssh/sshd_config

2. Еще можно запретить использование ssh v1
Protocol 2 в /etc/ssh/sshd_config

3. И обязательно закрывать на фаерволе доступ к SSH для ненадежных источников

4. Раньше бы я посоветовал сменить порт со стандартного 22 на другой, но наблюдая за тем, сколько бывает попыток авторизоваться на нестандартных портах я могу сказать однозначно: это не является защитой от взлома.

5. Если уже рассматривать подбор пароля, то не лишним будет настроить fail2ban для того, чтобы ограничить попытки подбора пароля.

6. В протоколе и библиотеках периодически находят проблемы и уязвимости, поэму тут работает универсальный подход — следите за обновлениям ПО и старайтесь регулярно устанавливать как минимум заплатки безопасности.

7. ssh-agent безопасней, чем просто копирование файлов, однако, его тоже можно взломать, например, root пользователь на сервере. Поэтому рекомендуется включать форвардинг только в тех случаях, когда это необходимо.

Выводы


Читайте документацию, в ней все написано, как бы это пошло не звучало.
SSH очень безопасный протокол, но, зачастую, именно человеческий фактор является основным источником проблем — следите за безопасностью.

Использованные материалы
Защита с помощью SSH ключей
Управление ключами SSH с помощью агента
Tags:ssh-ключиинформационная безопасность
Hubs: Блог компании Lodoss Team corporate blog Information Security
+38
30.5k 290
Comments 54