From abe3fd7605872ba0d1376ef16b27319a80720f6f Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 16 Jul 2022 08:27:38 +0000 Subject: [PATCH] Improved check for invalid certificates --- src/Network/HTTPClient/Client/HttpClient.php | 11 +++++ .../HTTPClient/Client/HttpClientOptions.php | 6 +++ .../HTTPClient/Response/CurlResult.php | 4 ++ src/Util/Network.php | 45 ++----------------- 4 files changed, 25 insertions(+), 41 deletions(-) diff --git a/src/Network/HTTPClient/Client/HttpClient.php b/src/Network/HTTPClient/Client/HttpClient.php index 98d58e03c1..a33d9749c5 100644 --- a/src/Network/HTTPClient/Client/HttpClient.php +++ b/src/Network/HTTPClient/Client/HttpClient.php @@ -68,6 +68,13 @@ class HttpClient implements ICanSendHttpRequests $this->profiler->startRecording('network'); $this->logger->debug('Request start.', ['url' => $url, 'method' => $method]); + $host = parse_url($url, PHP_URL_HOST); + if(!filter_var($host, FILTER_VALIDATE_IP) && !@dns_get_record($host . '.', DNS_A + DNS_AAAA)) { + $this->logger->debug('URL cannot be resolved.', ['url' => $url, 'callstack' => System::callstack(20)]); + $this->profiler->stopRecording(); + return CurlResult::createErrorCurl($url); + } + if (Network::isLocalLink($url)) { $this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]); } @@ -125,6 +132,10 @@ class HttpClient implements ICanSendHttpRequests $conf[RequestOptions::TIMEOUT] = $opts[HttpClientOptions::TIMEOUT]; } + if (isset($opts[HttpClientOptions::VERIFY])) { + $conf[RequestOptions::VERIFY] = $opts[HttpClientOptions::VERIFY]; + } + if (!empty($opts[HttpClientOptions::BODY])) { $conf[RequestOptions::BODY] = $opts[HttpClientOptions::BODY]; } diff --git a/src/Network/HTTPClient/Client/HttpClientOptions.php b/src/Network/HTTPClient/Client/HttpClientOptions.php index fd0279a0ad..9a9ee772af 100644 --- a/src/Network/HTTPClient/Client/HttpClientOptions.php +++ b/src/Network/HTTPClient/Client/HttpClientOptions.php @@ -52,6 +52,12 @@ class HttpClientOptions * content_length: (int) maximum File content length */ const CONTENT_LENGTH = 'content_length'; + + /** + * verify: (bool|string, default=true) Describes the SSL certificate + */ + const VERIFY = 'verify'; + /** * body: (mixed) Setting the body for sending data */ diff --git a/src/Network/HTTPClient/Response/CurlResult.php b/src/Network/HTTPClient/Response/CurlResult.php index 9ae510b666..1b24cebf73 100644 --- a/src/Network/HTTPClient/Response/CurlResult.php +++ b/src/Network/HTTPClient/Response/CurlResult.php @@ -167,6 +167,10 @@ class CurlResult implements ICanHandleHttpResponses $this->isSuccess = false; } + if (empty($this->returnCode) && empty($this->header) && empty($this->body)) { + $this->isSuccess = false; + } + if (!$this->isSuccess) { Logger::debug('debug', ['info' => $this->info]); } diff --git a/src/Util/Network.php b/src/Util/Network.php index 1a9f7cb98a..2cc603e501 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -71,16 +71,17 @@ class Network $xrd_timeout = DI::config()->get('system', 'xrd_timeout'); $host = parse_url($url, PHP_URL_HOST); - if (empty($host) || !(@dns_get_record($host . '.', DNS_A + DNS_AAAA + DNS_CNAME) || filter_var($host, FILTER_VALIDATE_IP))) { + if (empty($host) || !(filter_var($host, FILTER_VALIDATE_IP) || @dns_get_record($host . '.', DNS_A + DNS_AAAA))) { return false; } if (in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'])) { - $curlResult = DI::httpClient()->head($url, [HttpClientOptions::TIMEOUT => $xrd_timeout]); + $options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout]; + $curlResult = DI::httpClient()->head($url, $options); // Workaround for systems that can't handle a HEAD request. Don't retry on timeouts. if (!$curlResult->isSuccess() && ($curlResult->getReturnCode() >= 400) && !in_array($curlResult->getReturnCode(), [408, 504])) { - $curlResult = DI::httpClient()->get($url, HttpClientAccept::DEFAULT, [HttpClientOptions::TIMEOUT => $xrd_timeout]); + $curlResult = DI::httpClient()->get($url, HttpClientAccept::DEFAULT, $options); } if (!$curlResult->isSuccess()) { @@ -91,44 +92,6 @@ class Network } } - // Check if the certificate is valid for this hostname - if (parse_url($url, PHP_URL_SCHEME) == 'https') { - $port = parse_url($url, PHP_URL_PORT) ?? 443; - - $context = stream_context_create(["ssl" => ['capture_peer_cert' => true]]); - - $resource = @stream_socket_client('ssl://' . $host . ':' . $port, $errno, $errstr, $xrd_timeout, STREAM_CLIENT_CONNECT, $context); - if (empty($resource)) { - Logger::notice('Invalid certificate', ['host' => $host]); - return false; - } - - $cert = stream_context_get_params($resource); - if (empty($cert)) { - Logger::notice('Invalid certificate params', ['host' => $host]); - return false; - } - - $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); - if (empty($certinfo)) { - Logger::notice('Invalid certificate information', ['host' => $host]); - return false; - } - - $valid_from = date(DATE_RFC2822,$certinfo['validFrom_time_t']); - $valid_to = date(DATE_RFC2822,$certinfo['validTo_time_t']); - - if ($certinfo['validFrom_time_t'] > time()) { - Logger::notice('Certificate validity starts after current date', ['host' => $host, 'from' => $valid_from, 'to' => $valid_to]); - return false; - } - - if ($certinfo['validTo_time_t'] < time()) { - Logger::notice('Certificate validity ends before current date', ['host' => $host, 'from' => $valid_from, 'to' => $valid_to]); - return false; - } - } - return $url; }