[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/Requests/src/ -> Ssl.php (source)

   1  <?php
   2  /**
   3   * SSL utilities for Requests
   4   *
   5   * @package Requests\Utilities
   6   */
   7  
   8  namespace WpOrg\Requests;
   9  
  10  use WpOrg\Requests\Exception\InvalidArgument;
  11  use WpOrg\Requests\Utility\InputValidator;
  12  use WpOrg\Requests\Utility\Trim;
  13  
  14  /**
  15   * SSL utilities for Requests
  16   *
  17   * Collection of utilities for working with and verifying SSL certificates.
  18   *
  19   * @package Requests\Utilities
  20   */
  21  final class Ssl {
  22      /**
  23       * Verify the certificate against common name and subject alternative names
  24       *
  25       * Unfortunately, PHP doesn't check the certificate against the alternative
  26       * names, leading things like 'https://www.github.com/' to be invalid.
  27       *
  28       * @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
  29       *
  30       * @param string|Stringable $host Host name to verify against
  31       * @param array $cert Certificate data from openssl_x509_parse()
  32       * @return bool
  33       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $host argument is not a string or a stringable object.
  34       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $cert argument is not an array or array accessible.
  35       */
  36  	public static function verify_certificate($host, $cert) {
  37          if (InputValidator::is_string_or_stringable($host) === false) {
  38              throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
  39          }
  40  
  41          if (InputValidator::has_array_access($cert) === false) {
  42              throw InvalidArgument::create(2, '$cert', 'array|ArrayAccess', gettype($cert));
  43          }
  44  
  45          $has_dns_alt = false;
  46  
  47          // Check the subjectAltName
  48          if (!empty($cert['extensions']['subjectAltName'])) {
  49              $altnames = explode(',', $cert['extensions']['subjectAltName']);
  50              foreach ($altnames as $altname) {
  51                  $altname = trim($altname, Trim::WHITESPACE_CHARS_NO_FF);
  52                  if (strpos($altname, 'DNS:') !== 0) {
  53                      continue;
  54                  }
  55  
  56                  $has_dns_alt = true;
  57  
  58                  // Strip the 'DNS:' prefix and trim whitespace
  59                  $altname = trim(substr($altname, 4), Trim::WHITESPACE_CHARS_NO_FF);
  60  
  61                  // Check for a match
  62                  if (self::match_domain($host, $altname) === true) {
  63                      return true;
  64                  }
  65              }
  66  
  67              if ($has_dns_alt === true) {
  68                  return false;
  69              }
  70          }
  71  
  72          // Fall back to checking the common name if we didn't get any dNSName
  73          // alt names, as per RFC2818
  74          if (!empty($cert['subject']['CN'])) {
  75              // Check for a match
  76              return (self::match_domain($host, $cert['subject']['CN']) === true);
  77          }
  78  
  79          return false;
  80      }
  81  
  82      /**
  83       * Verify that a reference name is valid
  84       *
  85       * Verifies a dNSName for HTTPS usage, (almost) as per Firefox's rules:
  86       * - Wildcards can only occur in a name with more than 3 components
  87       * - Wildcards can only occur as the last character in the first
  88       *   component
  89       * - Wildcards may be preceded by additional characters
  90       *
  91       * We modify these rules to be a bit stricter and only allow the wildcard
  92       * character to be the full first component; that is, with the exclusion of
  93       * the third rule.
  94       *
  95       * @param string|Stringable $reference Reference dNSName
  96       * @return boolean Is the name valid?
  97       * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
  98       */
  99  	public static function verify_reference_name($reference) {
 100          if (InputValidator::is_string_or_stringable($reference) === false) {
 101              throw InvalidArgument::create(1, '$reference', 'string|Stringable', gettype($reference));
 102          }
 103  
 104          if ($reference === '') {
 105              return false;
 106          }
 107  
 108          if (preg_match('`\s`', $reference) > 0) {
 109              // Whitespace detected. This can never be a dNSName.
 110              return false;
 111          }
 112  
 113          $parts = explode('.', $reference);
 114          if ($parts !== array_filter($parts)) {
 115              // DNSName cannot contain two dots next to each other.
 116              return false;
 117          }
 118  
 119          // Check the first part of the name
 120          $first = array_shift($parts);
 121  
 122          if (strpos($first, '*') !== false) {
 123              // Check that the wildcard is the full part
 124              if ($first !== '*') {
 125                  return false;
 126              }
 127  
 128              // Check that we have at least 3 components (including first)
 129              if (count($parts) < 2) {
 130                  return false;
 131              }
 132          }
 133  
 134          // Check the remaining parts
 135          foreach ($parts as $part) {
 136              if (strpos($part, '*') !== false) {
 137                  return false;
 138              }
 139          }
 140  
 141          // Nothing found, verified!
 142          return true;
 143      }
 144  
 145      /**
 146       * Match a hostname against a dNSName reference
 147       *
 148       * @param string|Stringable $host Requested host
 149       * @param string|Stringable $reference dNSName to match against
 150       * @return boolean Does the domain match?
 151       * @throws \WpOrg\Requests\Exception\InvalidArgument When either of the passed arguments is not a string or a stringable object.
 152       */
 153  	public static function match_domain($host, $reference) {
 154          if (InputValidator::is_string_or_stringable($host) === false) {
 155              throw InvalidArgument::create(1, '$host', 'string|Stringable', gettype($host));
 156          }
 157  
 158          // Check if the reference is blocklisted first
 159          if (self::verify_reference_name($reference) !== true) {
 160              return false;
 161          }
 162  
 163          // Check for a direct match
 164          if ((string) $host === (string) $reference) {
 165              return true;
 166          }
 167  
 168          // Calculate the valid wildcard match if the host is not an IP address
 169          // Also validates that the host has 3 parts or more, as per Firefox's ruleset,
 170          // as a wildcard reference is only allowed with 3 parts or more, so the
 171          // comparison will never match if host doesn't contain 3 parts or more as well.
 172          if (ip2long($host) === false) {
 173              $parts    = explode('.', $host);
 174              $parts[0] = '*';
 175              $wildcard = implode('.', $parts);
 176              if ($wildcard === (string) $reference) {
 177                  return true;
 178              }
 179          }
 180  
 181          return false;
 182      }
 183  }


Generated : Sat Nov 14 08:20:01 2026 Cross-referenced by PHPXref