Как проверить, что сертификат SMTP TLS действителен в PHP?

Чтобы предотвратить атаки типа «злоумышленник в середине» (сервер, притворяющийся кем-то другим), я хотел бы убедиться, что у 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-сервер действительно является тем, кем я думаю?

Обновлять

На основе этого комментария на PHP.net Кажется, я могу выполнять проверки SSL, используя некоторые параметры потока. Самое приятное то, что stream_context_set_option принимает контекст или ресурс потока. Следовательно, в какой-то момент вашего TCP-соединения вы можете переключиться на SSL с помощью пакета сертификатов CA.

$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, используя параметры контекста Steam, и проанализировать его с помощью openssl_x509_parse.

$cont = stream_context_get_params($r);
print_r(openssl_x509_parse($cont["options"]["ssl"]["peer_certificate"]));

person Xeoncross    schedule 15.11.2012    source источник
comment
Вы видели php.net/manual/en/book.openssl.php   -  person Lawrence Cherone    schedule 15.11.2012
comment
Да, я знаю, что и cURL, и OpenSSL поддерживают проверку сертификата через параметр, определяющий список корневого центра сертификации. Однако я не уверен, как (или если) я могу заставить его работать с потоком / сокетом PHP. Это выглядит многообещающим.   -  person Xeoncross    schedule 15.11.2012
comment
Сертификат не принадлежит IP. Что вы можете проверить: если имя хоста, к которому вы пытаетесь подключиться, совпадает с именем из сертификата, если прямой и обратный поиск DNS совпадают (все еще хост может быть многодомным, но это выполнимо), если эмитент сертификата является доверенным и цепочка сертификатов сохраняется и, наконец, если сертификат не отозван   -  person Marcin Orlowski    schedule 17.11.2012
comment
@ WebnetMobile.com, поэтому вопрос в том, как мне это сделать в PHP?   -  person Xeoncross    schedule 17.11.2012
comment
Если вы уже подтвердили сертификат с помощью CA, теперь вы можете проверить атрибут CN в сертификате. если сертификат самоподписан, вы должны установить этот сертификат где-нибудь, чтобы использовать его в качестве CA позже,   -  person zb'    schedule 18.11.2012


Ответы (3)


Чтобы не загружать и без того длинный и не слишком посвященный теме ответ с дополнительным текстом, я оставляю его, чтобы разобраться, почему и почему, а здесь я опишу как.

Я протестировал этот код на Google и паре других серверов; какие там есть комментарии, ну комментарии в коде.

