Алгоритм роботи протоколу SSH
Періодично читаючи статті, присвячені 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
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)?
У власній практиці я не пригадаю жодного спостерігача в зовнішню мережу сервера, який би щодня не піддавався долбежке на 22-й порт. У ситуації, якщо SSH у вас працює на стандартному порту (і нічим додатково не захищений), навіть якщо аутентифікація виключно по ключам і ніякі підбирання паролів не лякають, то з причини постійно валящихся запитів від недобросовісних клієнтів сервер все одно змушений здійснювати масу непотрібної роботи: встановлювати TCP-з’єднання, обирати алгоритми, генерувати сесійний ключ, відправляти запити аутентифікації, писати лог-файл.
У ситуації ж, коли на 22-му порту нічого немає, або порт захищений за допомогою iptables (або надбудовами над ним типу fail2ban), то зловмисник буде дропнут ще на етапі встановлення TCP-з’єднання.
Найбільш цікаво описане виглядає у вигляді таблиці*
Конфігурація Вірогідність злому Втрати від флуду**
22 порт, авторизація по паролю, без захисту |
висока | високі |
22 порт, авторизація по ключам, без захисту |
середня*** | високі |
22 порт, авторизація по ключам, захист на основі обмеження невдалих спроб авторизації |
низька | середні**** |
Нестандартний порт, авторизація по паролю, без захисту |
висока | низькі |
Нестандартний порт, авторизація по ключам, без захисту |
середня*** | низькі |
Нестандартний порт, авторизація по ключам, захист на основі обмеження невдалих спроб авторизації |
низька | низькі |
* — значення параметрів (високий, середній, низький) носять відносний характер і служать тільки для порівняння показників.
** — мається на увазі витрати ресурсів сервера (процесор, диск, мережевий канал тощо) на обробку лавини запитів, зазвичай йдуть на 22-й порт.
*** — здійснити злом, якщо для авторизації використовуються RSA-ключі, дуже складно, однак необмежена кількість спроб авторизації робить це можливим.
**** — кількість спроб авторизації обмежена, але серверу все одно доводиться обробляти їх від великої кількості зловмисників.
Додаткові матеріали
- Исходники SSH
- Специфікації протоколу SSH (en)
- Алгоритм Діффі-Хелмана
- Алгоритм RSA
- Як SSH з’явився на 22 порту
- Що записано в файлі .ssh/known_hosts
- Пам’ятка користувачам ssh