- Вступление
- Как работает https и при чём здесь SSL-сертификаты (кратко)
- Как работать с https через curl
- P.S. Должны ли мы посылать сертификат серверу?
- P.P.S. Как сохранить себе на диск сертификат сайта?
В предыдущей части, для отправки get-запросов по https при помощи curl, использовалась функция вот такого вида:
function execRequest($telegram_req_url){ $telegram_ch = curl_init(); curl_setopt($telegram_ch, CURLOPT_URL, $telegram_req_url); curl_setopt($telegram_ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($telegram_ch, CURLOPT_HTTPGET, true); curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYPEER, false); // отменяем проверку сертификатов curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYHOST, false); // (это для тестов, ну а что делать) curl_setopt($telegram_ch, CURLOPT_MAXREDIRS, 10); curl_setopt($telegram_ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($telegram_ch, CURLOPT_TIMEOUT, 20); $telegram_ch_result = curl_exec($telegram_ch); return $telegram_ch_result; } |
При этом справа, в комментариях, указано, что опции CURLOPT_SSL_VERIFYPEER и CURLOPT_SSL_VERIFYHOST для примера установлены в false. Вот об этом и будет сегодняшняя статья. Я расскажу о том, как дописать эту функцию до правильного рабочего варианта и почему это важно. Для того, чтобы всё было понятно придётся сделать вступление в виде краткого описания базовых принципов функционирования https.
Расширение протокола http для организации безопасных шифрованных сеансов связи называется https. Это по-сути всё тот же http трафик, только передаваемый в ходе защищённого сеанса связи. Защита сеанса обеспечивается криптографическим протоколом SSL. Cовременная стандартизированная версия протокола SSL называется TLS, но в обиходе все протоколы этой группы продолжают называть общим именем SSL. SSL работает на уровень ниже протокола http, то есть весь http-трафик оказывается как бы «завёрнут» внутрь SSL-трафика.
Защита соединения в протоколе SSL построена на трёх вещах:
- Аутентификация сторон
- Шифрование всей полезной информации (в том числе полезного http траффика)
- Контроль целостности передаваемой информации
Процесс аутентификации позволяет обоим участникам сеанса связи убедиться, что их партнёр именно тот, за кого он себя выдаёт. Этот процесс производится в самом начале установления https-соединения, в ходе так называемой процедуры рукопожатия (handshake), ещё до отправки самого запроса (до отправки полезного http-трафика). Он позволяет избежать атаки, условно называемой «человек посередине», когда между вами и вашим абонентом вклинивается посредник. Если этот посредник сможет выдать себя за вашего партнёра, то дальнейшее шифрование трафика не будет иметь никакого смысла, поскольку посредник сможет обмениваться с вами данными от имени вашего партнёра, а с вашим партнёром от вашего имени.
Для однозначной идентификации партнёра используется система так называемых SSL-сертификатов. SSL-сертификат — это такой электронный документ, выдаваемый специальными доверенными центрами и подтверждающий, что заявленный в сертификате открытый ключ шифрования принадлежит указанному владельцу. В сертификате, помимо открытого ключа, используемого для асимметричного шифрования данных в процессе аутентификации, и данных о владельце сертификата, указано кем и на какой срок выдан этот сертификат (то есть это примерно как паспорт у людей).
Для защиты от подделывания сертификат подписан электронной цифровой подписью. Электронная подпись формируется с помощью закрытого ключа центра сертификации, а расшифровывается с помощью их открытого ключа (то же самое асимметричное шифрование, только задом наперёд). Таким образом эту электронную подпись может проверить кто угодно (имея сертификат центра сертификации, который содержит их открытый ключ), а подделать никто не может, поскольку не имеет закрытого ключа центра сертификации.
Считается, что если вы доверяете центру сертификации (имеете копию их сертификата, которой доверяете), то можете доверять и всем выданным этим центром сертификатам.
Скажем, браузеры сразу содержат в составе пакета установки сертификаты всех основных центров сертификации, поэтому мы спокойно можем посещать с помощью этих браузеров сайты, сертификаты которых выданы этими центрами.
Эмитенты сертификатов могут иметь иерархическую структуру. Каждое звено в такой цепочке имеет свой сертификат, а для проверки конечного сертификата клиента нужно иметь сертификаты всех звеньев цепочки эмитентов, начиная с корневого. Документы, содержащие такие цепочки сертификатов, называются CA-бандлы (bundles).
Не нужно думать, что вклинивание посредника между вами и вашим партнёром связано с какими-то техническими сложностями. На самом деле такой посредник всегда есть, например, в лице провайдера. Так что технически, единственное, что останавливает, скажем, тот же РКН от онлайн-мониторинга вашего https-трафика при помощи провайдеров — это невозможность подделать SSL-сертификаты. Так что государство всегда сможет за вами подглядеть, разница только в том, что в одних странах по закону вершиной местных центров выдачи SSL-сертификатов должен являться специальный госорган (такие могут при желании мониторить трафик прямо налету), а в других приняты законы о хранении всего трафика и передаче спецслужбам ключей шифрования по требованиям разных инстанций (такие могут то, что им нужно, потом почитать). Ну ладно, это так, — небольшое лирическое отступление.
Резюмируя, — если вы хотите, чтобы сеансы связи по https были надёжно защищены — нужно обязательно проверять партнёров, с которыми вы связываетесь, при помощи предоставляемых ими SSL-сертификатов (чтобы убедиться, что эти партнёры именно те, за кого себя выдают).
Как работать с https через curl
Переходим к главному. В представленном для примера коде я отменил проверку сертификатов, установив опции CURLOPT_SSL_VERIFYHOST и CURLOPT_SSL_VERIFYPEER в false. Теперь вы знаете чем такая отмена грозит, поэтому далее давайте рассмотрим как правильно настроить указанные опции и провести проверку сертификатов сервера, которому вы отправляете запрос.
Итак, первая опция — CURLOPT_SSL_VERIFYHOST. Эта опция устанавливает, нужно ли проверять указанное в сертификате имя хоста. CURLOPT_SSL_VERIFYHOST можно установить в 0 или в 2 (в ранних версиях curl был ещё вариант 1, но сейчас его отменили). По-умолчанию стоит 2, но лучше всегда задавать это значение явно. Значение 0 отменяет проверку имени хоста, а вот при значении 2 curl будет извлекать имя хоста из полученного от сервера SSL-сертификата и сравнивать его с именем хоста, на который должен быть отправлен запрос. Если эти имена не совпадут — процесс установления соединения будет прерван с ошибкой и запрос не будет отправлен.
Вторая опция — CURLOPT_SSL_VERIFYPEER. Эта опция устанавливает, нужно ли проверять подлинность присланного сертификата сервера чтобы решить, можно ли этому сертификату доверять. CURLOPT_SSL_VERIFYPEER можно установить в false или true. Когда опция установлена в false — любой присланный сертификат считается подлинным и вызывает доверие. Если же опция установлена в true, то curl будет пытаться выполнить проверку подлинности присланного хостом сертификата. Чтобы выполнить эту проверку, курлу нужно иметь открытый ключ центра сертификации, выдавшего хосту сертификат. Для этого, в свою очередь, нужно скачать сертификат этого центра, положить к себе на диск и при выполнения запроса указывать курлу где он лежит. Это делается при помощи опции CURLOPT_CAINFO. Она имеет следующий синтаксис:
curl_setopt($ch, CURLOPT_CAINFO, "/path/to/certificate/cabundle.pem"); |
Здесь path_to_certificate — это путь к папке (в доке написано, что для CAINFO может потребоваться абсолютный путь), а cabundle.pem — сам файл, содержащий сохранённый сертификат или цепочку сертификатов.
Иногда нам может понадобиться иметь для проверки несколько разных файлов с сертификатами, например, если мы хотим отправлять запросы к разным хостам, сертификаты которым выдавали разные центры сертификации. В этом случае вместо CURLOPT_CAINFO можно воспользоваться другой опцией, — CURLOPT_CAPATH. Эта опция позволяет ссылаться не на один файл, а на директорию, содержащую все нужные файлы сертификатов. Синтаксис:
curl_setopt($ch, CURLOPT_CAPATH, "/path/to/certificates"); |
С последней опцией, правда, есть одна проблема, — все сертификаты в указанной директории, не имеющие специального вида имена, игнорируются. Для того, чтобы правильно подготовить папку с сертификатами к использованию опции CURLOPT_CAPATH — можно воспользоваться специальной утилитой c_rehash из пакета openssl-perl (она автоматически создаст копии файлов с нужными именами).
Учитывая всё вышеизложенное, правильный curl-запрос с использованием https-соединения должен выглядеть следующим образом:
function execRequest($telegram_req_url){ $telegram_ch = curl_init(); curl_setopt($telegram_ch, CURLOPT_URL, $telegram_req_url); // адрес c https curl_setopt($telegram_ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($telegram_ch, CURLOPT_HTTPGET, true); curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYHOST, 2); // проверяем совпадение имени хоста в url и в сертификате curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYPEER, true); // выполняем проверку подлинности сертификата curl_setopt($telegram_ch, CURLOPT_CAINFO, "/path/to/certificate/cabundle.pem"); // бандл сертификатов (должен содержать сертификат центра, выдавшего сертификат хоста) curl_setopt($telegram_ch, CURLOPT_MAXREDIRS, 10); curl_setopt($telegram_ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($telegram_ch, CURLOPT_TIMEOUT, 20); $telegram_ch_result = curl_exec($telegram_ch); return $telegram_ch_result; } |
function execRequest($telegram_req_url){ $telegram_ch = curl_init(); curl_setopt($telegram_ch, CURLOPT_URL, $telegram_req_url); // адрес c https curl_setopt($telegram_ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($telegram_ch, CURLOPT_HTTPGET, true); curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYHOST, 2); // проверяем совпадение имени хоста в url и в сертификате curl_setopt($telegram_ch, CURLOPT_SSL_VERIFYPEER, true); // выполняем проверку подлинности сертификата curl_setopt($telegram_ch, CURLOPT_CAPATH, "/path/to/certificates"); // папка с сертификатами (должна содержать сертификат центра, выдавшего сертификат хоста) curl_setopt($telegram_ch, CURLOPT_MAXREDIRS, 10); curl_setopt($telegram_ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($telegram_ch, CURLOPT_TIMEOUT, 20); $telegram_ch_result = curl_exec($telegram_ch); return $telegram_ch_result; } |
А как же сервер нас проверяет, у нас же никакого сертификата нет? Тут всё просто. Если это публичный сервис, то ему нет смысла кому-то доверять или не доверять, — он должен обслуживать все запросы от кого угодно. Аутентификация пользователей публичными сервисами производится не на основе сертификатов, а при помощи других механизмов (например, пары логин/пароль).
В принципе, клиент точно так же вполне может иметь свой сертификат, который сервер будет использовать для аутентификации клиента, однако по ряду причин для клиентов такой механизм неудобен (скажем, как таскать сертификат с собой, если клиент не привязан к машине) и обычно используется только в непубличных закрытых сетях (и даже там жёстко привязывать клиента к машине imho глупость, должны быть разграничены именно права клиентов, а не права машин в сети).
Сохранить себе на диск сертификат доверенного центра, выдавшего сертификат какому-либо ресурсу, можно прямо с помощью браузера:
Например, в мозилле это делается так:
- Заходим браузером на нужный ресурс по https
- Щёлкаем по зелёному замку в адресной строке и жмём на всплывающей вкладке стрелку вправо напротив замка и далее внизу кнопку «Подробнее»
- В открывшемся окне жмём «Просмотреть сертификат» и на следующей открывшейся странице переходим на вкладку «Подробности»
- В верхней части окна, подписанной «Иерархия сертификатов» выбираем нужный сертификат и жмём внизу «Экспортировать»
- Выбираем тип файла «Сертификат X.509 в формате PEM», указываем куда его сохранить и как назвать. (Так же можно выгрузить сразу весь бандл)
Вот отсюда можно скачать бандл сертификатов всех основных центров сертификации, выгруженный из мозиллы.
- Часть 1. Что такое Telegram боты и как они работают
- Часть 2. Регистрация аккаунтов Telegram ботов в картинках
- Часть 3. Пишем простого чат-бота для Telegram на чистом php (webhook)
- Часть 4. Прикручиваем MySQL к чат-боту для Telegram на php (webhook)
- Часть 5. Пишем Telegram бота на php для работы через longpolling
- Дополнение. Как отправлять правильные https-запросы через curl и при чём здесь SSL-cертификаты