<?php
    $server   = "smtp.gmail.com";        // Who I connect to
    $myself   = "my_server.example.com"; // Who I am
    $cabundle = '/etc/ssl/cacert.pem';   // Where my root certificates are

    // Verify server. There's not much we can do, if we suppose that an attacker
    // has taken control of the DNS. The most we can hope for is that there will
    // be discrepancies between the expected responses to the following code and
    // the answers from the subverted DNS server.

    // To detect these discrepancies though, implies we knew the proper response
    // and saved it in the code. At that point we might as well save the IP, and
    // decouple from the DNS altogether.

    $match1   = false;
    $addrs    = gethostbynamel($server);
    foreach($addrs as $addr)
    {
        $name = gethostbyaddr($addr);
        if ($name == $server)
        {
            $match1 = true;
            break;
        }
    }
    // Here we must decide what to do if $match1 is false.
    // Which may happen often and for legitimate reasons.
    print "Test 1: " . ($match1 ? "PASSED" : "FAILED") . "\n";

    $match2   = false;
    $domain   = explode('.', $server);
    array_shift($domain);
    $domain = implode('.', $domain);
    getmxrr($domain, $mxhosts);
    foreach($mxhosts as $mxhost)
    {
        $tests = gethostbynamel($mxhost);
        if (0 != count(array_intersect($addrs, $tests)))
        {
            // One of the instances of $server is a MX for its domain
            $match2 = true;
            break;
        }
    }
    // Again here we must decide what to do if $match2 is false.
    // Most small ISP pass test 2; very large ISPs and Google fail.
    print "Test 2: " . ($match2 ? "PASSED" : "FAILED") . "\n";
    // On the other hand, if you have a PASS on a server you use,
    // it's unlikely to become a FAIL anytime soon.

    // End of maybe-they-help-maybe-they-don't checks.

    // Establish the connection on SMTP port 25
    $smtp = fsockopen( "tcp://{$server}", 25, $errno, $errstr );
    fread( $smtp, 512 );

    // Here you can check the usual banner from $server (or in general,
    // check whether it contains $server's domain name, or whether the
    // domain it advertises has $server among its MX's.
    // But yet again, Google fails both these tests.

    fwrite($smtp,"HELO {$myself}\r\n");
    fread($smtp, 512);

    // Switch to TLS
    fwrite($smtp,"STARTTLS\r\n");
    fread($smtp, 512);
    stream_set_blocking($smtp, true);
    stream_context_set_option($smtp, 'ssl', 'verify_peer', true);
    stream_context_set_option($smtp, 'ssl', 'allow_self_signed', false);
    stream_context_set_option($smtp, 'ssl', 'capture_peer_cert', true);
    stream_context_set_option($smtp, 'ssl', 'cafile', $cabundle);
    $secure = stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
    stream_set_blocking($smtp, false);
    $opts = stream_context_get_options($smtp);
    if (!isset($opts['ssl']['peer_certificate'])) {
        $secure = false;
    } else {
        $cert = openssl_x509_parse($opts['ssl']['peer_certificate']);
        $names = '';
        if ('' != $cert) {
            if (isset($cert['extensions'])) {
                $names = $cert['extensions']['subjectAltName'];
            } elseif (isset($cert['subject'])) {
                if (isset($cert['subject']['CN'])) {
                    $names = 'DNS:' . $cert['subject']['CN'];
                } else {
                    $secure = false; // No exts, subject without CN
                }
            } else {
                $secure = false; // No exts, no subject
            }
        }
        $checks = explode(',', $names);

        // At least one $check must match $server
        $tmp    = explode('.', $server);
        $fles   = array_reverse($tmp);
        $okay   = false;
        foreach($checks as $check) {
            $tmp = explode(':', $check);
            if ('DNS' != $tmp[0])    continue;  // candidates must start with DNS:
            if (!isset($tmp[1]))     continue;  // and have something afterwards
            $tmp  = explode('.', $tmp[1]);
            if (count($tmp) < 3)     continue;  // "*.com" is not a valid match
            $cand = array_reverse($tmp);
            $okay = true;
            foreach($cand as $i => $item) {
                if (!isset($fles[$i])) {
                    // We connected to www.example.com and certificate is for *.www.example.com -- bad.
                    $okay = false;
                    break;
                }
                if ($fles[$i] == $item) {
                    continue;
                }
                if ($item == '*') {
                    break;
                }
            }
            if ($okay) {
                break;
            }
        }
        if (!$okay) {
            $secure = false; // No hosts matched our server.
        }
    }

    if (!$secure) {
            die("failed to connect securely\n");
    }
    print "Success!\n";
    // Continue with connection...
person LSerni    schedule 18.11.2012
comment
Обязательно прочитайте предыдущий подробный ответ @lserni для обсуждения этого кода. - person Xeoncross; 21.11.2012

ОБНОВЛЕНИЕ: есть способ лучше, см. комментарии.

Вы можете захватить сертификат и поговорить с сервером, используя openssl в качестве фильтра. Таким образом, вы можете извлечь сертификат и изучить его во время того же подключения.

Это неполная реализация (фактического разговора об отправке почты нет), которая должна помочь вам:

