14.4.5. Перевірка публічного сервера (камера як клієнт)¶
Усе сказане на попередній сторінці про клієнта, який «вже має кореневий сертифікат», справедливо для браузерів, телефонів і ПК – але не для камери. MicroPython’s ssl не містить вбудованого сховища довіри: щойно прошита камера не довіряє жодному CA, а налаштування за замовчуванням (ssl.CERT_NONE) нічого не перевіряє та повністю відкрита для атаки типу «людина посередині». Тому якщо камера є клієнтом, який підключається до публічного TLS-сервера (HTTPS API, MQTT-брокер тощо) і ви хочете справжньої перевірки цього сервера, потрібно самостійно надати якір довіри.
Механіка та ж, що й у прикладі з самопідписаним клієнтом на сторінці Самопідписані сертифікати; єдина відмінність — файл, який ви завантажуєте, є справжнім сертифікатом CA, а не власним сертифікатом вузла:
Отримайте сертифікат CA, який є якорем ланцюжка сервера. «Якір» означає сертифікат на вершині (або поблизу вершини) ланцюжка сервера, який ви обираєте як початкову точку довіри. TLS-сервер надсилає свій листовий та зазвичай проміжний сертифікат(и); кореневий він ніколи не надсилає. Якір довіри ви повинні отримати самостійно і незалежно від сервера – просто довіряти тому, що сервер надсилає, зводить нанівець весь сенс перевірки.
Спочатку з’ясуйте, який CA фактично видав сертифікат сервера. Наприклад, для
openmv.ioopenssl s_client -connect openmv.io:443 -showcerts < /dev/nullБлок
Certificate chainмістить кожен сертифікат з його суб’єктом (s:) та видавцем (i:); нові версії OpenSSL також виводять рядкиa:(тип ключа) таv:(термін дії), які тут можна ігнорувати:Certificate chain 0 s:CN=openmv.io i:C=US, O=Let's Encrypt, CN=E8 1 s:C=US, O=Let's Encrypt, CN=E8 i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
Запис 0 — це листовий сертифікат (
openmv.io), виданий проміжним CAE8. Запис 1 — це цей проміжний CA, виданий кореневимISRG Root X1. Видавець (i:) найвищого запису називає корінь – тутISRG Root X1. (Проміжний –E8, а неR10/R11, які ви могли бачити в інших місцях, тому щоopenmv.ioвикористовує сертифікат ECDSA; Let’s Encrypt підписує листові сертифікати ECDSA своїми проміжними сертифікатами серіїE, а листові RSA – серіїR. Обидва ведуть доISRG Root X1.)OpenSSL також виводить рядки
depth=і може повідомити про корінь ізVerification: OK. Це відбувається лише тому, що ваш ПК вже довіряєISRG Root X1– сервер його не надсилав (сервер ніколи не надсилає свій корінь), і камера, не маючи сховища довіри, його теж не матиме. Саме тому ви повинні надати його самостійно.Завантажте цей кореневий сертифікат з опублікованих кореневих сертифікатів самого CA. Let’s Encrypt каталогізує всі свої на сторінці сертифікатів Let’s Encrypt; прямий файл для ISRG Root X1 — isrgrootx1.pem (вони також пропонують його у вигляді isrgrootx1.der). Інші CA публікують свої на схожій сторінці «кореневих сертифікатів» / «сховища»; канонічний публічний набір — Mozilla CA program (CCADB). Підтвердіть завантажений файл, порівнявши його відбиток із значенням, опублікованим CA (додайте
-inform DER, якщо завантажили.der):openssl x509 -in isrgrootx1.pem -noout -subject -fingerprint -sha256Якщо ви не хочете відстежувати кореневий сертифікат, ви можете натомість скопіювати проміжний безпосередньо з виводу
-showcerts(другий блок-----BEGIN CERTIFICATE-----), довіряти йому та прийняти, що вам доведеться оновлювати його щоразу, коли CA ротує проміжний – що відбувається набагато частіше, ніж ротація кореневого (детальніше про компроміс нижче).Конвертуйте в DER, точно як раніше:
openssl x509 -in isrgrootx1.pem -outform DER -out ca.derСкопіюйте
ca.derна камеру (файлова система або ROMFS) та завантажте як якір довіри:import socket import ssl import ntptime ntptime.settime() # validity check needs the clock addr = socket.getaddrinfo("api.example.com", 443)[0][-1] sock = socket.socket() sock.connect(addr) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.verify_mode = ssl.CERT_REQUIRED ctx.load_verify_locations(cafile="ca.der") ssock = ctx.wrap_socket(sock, server_hostname="api.example.com")
server_hostnameтут є обов’язковим: він керує SNI і є ім’ям, яке перевіряється протиsubjectAltNameсертифіката сервера.
Порада
Скорочення для поширеного випадку. Let’s Encrypt є найбільш широко використовуваним публічним CA, і обидва його RSA та ECDSA сертифікати наразі ведуть до ISRG Root X1 (як показано в прикладі з openmv.io вище). Якщо сервери, до яких підключається ваша камера, використовують Let’s Encrypt, ви можете пропустити перевірку: просто покладіть isrgrootx1.der на камеру та викличте load_verify_locations для нього.
Це не робить TLS робочим для кожного сайту. Сервер, сертифікат якого видано іншим CA (DigiCert, Google Trust Services, Amazon, Sectigo тощо), все одно не пройде перевірку, і оскільки камера довіряє одному DER-сертифікату на ssl.SSLContext, ви не можете зібрати всі кореневі сертифікати, як це робить браузер. У разі сумнівів визначте фактичний CA сервера, як показано вище, та довіряйте цьому кореневому сертифікату.
Вибір сертифіката для довіри є компромісом:
Кореневий (рекомендовано). Довгостроковий – часто десятиліттями – тому
ca.derрідко змінюється. Вимагає від сервера надсилати свій проміжний сертифікат, щоб mbedTLS міг побудувати шлях листовий → проміжний → ваш довірений корінь; практично кожен правильно налаштований публічний сервер це робить.Проміжний. Також працює і продовжує працювати, навіть якщо сервер не надсилає проміжний, але проміжні ротуються набагато частіше, ніж кореневі, тому вам доведеться частіше оновлювати
ca.der.Сам листовий сертифікат (прив’язка сертифіката). Найбільш обмежений, але листовий сертифікат змінюється при кожному поновленні – приблизно кожні 90 днів для Let’s Encrypt – тому це має сенс лише тоді, коли ви також контролюєте сервер і можете одночасно надіслати новий прив’язаний сертифікат на кожну камеру. Саме це й робить приклад із самопідписаним клієнтом.
Примітка
ssl.SSLContext.load_verify_locations() приймає один DER-кодований сертифікат CA, тому камера довіряє рівно одному якорю одночасно. Щоб підключатися до серверів з різними CA, використовуйте окремий ssl.SSLContext для кожного якоря. І оскільки цей сертифікат з часом може закінчитися або бути ротований CA, ставтесь до нього як до будь-якого іншого сертифіката на пристрої.