(Начальное название статьи «Алгоритм работы протокола SSH» было изменено по рекомендациям Vindicar, Karroplan и других участников хабросообщества)
Периодически читая статьи, посвящённые SSH, обратил внимание, что их авторы порой не имеют понятия, как работает этот протокол. В большинстве случаев они ограничиваются рассмотрением темы генерации ключей и описанием опций основных команд. Даже опытные системные администраторы часто несут полную ахинею при обсуждении вопросов работы SSH, выдавая опусы в стиле: передаваемые данные шифруются открытым SSH-ключом клиента, а расшифровываются закрытым, или: для шифрования данных при передаче используется алгоритм RSA.
Попытаюсь внести немного ясности в работу протокола SSH, а заодно рассмотреть роль алгоритма RSA и ключей авторизации пользователя…

Алгоритм протокола SSH можно разделить на три уровня, каждый из которых располагается над предыдущим: транспорт (открытие защищённого канала), аутентификация, подключение. Для целостности картины я также добавлю уровень установки сетевого соединения, хотя официально этот уровень находится ниже SSH.
Не буду подробно останавливаться на принципе работы стека TCP/IP, так как эта тема достаточно хорошо задокументирована в Рунете. При необходимости вы легко найдёте информацию.
На этом этапе происходит сетевое подключение клиента к серверу на TCP-порт, указанный в опции Port (по умолчанию: 22) в файле конфигурации сервера /etc/ssh/sshd_config.
2.1 Обмен идентификационными данными
После установки TCP-соединения, клиент и сервер (далее по тексту – стороны) обмениваются версиями SSH-протокола и другими вспомогательными данными, необходимыми для выяснения совместимости протоколов и для выбора алгоритмов работы.
2.2 Выбор алгоритмов: обмена ключами, шифрования, сжатия и т.п.
При работе SSH используется довольно много алгоритмов, одни из них используются для шифрования, вторые для обмена ключами, третьи для сжатия передаваемых данных и т.п. На этом шаге стороны отсылают друг другу списки поддерживаемых алгоритмов, наибольший приоритет имеют алгоритмы в начале каждого списка. Затем сравнивают алгоритмы в полученных списках с алгоритмами, имеющимися в системе, и выбирают первый совпавший в каждом списке.
Список доступных алгоритмов обмена ключами на стороне клиента (используются для получения сессионного ключа) можно посмотреть командой:
Список доступных в системе симметричных алгоритмов (используются для шифрования канала):
Список типов ключей для авторизации у клиента:
Дополнено по замечанию onix74:
Все используемые в публикации команды актуальны для версии OpenSSH 7.6 из Ubuntu 18.04 LTS.
2.3 Получение сессионного ключа шифрования
Процесс получения сессионного ключа может отличаться в зависимости от версии алгоритма, но в общих чертах сводится к следующему:
Сеансовый ключ создается исключительно на период жизни канала и уничтожается при закрытии соединения.
И только теперь, когда клиент и сервер установили канал для зашифрованной передачи данных, они могут произвести аутентификацию по паролю или ключам.
В общих чертах, аутентификация посредством ключей происходит следующим образом:
После проведения всех вышеперечисленных процедур, пользователь получает возможность передавать команды серверу или копировать файлы.
На этом уровне обеспечивается: мультиплицирование каналов (возможность работы множества каналов к одному серверу за счет объединения их в один канал), туннелирование и т.п.
Ну а теперь, думаю, у читателей назрел вполне закономерный вопрос: а зачем нужно знать все эти тонкости работы SSH-протокола, если для повседневной работы достаточно знаний команд создания ключей (ssh-keygen), открытия терминальной сессии (ssh), передачи файлов (scp)?
В качестве ответа, можно вспомнить тему о смене стандартного порта SSH на какой-то другой, которая постоянно становится причиной холивара на Хабре…
В собственной практике я не припомню ни одного смотрящего во внешнюю сеть сервера, который бы ежедневно не подвергался долбёжке на 22-й порт. В ситуации, если SSH у вас работает на стандартном порту (и ничем дополнительно не защищён), даже если аутентификация исключительно по ключам и никакие подборы паролей не пугают, то по причине постоянно валящихся запросов от недобросовестных клиентов сервер всё равно вынужден совершать массу бесполезной работы: устанавливать TCP-соединение, выбирать алгоритмы, генерировать сессионный ключ, отправлять запросы аутентификации, писать лог-файл.
В ситуации же, когда на 22-м порту ничего нет, или порт защищён с помощью iptables (либо надстройками над ним типа fail2ban), то злоумышленник будет дропнут ещё на этапе установки TCP-соединения.
Наиболее интересно описанное выглядит в виде таблицы*
* — значения параметров (высокий, средний, низкий) носят относительный характер и служат только для сравнения показателей.
** — имеется ввиду расход ресурсов сервера (процессор, диск, сетевой канал и т.п.) на обработку лавины запросов, обычно идущих на 22-й порт.
*** — произвести взлом, если для авторизации используются RSA-ключи, очень сложно, однако неограниченное количество попыток авторизации делает это возможным.
**** — количество попыток авторизации ограничено, но серверу всё равно приходится обрабатывать их от большого количества злоумышленников.
Периодически читая статьи, посвящённые SSH, обратил внимание, что их авторы порой не имеют понятия, как работает этот протокол. В большинстве случаев они ограничиваются рассмотрением темы генерации ключей и описанием опций основных команд. Даже опытные системные администраторы часто несут полную ахинею при обсуждении вопросов работы SSH, выдавая опусы в стиле: передаваемые данные шифруются открытым SSH-ключом клиента, а расшифровываются закрытым, или: для шифрования данных при передаче используется алгоритм RSA.
Попытаюсь внести немного ясности в работу протокола SSH, а заодно рассмотреть роль алгоритма RSA и ключей авторизации пользователя…