<?php
    $server = 'smtp.gmail.com';

    $pid    = proc_open("openssl s_client -connect $server:25 -starttls smtp",
                    array(
                            0 => array('pipe', 'r'),
                            1 => array('pipe', 'w'),
                            2 => array('pipe', 'r'),
                    ),
                    $pipes,
                    '/tmp',
                    array()
            );
    list($smtpout, $smtpin, $smtperr) = $pipes; unset($pipes);

    $stage  = 0;
    $cert   = 0;
    $certificate = '';
    while(($stage < 5) && (!feof($smtpin)))
    {
            $line = fgets($smtpin, 1024);
            switch(trim($line))
            {
                    case '-----BEGIN CERTIFICATE-----':
                            $cert   = 1;
                            break;
                    case '-----END CERTIFICATE-----':
                            $certificate .= $line;
                            $cert   = 0;
                            break;
                    case '---':
                            $stage++;
            }
            if ($cert)
                    $certificate .= $line;
    }
    fwrite($smtpout,"HELO mail.example.me\r\n"); // .me is client, .com is server
    print fgets($smtpin, 512);
    fwrite($smtpout,"QUIT\r\n");
    print fgets($smtpin, 512);

    fclose($smtpin);
    fclose($smtpout);
    fclose($smtperr);
    proc_close($pid);

    print $certificate;

    $par    = openssl_x509_parse($certificate);
?>

Конечно, вы переместите синтаксический анализ и проверку сертификата, прежде чем отправлять что-либо значимое на сервер.

В массиве $par вы должны найти (среди прочего) имя, такое же, как и тема.

