Чтобы предотвратить атаки "человек-в-середине" (сервер, притворяющийся кем-то другим), я хотел бы проверить, что SMTP-сервер, с которым я подключаюсь слишком по SSL, имеет действительный сертификат SSL, который доказывает, что это тот, есть.
Например, после подключения к SMTP-серверу на порту 25 я могу переключиться на безопасное соединение, например:
<?php
$smtp = fsockopen( "tcp://mail.example.com", 25, $errno, $errstr );
fread( $smtp, 512 );
fwrite($smtp,"HELO mail.example.me\r\n"); // .me is client, .com is server
fread($smtp, 512);
fwrite($smtp,"STARTTLS\r\n");
fread($smtp, 512);
stream_socket_enable_crypto( $smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
fwrite($smtp,"HELO mail.example.me\r\n");
Однако не упоминается, где PHP проверяет сертификат SSL. Есть ли у PHP встроенный список корневых центров сертификации? Это просто что-то принимает?
Каков правильный способ проверки сертификата, и что SMTP-сервер действительно является тем, кем я считаю?
Update
На основе этот комментарий на PHP.net кажется, что я могу выполнять проверки SSL с использованием некоторых параметров потока. Наилучшая часть состоит в том, что stream_context_set_option принимает контекст или ресурс потока. Поэтому в какой-то момент вашего TCP-соединения вы можете переключиться на SSL, используя CA cert bundle.
$resource = fsockopen( "tcp://mail.example.com", 25, $errno, $errstr );
...
stream_set_blocking($resource, true);
stream_context_set_option($resource, 'ssl', 'verify_host', true);
stream_context_set_option($resource, 'ssl', 'verify_peer', true);
stream_context_set_option($resource, 'ssl', 'allow_self_signed', false);
stream_context_set_option($resource, 'ssl', 'cafile', __DIR__ . '/cacert.pem');
$secure = stream_socket_enable_crypto($resource, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
stream_set_blocking($resource, false);
if( ! $secure)
{
die("failed to connect securely\n");
}
Также см. Параметры и параметры контекста, которые расширяются на Параметры SSL.
Однако, хотя теперь это решает основную проблему - как проверить, действительно ли действительный сертификат принадлежит домену /IP, к которому я подключаюсь?
Другими словами, сертификат, к которому подключается сервер, может иметь действительный сертификат, но как я могу его узнать для "example.com", а не для другого сервера, использующего действительный сертификат, чтобы действовать как "example.com"?
Обновление 2
Кажется, что вы можете захватить сертификат SSL с использованием параметров пара и проанализировать его с помощью openssl_x509_parse.
$cont = stream_context_get_params($r);
print_r(openssl_x509_parse($cont["options"]["ssl"]["peer_certificate"]));