Алгоритм протокола SSH можно разделить на три уровня, каждый из которых располагается над предыдущим: транспорт (открытие защищённого канала), аутентификация, подключение. Для целостности картины я также добавлю уровень установки сетевого соединения, хотя официально этот уровень находится ниже SSH.
1. Установка TCP-соединения
Не буду подробно останавливаться на принципе работы стека TCP/IP, так как эта тема достаточно хорошо задокументирована в Рунете. При необходимости вы легко найдёте информацию.
На этом этапе происходит сетевое подключение клиента к серверу на TCP-порт, указанный в опции Port (по умолчанию: 22) в файле конфигурации сервера /etc/ssh/sshd_config.
2. Открытие защищенного канала
2.1 Обмен идентификационными данными
После установки TCP-соединения, клиент и сервер (далее по тексту – стороны) обмениваются версиями SSH-протокола и другими вспомогательными данными, необходимыми для выяснения совместимости протоколов и для выбора алгоритмов работы.
2.2 Выбор алгоритмов: обмена ключами, шифрования, сжатия и т.п.
При работе SSH используется довольно много алгоритмов, одни из них используются для шифрования, вторые для обмена ключами, третьи для сжатия передаваемых данных и т.п. На этом шаге стороны отсылают друг другу списки поддерживаемых алгоритмов, наибольший приоритет имеют алгоритмы в начале каждого списка. Затем сравнивают алгоритмы в полученных списках с алгоритмами, имеющимися в системе, и выбирают первый совпавший в каждом списке.
Список доступных алгоритмов обмена ключами на стороне клиента (используются для получения сессионного ключа) можно посмотреть командой:
ssh -Q kex
Список доступных в системе симметричных алгоритмов (используются для шифрования канала):
ssh -Q cipher
Список типов ключей для авторизации у клиента:
ssh -Q key-cert
Дополнено по замечанию onix74:
Все используемые в публикации команды актуальны для версии OpenSSH 7.6 из Ubuntu 18.04 LTS.
2.3 Получение сессионного ключа шифрования
Процесс получения сессионного ключа может отличаться в зависимости от версии алгоритма, но в общих чертах сводится к следующему:
- Сервер отсылает клиенту свой ключ (DSA, RSA или т.п. согласно договорённости между сторонами, произведёнными в п.2.2).
- Если клиент производит соединение с данным сервером впервые (о чем говорит отсутствие записи в файле /home/username/.ssh/known_hosts у клиента), то пользователю будет задан вопрос о доверии ключу сервера. Если же соединение с данным сервером уже устанавливалось ранее, то клиент сравнивает присланный ключ с ключом, записанным в /home/username/.ssh/known_hosts. Если ключи не совпадают, то пользователь получит предупреждение о возможной попытке взлома. Впрочем, эту проверку можно пропустить, если вызвать ssh с опцией StrictHostKeyChecking:
Также, если пользователю нужно удалить старый ключ сервера (например, когда есть точная уверенность, что ключ был изменён на сервере), то используется команда:ssh -o StrictHostKeyChecking=no username@servername
ssh-keygen -R servername
- Как только клиент определился с доверием к ключу сервера, с помощью одной из реализаций (версия определяется в п.2.2) алгоритма Диффи-Хеллмана клиент и сервер генерируют сеансовый ключ, который будет использоваться для симметричного шифрования канала.
Сеансовый ключ создается исключительно на период жизни канала и уничтожается при закрытии соединения.
3. Аутентификация клиента
И только теперь, когда клиент и сервер установили канал для зашифрованной передачи данных, они могут произвести аутентификацию по паролю или ключам.
В общих чертах, аутентификация посредством ключей происходит следующим образом:
- Клиент отсылает серверу имя пользователя (username) и свой публичный ключ.
- Сервер проверяет в файле /home/username/.ssh/authorized_keys наличие присланного клиентом открытого ключа. Если открытый ключ найден, то сервер генерирует случайное число и шифрует его открытым ключом клиента, после чего результат отправляется клиенту.
- Клиент расшифровывает сообщение своим приватным ключом и отправляет результат серверу.
- Сервер проверяет полученный результат на совпадение с тем числом, которое он изначально зашифровал открытым ключом клиента, и в случае совпадения считает аутентификацию успешной.
4. Уровень подключения
После проведения всех вышеперечисленных процедур, пользователь получает возможность передавать команды серверу или копировать файлы.
На этом уровне обеспечивается: мультиплицирование каналов (возможность работы множества каналов к одному серверу за счет объединения их в один канал), туннелирование и т.п.
От теории к практике
Ну а теперь, думаю, у читателей назрел вполне закономерный вопрос: а зачем нужно знать все эти тонкости работы SSH-протокола, если для повседневной работы достаточно знаний команд создания ключей (ssh-keygen), открытия терминальной сессии (ssh), передачи файлов (scp)?
В качестве ответа, можно вспомнить тему о смене стандартного порта SSH на какой-то другой, которая постоянно становится причиной холивара на Хабре…
В собственной практике я не припомню ни одного смотрящего во внешнюю сеть сервера, который бы ежедневно не подвергался долбёжке на 22-й порт. В ситуации, если SSH у вас работает на стандартном порту (и ничем дополнительно не защищён), даже если аутентификация исключительно по ключам и никакие подборы паролей не пугают, то по причине постоянно валящихся запросов от недобросовестных клиентов сервер всё равно вынужден совершать массу бесполезной работы: устанавливать TCP-соединение, выбирать алгоритмы, генерировать сессионный ключ, отправлять запросы аутентификации, писать лог-файл.
В ситуации же, когда на 22-м порту ничего нет, или порт защищён с помощью iptables (либо надстройками над ним типа fail2ban), то злоумышленник будет дропнут ещё на этапе установки TCP-соединения.
Наиболее интересно описанное выглядит в виде таблицы*
Конфигурация | Вероятность взлома | Потери от флуда** |
---|---|---|
22 порт, авторизация по паролю, без защиты |
высокая | высокие |
22 порт, авторизация по ключам, без защиты |
средняя*** | высокие |
22 порт, авторизация по ключам, защита на основе ограничения неудачных попыток авторизации |
низкая | средние**** |
Нестандартный порт, авторизация по паролю, без защиты |
высокая | низкие |
Нестандартный порт, авторизация по ключам, без защиты |
средняя*** | низкие |
Нестандартный порт, авторизация по ключам, защита на основе ограничения неудачных попыток авторизации |
низкая | низкие |
* — значения параметров (высокий, средний, низкий) носят относительный характер и служат только для сравнения показателей.
** — имеется ввиду расход ресурсов сервера (процессор, диск, сетевой канал и т.п.) на обработку лавины запросов, обычно идущих на 22-й порт.
*** — произвести взлом, если для авторизации используются RSA-ключи, очень сложно, однако неограниченное количество попыток авторизации делает это возможным.
**** — количество попыток авторизации ограничено, но серверу всё равно приходится обрабатывать их от большого количества злоумышленников.