@drawoharaя ❤️ це! << натисніть мене 🐛 🫖 🧚
/multi-domain-https-with-server-name-indication
опубліковано: 2014-12-18

(Це скорочена версія Так, Вірджинія, Ви можете використовувати SNI, яка спочатку з'явилася в блозі Спайка Речі... Та речі...)

Коли ви підключаєтеся до веб-сервера безпечно за допомогою HTTPS, безпека забезпечується за допомогою TLS. Відбуваються дві речі: перевіряється ідентичність сервера та з'єднання шифрується.

Перевірка важлива, не має значення, якщо з'єднання зашифроване, якщо ви якимсь чином перенаправлені на сервер злочинця. Однак ця перевірка може бути проблематичною, якщо веб-сервер обслуговує більше одного імені хоста.

Ви можете прочитати подробиці, але спрощена версія процесу така: сервер надсилає підписаний Публічний ключовий сертифікат, який повинен відповідати імені хоста в URL. Якщо клієнт переходить до dojo4.com, то сертифікат повинен бути для dojo4.com, якщо ні, браузер видає велике лякаюче попередження.

Технічно можливо мати кілька імен хостів у сертифікаті, насправді це звичайно мати, наприклад, обидва "dojo4.com" і "www.dojo.com" для повноти. Однак це приносить величезні проблеми з додаванням і видаленням імен хостів із сертифіката. Ви повинні змусити видавця згенерувати новий сертифікат і скасувати старий. І якщо ви працюєте з мережею постачання контенту, вони малоймовірно додадуть ваші імена хостів до їхнього сертифіката.

Спочатку TLS підтримував один сертифікат на веб-сервер (або точніше, на IP-адресу, прикріплену до веб-сервера) Server Name Indication (SNI) був доданий до TLS для вирішення цієї проблеми. На початку переговорів TLS клієнт повідомляє серверу ім'я хоста, до якого він намагається підключитися, і сервер може вибрати та надіслати правильний файл сертифіката. Проблема вирішена!

Окрім ... Не всі браузери підтримують SNI. Всі знають це, і в результаті, намагаються обійти SNI та переходити до окремих IP-адрес для кожного сайту або навіть кількох серверів. Це дорогий варіант, особливо коли працюєте з CDN, такими як CloudFront. Коли це сталося зі мною, я вирішив перевірити, що означає "не всі браузери".

Виявляється, SNI широко підтримується, основні проблеми з IE8 та нижче, а також будь-якою версією IE, що працює під Windows XP (оскільки бібліотека базової ОС не підтримує SNI). Також є кілька старих версій Android, які не підтримують.

Так, більшість відвідувачів не матимуть жодних проблем із SNI, а група, яка це робить, настільки мала, що ми можемо розглядати її як виняток.

Для браузерів без підтримки SNI обхідний шлях - перенаправити їх на сертифікат, який працюватиме, або до язичкової сторінки "оновити свій браузер". Якщо ви шукаєте в Google, ви знайдете безліч рішень щодо створення білих списків хороших браузерів та/або чорних списків поганих, а потім використовувати ці списки у правилах перенаправлення на стороні сервера. Брудно. Списки повинні підтримуватися, а в залежності від сервера порушують кешування.

Є розумніший спосіб. Перебираючись крізь море зразкових конфігурацій перенаправлення Apache, я знайшов це в пості. Основна ідея посту можна скоротити до цього: якщо браузер, який не підтримує SNI, намагається завантажити вміст SNI, він отримує помилку. Якщо ми тестуємо це на задньому плані та розрізняємо помилку та успіх, ми можемо перенаправити відвідувача відповідно. І найпростіший спосіб зробити це - спробувати додати зображення одного пікселя на сторінку.

У коді це виглядає так:

function secure_redirect() {
   var img=document.createElement('img'); // створіть елемент img.
   // Встановіть src на URL SNI зображення одного пікселя
   img.src='https://www.example.org/pixel.gif';
   // Це виконується, якщо SNI працює.
   img.onload = function() {
      // Перенаправте на безпечну сторінку.
      window.location.href = "https://example.org/";
   };
   // Це виконується, якщо SNI не працює.
   img.onerror = function(e) {
      // Перенаправте на інше місце
      window.location.href = "http://example.org/snarky-old-browser-message";
   };
   // Не відображайте фактично зображення
   img.style.display='none';
   // але додайте його на сторінку, щоб він завантажився.
   document.body.appendChild(img);
  }

Тут я використовую два HTML-зворотні виклики на тезі img, 'OnLoad', який спрацьовує, коли закінчується завантаження зображення, та 'OnError', який спрацьовує, якщо зображення не можна завантажити. Якщо браузер не підтримує SNI, зображення не вдасться завантажити через помилку сертифіката, спрацює 'OnError'. Однак, оскільки ми додаємо зображення на вже завантажену сторінку, воно не викличе помилку в браузері.

Тепер ми можемо тестувати SNI та обробляти відсутність підтримки граціозно. Різдво врятовано!

Однак, до чого ми дійшли, - це щось більш розумне. Поміть, що код насправді не тестує SNI, а лише можливість безпечно завантажити зображення. Якщо URL HTTPS у питанні насправді не вимагає SNI, є лише один сертифікат або перший сертифікат відповідає запитаному домену, це все одно працює. Проблема зведена до "Чи може цей браузер відвідувача відображати безпечний сайт чи ні?", а на кінець дня, це все, що насправді хвилює.