Настал 2020-й год и я перевел свой сайт на https и на странице одной из самых популярных записей в блоге перестал работать пример анонимного чата на технологии WebSocket. К моему удивлению, задача перехода на https весьма ощутимо затронула WebSocket: от небольших правок скрипта JavaScript, до значительной доработки PHP кода, в том числе и с погружением в некоторые детали стандарта RFC-6455.
Постараюсь разобрать вопрос максимально подробно как делал это в предыдущих статьях, сделав его доступным даже начинающим разработчикам.
Для новичков попавших на эту страницу миновав изучение базовых принципов работы технологии WebScoket настоятельно рекомендую ознакомиться со статьями в моём блоге на тему WebScoket начиная с простой веб-сокет на PHP или веб-сокеты с абсолютного 0.
История массового перехода на https
С развитием сети Интернет со временем компании стали задумываться о безопасности данных пользователей передаваемых по сети. Времена HTTP/0.9 и HTTP/1.0 когда данные передавались в незашифрованном виде давно прошли. В середине нулевых с появлением большого количества веб-сайтов требующих авторизации и содержащих большое количество данных пользователей начало набирать популярность расширение протокола HTTP HTTPS (HTTP Sercure), которое обеспечивало ассиметричное шифрование тела HTTP запроса передаваемого по сети. А в середине десятых, в эру Facebook, Google и регуляторов Интернета из Евросоюза, запрос общества на полную защищенность передаваемых данных в сети Интернет сформировался в требование поддержки HTTPS для большинства сайтов. И это требование не то, что можно игнорировать – ведь сайты без поддержки HTTPS стали понижаться в поисковой выдаче, браузеры стали предупреждать о нежелательности посещения сайтов без поддержки HTTPS, а также отключать возможность смешанного использования HTTP и HTTPS в рамках одного веб-сайта. Нужно понимать, что это не прихоть, а к такому стремлению использовать везде HTTPS подтолкнуло развитие технологий анализа трафика (снифферов) позволяющих перехватывать все данные пользователей вплоть до паролей передаваемых по незашифрованному HTTP протоколу.
В каких случаях нужна поддержка веб-сокетами https?
Очевидно, что во всех приложениях в которых передаются мало-мальски чувствительные данные или пароли настоятельно рекомендуется использовать secure версию протокола веб-сокетов wss://. Казалось бы, как это может относиться к скрипту моего чата, который не передаёт никаких конфиденциальных данных? А всё дело в том, что современные браузеры открывая страницу по HTTPS автоматически запрещают использовать внутри неё незащищенное соединение по протоколу WS. Поэтому задача реализации и поддержки шифрованного протокола веб-сокетов WSS становится как никогда актуальной для всех сайтовладельцев использующих протокол HTTPS.
Переход на wss
В качестве примера я обновлю версию своего анонимного чата на веб-сокетах выпустив новую версию wss server admin panel v.0.5.1 & chat v.0.2..
На самом деле все модификации я делал последовательно и вы можете проделать их самостоятельно в своих работающих проектах:
- Первое с чего я начал, получив уведомление о невозможности подключиться в браузере на странице чата – изменил протокол в строке адреса подключения к серверу с ws:// на wss://. На этом все изменения в клиентском JavaScript.
- На сервере это выглядело несколько сложнее. Существует два способа решения задачи:
- Настройка SSL-шифрования через проксирование трафика с порта на веб-сервере. Этот вариант отлично подходит, если нужно осуществить миграцию с WS на WSS в большом количестве сложных скриптов, так как абсолютно не требует изменений в php скриптах. Однако этот метод требует понимания настроек веб-сервера и соответствующих прав. К сожалению, для запуска на моём виртуальном хостинге он не подошел, по причине того, что у меня оказалось недостаточно прав для использования VirtualHost в файле .htaccess. При попытке включения в файл .htaccess я получил ошибку в error log:
[Sun Jul 05 19:47:08.011946 2020] [core:alert] [pid 16242] [client 31.173.84.75:35056] /var/www/petukhovsky/data/www/petukhovsky.com/.htaccess: <VirtualHost not allowed here, referer: https://petukhovsky.com/index.php
Полностью конфигурация выглядит примерно так, и может отличаться в зависимости от веб-сервера и его настроек. Я написал слово примерно, поскольку мне не удалось полноценно протестировать этот способ.
<VirtualHost *:8889> # Common SSL Config ServerName my-site.test SSLEngine on SSLCertificateFile "/usr/local/etc/httpd/server.crt" SSLCertificateKeyFile "/usr/local/etc/httpd/server.key" DocumentRoot "/var/www/my/site" <Directory "/var/www/my/site"> Options +Indexes +FollowSymLinks +MultiViews AllowOverride All Require local </Directory> # Websocket proxy # wss redirects to working ws protocol ProxyPass /wss ws://127.0.0.1:8889 retry=0 keepalive=On ProxyPassReverse /wss ws://127.0.0.1:8889 retry=0 </VirtualHost>
- SSL-шифрование в PHP. Обычно в качестве сертификатов в PHP используется *.pem файл содержащий SSL сертификат из двух файлов *.crt и *.key. Формат содержимого *.pem файла приведен ниже. Cамо-собой, вместо многоточия должно быть тело ключа.
-----BEGIN CERTIFICATE----- MIIFVjCCBD6gAwIBAgISBCQb....5V+jN81Y= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAyslsWOVc....46wjM2GI= -----END RSA PRIVATE KEY-----
Сервер WSS поднялся и прекрасно заработал на базе моего SSL сертификата используемого в шифровании данных передаваемых по HTTPS на мой домен. Но для меня это решение казалось не очень удачным, потому как не хотелось хранить в директории веб-сайта *.crt и *.key файлы содержащие ключи HTTPS домена, ко всему также не хотелось обновлять их вручную каждый раз при обновлении сертификата домена. Но, к сожалению, использовать их из директории сертификатов тоже оказалось проблематичным из-за ограничения доступа накладываемых на уровне файловой системы. Не помогло и точечное присвоение прав, по причине того что пользовательская директория с содержимым веб-сайта и скриптов PHP оказалась сильно изолированной от директории хранения файлов сертификатов и для доступа к ней пришлось бы давать PHP скриптам доступ ко многим критическим каталогам сервера.
В итоге я попробовал генерировать самоподписываемый сертификат и сохранять его в *.pem файле в моменте запуска WSS, но, как выяснилось, некоторые браузеры запрещают устанавливать WSS соединение с серверами использующими самоподписанные сертификаты и передо мной встала задача выбора наименее плохого из предложенных выше способов.
Upd 2021.04.13: Утомившись ручным копированием *.crt и *.key, я написал простой скрипт копирующий данные сертификатов в директорию проекта.
#!/bin/bash cp -f /var/www/httpd-cert/petukhovsky/petukhovsky.com_le2.crt /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.crt chown petukhovsky /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.crt chgrp petukhovsky /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.crt chmod 644 /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.crt cp -f /var/www/httpd-cert/petukhovsky/petukhovsky.com_le2.key /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.key chown petukhovsky /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.key chgrp petukhovsky /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.key chmod 644 /var/www/petukhovsky/data/www/petukhovsky.com/f/simple-web-socket-on-php-chat/chat/pem/petukhovsky.com_le2.key
Поместил этот скрипт в крон через 5 минут после проверки обновления letsencrypt выпускающего сертификаты для сайта. Добавление в крон можно сделать командой crontab -e выполненной из под root. Обратите внимание что может быть вызван редактор VI, который не всегда привычен пользователю графических оболочек.
Я остановился на втором способе как наиболее подходящем для себя.
- Настройка SSL-шифрования через проксирование трафика с порта на веб-сервере. Этот вариант отлично подходит, если нужно осуществить миграцию с WS на WSS в большом количестве сложных скриптов, так как абсолютно не требует изменений в php скриптах. Однако этот метод требует понимания настроек веб-сервера и соответствующих прав. К сожалению, для запуска на моём виртуальном хостинге он не подошел, по причине того, что у меня оказалось недостаточно прав для использования VirtualHost в файле .htaccess. При попытке включения в файл .htaccess я получил ошибку в error log:
По итогам, архив wss server admin panel v.0.5.1 & chat v.0.2. содержит обновленную версию чата на веб-сокетах, способную работать с протоколами ws и wss, соответствующие установки вынесены в конфиг, также добавлена инструкция по установке и настройке в файле !manual.txt.
2020.06.07: Что нового в wss server admin panel v.0.5.0
- Добавлена возможность использования WSS (на сайтах использующих HTTPS);
- В панели администратора wss server admin panel добавлена функция очистки лог-файлов;
- Их архива исключен эхо-клиент;
- Добавлен файл инструкций по настройке и запуску скрипта !manual.txt
2021.04.13: Что нового в wss server admin panel v.0.5.1
- Добавлен скрипт для автоматического обновления сертификата;
- Добавлены инструкции по настройке скрипта обновления сертификата
Мои статьи про PHP демонов и веб-сокеты
- Простой веб-сокет на PHP или веб сокеты с абсолютного 0
- Веб-сокет сервер на PHP, запуск демона на PHP
- Веб-сокеты на PHP это нормально: анонимный чат
- Веб-сокеты на PHP, простые рецепты разработки и отладки
- Анонимный чат релиз
- Хостинг для веб-сокетов на PHP
- Downloads
- WebSocket PHP SSL (Поддержка HTTPS)