/** * Verifies the received SSL certificate against it's Common Names and subjectAltName fields * * PHP's SSL verifications only verify that it's a valid Certificate, it doesn't verify if * the certificate is valid for the hostname which was requested. * This function verifies the requested hostname against certificate's subjectAltName field, * if that is empty, or contains no DNS entries, a fallback to the Common Name field is used. * * IP Address support is included if the request is being made to an IP address. * * @since 3.7.0 * @static * * @param stream $stream The PHP Stream which the SSL request is being made over * @param string $host The hostname being requested * @return bool If the cerficiate presented in $stream is valid for $host */ public static function verify_ssl_certificate($stream, $host) { $context_options = stream_context_get_options($stream); if (empty($context_options['ssl']['peer_certificate'])) { return false; } $cert = openssl_x509_parse($context_options['ssl']['peer_certificate']); if (!$cert) { return false; } /* * If the request is being made to an IP address, we'll validate against IP fields * in the cert (if they exist) */ $host_type = WP_HTTP::is_ip_address($host) ? 'ip' : 'dns'; $certificate_hostnames = array(); if (!empty($cert['extensions']['subjectAltName'])) { $match_against = preg_split('/,\\s*/', $cert['extensions']['subjectAltName']); foreach ($match_against as $match) { list($match_type, $match_host) = explode(':', $match); if ($host_type == strtolower(trim($match_type))) { // IP: or DNS: $certificate_hostnames[] = strtolower(trim($match_host)); } } } elseif (!empty($cert['subject']['CN'])) { // Only use the CN when the certificate includes no subjectAltName extension. $certificate_hostnames[] = strtolower($cert['subject']['CN']); } // Exact hostname/IP matches. if (in_array(strtolower($host), $certificate_hostnames)) { return true; } // IP's can't be wildcards, Stop processing. if ('ip' == $host_type) { return false; } // Test to see if the domain is at least 2 deep for wildcard support. if (substr_count($host, '.') < 2) { return false; } // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com. $wildcard_host = preg_replace('/^[^.]+\\./', '*.', $host); return in_array(strtolower($wildcard_host), $certificate_hostnames); }