Array
(
    [name] => /C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
    [subject] => Array
        (
            [C] => US
            [ST] => California
            [L] => Mountain View
            [O] => Google Inc
            [CN] => smtp.gmail.com
        )

    [hash] => 11e1af25
    [issuer] => Array
        (
            [C] => US
            [O] => Google Inc
            [CN] => Google Internet Authority
        )

    [version] => 2
    [serialNumber] => 280777854109761182656680
    [validFrom] => 120912115750Z
    [validTo] => 130607194327Z
    [validFrom_time_t] => 1347451070
    [validTo_time_t] => 1370634207
    ...
    [extensions] => Array
        (
            ...
            [subjectAltName] => DNS:smtp.gmail.com
        )

Для проверки действительности, помимо проверки даты и т. Д., Которую SSL выполняет сам по себе, вы должны убедиться, что применяются ЛИБО из этих условий:

  • CN объекта - это ваше DNS-имя, например "CN = smtp.your.server.com"

  • определены расширения, и они содержат subjectAltName, которое после разнесения explode(',', $subjectAltName) дает массив записей с префиксом DNS:, по крайней мере одна из которых совпадает с вашим DNS-именем. Если ничего не найдено, сертификат отклоняется.

Проверка сертификата в PHP

Значение проверить хост в различных программах кажется в лучшем случае неясным < / а>.

Поэтому я решил разобраться в этом, загрузил исходный код OpenSSL (openssl-1.0.1c) и попытался проверить сам.

Я не нашел ссылок на ожидаемый код, а именно:

  • пытается разобрать строку, разделенную двоеточиями
  • ссылки на subjectAltName (который OpenSSL называет SN_subject_alt_name)
  • использование "DNS [:]" в качестве разделителя

OpenSSL, кажется, помещает все детали сертификата в структуру, запускает очень простые тесты на некоторых из них, но большинство полей, "удобочитаемых человеком", остаются в покое. Это имеет смысл: можно утверждать, что проверка имени находится на более высоком уровне, чем проверка подписи сертификата.

Затем я загрузил также последнюю версию cURL и последнюю версию архива PHP.

В исходном коде PHP я тоже ничего не нашел; очевидно, любые параметры просто передаются по строке и в противном случае игнорируются. Этот код запустился без предупреждения:

    stream_context_set_option($smtp, 'ssl', 'I-want-a-banana', True);

и stream_context_get_options позже послушно извлечены

    [ssl] => Array
        (
            [I-want-a-banana] => 1
            ...

Это тоже имеет смысл: PHP не может знать в контексте «настройки параметров контекста», какие параметры будут использоваться в дальнейшем.

Точно так же код синтаксического анализа сертификата разбирает сертификат и извлекает информацию, помещенную туда OpenSSL, но не проверяет ту же самую информацию.

Итак, я копнул немного глубже и, наконец, нашел код подтверждения сертификата в cURL, здесь:

// curl-7.28.0/lib/ssluse.c

static CURLcode verifyhost(struct connectdata *conn,
                       X509 *server_cert)
{

где он делает то, что я ожидал: он ищет subjectAltNames, он проверяет их все на работоспособность и запускает их за hostmatch, где выполняются проверки вроде hello.example.com == * .example.com. Существуют дополнительные проверки работоспособности: «Нам требуется как минимум 2 точки в шаблоне, чтобы избежать слишком широкого совпадения с подстановочными знаками». и xn-- чеки.

Подводя итог, OpenSSL выполняет несколько простых проверок, а все остальное оставляет вызывающему. cURL, вызывающий OpenSSL, выполняет больше проверок. PHP тоже выполняет некоторые проверки CN с verify_peer, но оставляет subjectAltName в покое. Эти проверки меня не слишком убеждают; см. ниже в разделе «Тест».

Из-за отсутствия возможности доступа к функциям cURL лучшей альтернативой является повторная реализация функций PHP.

Например, сопоставление переменных подстановочных доменов может быть выполнено путем расстановки точек как фактического домена, так и домена сертификата, поменяв местами два массива

com.example.site.my
com.example.*

и убедитесь, что соответствующие элементы либо равны, либо сертификат имеет *; если это произойдет, мы должны уже проверить как минимум два компонента, здесь com и example.

Я считаю, что приведенное выше решение является одним из лучших, если вы хотите проверять сертификаты за один раз. Еще лучше было бы открыть поток напрямую, не прибегая к openssl клиенту - и это возможно; см. комментарий.

Контрольная работа

У меня есть хороший, действующий и полностью доверенный сертификат от Thawte, выданный для mail.eve.com.

Приведенный выше код, запущенный на Алисе, затем будет безопасно подключаться к mail.eve.com, и это происходит, как и ожидалось.

Теперь я устанавливаю тот же сертификат на mail.bob.com или каким-то другим способом убеждаю DNS, что моим сервером является Боб, хотя на самом деле это все еще Ева.

Я ожидаю, что SSL-соединение по-прежнему работает (сертификат действителен и является надежным), но сертификат не выдается Бобу - он выдается Еве. Итак, кто-то должен выполнить эту последнюю проверку и предупредить Алису, что Боб фактически выдает себя за Евы (или, что то же самое, что Боб использует украденный сертификат Евы).

Я использовал приведенный ниже код:

    $smtp = fsockopen( "tcp://mail.bob.com", 25, $errno, $errstr );
    fread( $smtp, 512 );
    fwrite($smtp,"HELO alice\r\n");
    fread($smtp, 512);
    fwrite($smtp,"STARTTLS\r\n");
    fread($smtp, 512);
    stream_set_blocking($smtp, true);
    stream_context_set_option($smtp, 'ssl', 'verify_host', true);
    stream_context_set_option($smtp, 'ssl', 'verify_peer', true);
    stream_context_set_option($smtp, 'ssl', 'allow_self_signed', false);
    stream_context_set_option($smtp, 'ssl', 'cafile', '/etc/ssl/cacert.pem');
    $secure = stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
    stream_set_blocking($smtp, false);
    print_r(stream_context_get_options($smtp));
    if( ! $secure)
            die("failed to connect securely\n");
    print "Success!\n";

и:

  • if the certificate is not verifiable with a trusted authority:
    • verify_host does nothing
    • verify_peer TRUE вызывает ошибку
    • verify_peer FALSE разрешает соединение
    • allow_self_signed ничего не делает
  • if the certificate is expired:
    • I get an error.
  • if the certificate is verifiable:
    • connection is allowed to "mail.eve.com" impersonating "mail.bob.com" and I get a "Success!" message.

Я понимаю, что это означает, что, за исключением некоторых глупых ошибок с моей стороны, PHP сам по себе не проверяет сертификаты по именам.

Используя код proc_open в начале этого сообщения, я снова могу подключиться, но на этот раз у меня есть доступ к subjectAltName, и поэтому я могу проверить его самостоятельно, обнаружив олицетворение.

person LSerni    schedule 18.11.2012
comment
Но делает ли stream_context_set_option($resource, 'ssl', 'verify_host', true); ту же самую проверку? - person Xeoncross; 18.11.2012
comment
Я провел небольшое исследование и надеюсь, что ошибаюсь, но, похоже, это не работает. Позже сегодня я создам сайт с неправильным названием сертификата, чтобы посмотреть, не пропустил ли я как-то этап проверки. - person LSerni; 18.11.2012
comment
Возможно, я ошибаюсь, но тест подтвердил, что PHP не (по крайней мере, по умолчанию) не проверяет соответствие доменного имени сертификата, даже при наличии различных опций SSL. - person LSerni; 18.11.2012
comment
Параметр verify_host не существует (это verify_peer). И нет, он не проверяет это. Но можно получить сертификат, как вы указали в обновлении 2 (с небольшими изменениями): stream_context_set_option($smtp, 'ssl', 'capture_peer_cert', true); ... $cont = stream_context_get_options($smtp); $cert = openssl_x509_parse($cont["ssl"]["peer_certificate"]);. Как только вы это сделаете, вы можете проверить, существует ли subjectAltName. Если это не так, вы проверяете CN. Если это так, вы взрываете его и проверяете все записи DNS:, оценивая совпадения. - person LSerni; 18.11.2012
comment
@Iserni, этот документ, на который вы ссылались, отвечает на многие вопросы безопасности о SSL не правильно проверяет CN в сертификате. Однако я обнаружил, что этот stream_context_set_option($stream, 'ssl', 'CN_match', $host); - это то, что нужно для проверки совпадения хоста, с которым, как мне кажется, я тоже подключаюсь. После включения этой опции я больше не могу подключаться к Google через STARTTLS, поскольку библиотека SSL правильно отклоняет их недопустимое имя CN. Позор Google. - person Xeoncross; 19.11.2012
comment
Тем не менее, вы по-прежнему подвержены риску ложноотрицательных результатов, т. Е. Многосетевого хоста, имеющего некоторый случайный CN (даже если подстановочный знак может помочь) и настоящие сертифицированные домены в subjectAltName. Но поездка была действительно интересной :-) - person LSerni; 19.11.2012
comment
Шикарный обзор, наконец-то я все это доделал. Но для меня это еще не конец. Как и в вашем первом примере, на самом деле есть способ получить сертификат (stream_context_set_option($stream, 'ssl', 'capture_peer_cert', true);), не переходя к прямому системному вызову OpenSSL. $meta = stream_context_get_params($stream); $cert = openssl_x509_parse($meta["options"]["ssl"]["peer_certificate"]); Обновление: я только что понял, что вы сделали это ниже - person Xeoncross; 19.11.2012
comment
Я запускал mantis на chrooted nginx, и отправка писем через PHPMailer не работала. Благодаря вашей статье мне удалось исправить свою проблему - спасибо !!! - person Greg0ry; 04.02.2015

как проверить, действительно ли действующий сертификат принадлежит домену / IP-адресу, к которому я подключаюсь?

Сертификаты выдаются для доменных имен (никогда для IP). Это может быть одно доменное имя (например, mail.example.com) или подстановочный знак *.example.com). После декодирования сертификата с помощью openssl вы можете прочитать это имя, которое называется common name в поле cn. Затем вам просто нужно проверить, является ли машина, которую вы пытаетесь подключить, той из сертификата. Поскольку у вас есть имя удаленного однорангового узла, когда вы уже подключаетесь к нему, проверка довольно тривиальна, однако, в зависимости от того, насколько параноидальные проверки вы хотите выполнить, вы можете попытаться выяснить, не используете ли вы зараженный DNS, который разрешает ваш mail.example.com имя хоста для поддельного IP. Это следует сделать, сначала разрешив mail.example.com с помощью gethostbynamel (), который даст у вас есть хотя бы один IP-адрес (допустим, вы получили только 1.2.3.4). Затем вы проверяете обратный DNS с помощью gethostbyaddr () для каждого возвращенного IP-адреса, и один из них должен вернуть mail.example.com (обратите внимание, что я использовал gethostbynamel(), а не gethostbyname(), поскольку нередко серверу назначается более одного IP-адреса для каждого имени).

ПРИМЕЧАНИЕ. Будьте осторожны, пытаясь применить слишком строгие правила - вы можете навредить своим пользователям. Это довольно популярный сценарий, когда на одном сервере размещается много доменов (например, при виртуальном хостинге). В таком случае сервер использует IP 1.2.3.4, домену клиента example.com присваивается этот IP-адрес (поэтому разрешение example.com даст вам 1.2.3.4, однако обратный DNS для этого хоста скорее всего будет чем-то другим, связь с интернет-провайдером доменное имя, а не домен клиента, например box0123.hosterdomain.com или 4-3-2-1.hosterdomain.com. И все это совершенно нормально и законно. Хостеры делают это, потому что технически вы можете назначить один IP-адрес нескольким доменным именам одновременно, но с обратным DNS вы можете назначить только одна запись для каждого IP-адреса. И, используя собственное доменное имя вместо имени клиентов, вам не нужно беспокоить revDNS независимо от того, добавляются ли клиенты или удаляются с сервера.

Итак, если у вас есть закрытые списки хостов, к которым вы будете подключаться - вы можете выполнить этот тест, но если ваши пользователи могут попытаться подключиться где угодно, я бы просто придерживался проверки только цепочки сертификатов.

ИЗМЕНИТЬ №1

Если вы запрашиваете DNS, который вы не контролируете, вы не можете полностью доверять ему. Такой DNS можно превратить в зомби, отравить, и он просто может все время лгать и поддельный ответ на любой запрос, который вы ему задаете, как "вперед" (FQDN в IP), так и в обратном направлении (с IP в FQDN ). Если DNS-сервер взломан (рутирован), он может (если злоумышленник достаточно мотивирован) заставить его не пересылать in-addr.arpa запросов и подделывать ответ, чтобы он соответствовал другим ответам (подробнее на обратный поиск здесь). Так что на самом деле, если вы не используете DNSSEC, есть способ обмануть ваши чеки. Итак, вы должны подумать, насколько параноиком вам нужно действовать - переадресация очереди может быть подделана путем отравления DNS, в то время как это не работает для обратного просмотра, если хост не ваш (я имею в виду, что его обратная зона DNS размещена на каком-то другом сервере, кроме один, отвечающий на ваши обычные вопросы). Вы можете попытаться обезопасить себя от локального заражения DNS, например, запросив несколько DNS напрямую, так что даже один будет взломан, а другие, вероятно, нет. Если все в порядке, все запросы DNS должны дать вам одинаковый ответ. Если что-то подозрительно, некоторые ответы будут отличаться, что вы легко заметите.

Так что все зависит от того, насколько вы хотите быть в безопасности и чего хотите достичь. Если вам нужен высокий уровень безопасности, вы не должны использовать «общедоступные» сервисы и напрямую туннелировать свой трафик на целевые сервисы, например, используя VPN.

ИЗМЕНИТЬ №2

Что касается IPv4 и IPv6 - в PHP отсутствуют функции для обоих, поэтому, если вы хотите выполнить упомянутые выше проверки, я бы предпочел рассмотреть возможность вызова таких инструментов, как host, для выполнения этой работы (или написания расширения PHP).

person Marcin Orlowski    schedule 18.11.2012
comment
Какое значение имеет IP6? - person Xeoncross; 18.11.2012
comment
Похоже, что если бы ящик был установлен в правильном месте ниже по течению, он мог бы обмануть ваши DNS-запросы, а также подделать его IP-адрес для соответствия. В этом случае кажется, что вы можете сравнить строковое имя домена в сертификате с доменом, который, по вашему мнению, вы тоже подключились, что опять же может быть ложью. Итак, я полагаю, что окончательного решения не существует. - person Xeoncross; 18.11.2012
comment
Добавлены еще несколько примечаний по перехвату DNS и связанным с ним темам. Что касается IPv6 против IPv4 - это не имеет особого значения - вы в данный момент думаете только об IP-адресе, независимо от того, в каком он формате .. - person Marcin Orlowski; 18.11.2012
comment
Добавлен РЕДАКТИРОВАНИЕ # 2 также для IPv4 / v6. - person Marcin Orlowski; 18.11.2012
comment
Во-первых, сертификаты можно выдавать на IP-адреса, по крайней мере, для HTTPS (но это действительно плохая идея). Во-вторых, вам никогда не нужно проверять обратную запись DNS. Проверка сертификата должна всегда основываться на имени, к которому вы собираетесь подключиться (и желательно с использованием альтернативных имен субъектов, если они доступны). Обратный поиск не входит в спецификации и не добавляет безопасности для этого: либо вы доверяете сертификату, либо нет. Разрешение DNS вообще не имеет отношения к этому. - person Bruno; 18.11.2012
comment
Выпуск сертификата для IP-адреса технически возможен, но это так мало смысла, что я думаю, вы не сможете купить его ни в одном ЦС. Что касается проверки revDNS - если OP хочет проверить или у него есть причина для этого - он может это сделать, это ему решать. Никто не говорит, что это часть чего-либо - person Marcin Orlowski; 18.11.2012
comment
Я просто говорю, что разрешение DNS не имеет ничего общего с проверкой имени хоста сертификата. Выполнение обратного поиска, как правило, вызовет проблемы и не добавит дополнительной безопасности. Если у вас зараженный DNS и из-за этого вы подключаетесь к неправильному IP-адресу, у поддельного хоста просто не будет доверенного сертификата для хоста, который вы искали. Проверка личности выполняется исключительно с использованием сертификата, которому можно доверять, и искомого имени хоста. DNS тут ни при чем. - person Bruno; 18.11.2012
comment
Вы в основном написали то же самое, что и я в своем ответе. - person Marcin Orlowski; 18.11.2012
comment
3/4 вашего ответа касается работы с обратным DNS и отравленным DNS, что совершенно неуместно (и почти вводит в заблуждение). Полезны только первые 4 предложения, и вы даже не упоминаете альтернативные имена субъектов. - person Bruno; 18.11.2012
comment
это в основном потому, что OP спросил, как это сделать. Нет, если это должно быть сделано. - person Marcin Orlowski; 18.11.2012
comment
Спасибо @Bruno и WebnetMobile, я предполагал, что отравленный DNS сделает проверки rDNS / PTR бесполезными, поэтому проверка сертификата на хосте, который, как я полагаю, я подключаюсь, кажется единственным вариантом. Вдобавок (крайний случай), если они лгали, кажется, что они все еще могут зарегистрироваться для получения действительного сертификата для хоста, который они хотели использовать, а затем расположиться между моим сервером и внешним миром и представьте свой действительный сертификат, утверждающий, что это тот хост, и у меня не будет возможности узнать, что что-то не так. Это требует больших усилий и натянуто, но кажется возможным. - person Xeoncross; 19.11.2012