From 9d348e26f3b05295cd71f6f939ae244f8a096779 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Fri, 29 Jan 2021 19:10:58 +0000 Subject: [PATCH] Security, Site Health: Improve accuracy in messaging about HTTPS support. Following up on [49904], this changeset focuses mainly on improving the guidance about the current state of HTTPS in Site Health. * Correct the existing copy to indicate that both the Site Address and the WordPress Address need to be changed to fully switch to HTTPS. * Link to the respective input fields via anchor links rather than to the overall General Settings screen. * Show different copy if the site is using HTTPS for the WordPress Address (for example to have only the administration panel in HTTPS), but not for the Site Address. * Inform the user about potential problems even when the site is already using HTTPS, for example if the SSL certificate was no longer valid. * Always rely on fresh information for determining HTTPS support issues in Site Health, and therefore change the `https_status` test to become asynchronous. * Rename the new private `wp_is_owned_html_output()` function to a more appropriate `wp_is_local_html_output()`. Props adamsilverstein, flixos90, johnjamesjacoby, timothyblynjacobs. See #47577. Built from https://develop.svn.wordpress.org/trunk@50072 git-svn-id: http://core.svn.wordpress.org/trunk@49773 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/class-wp-site-health.php | 108 ++++++++++++++---- wp-includes/https-detection.php | 45 ++++++-- .../class-wp-rest-site-health-controller.php | 31 +++++ wp-includes/version.php | 2 +- 4 files changed, 152 insertions(+), 34 deletions(-) diff --git a/wp-admin/includes/class-wp-site-health.php b/wp-admin/includes/class-wp-site-health.php index f08215659f..9d3a455f23 100644 --- a/wp-admin/includes/class-wp-site-health.php +++ b/wp-admin/includes/class-wp-site-health.php @@ -108,7 +108,7 @@ class WP_Site_Health { // Don't run https test on development environments. if ( $this->is_development_environment() ) { - unset( $tests['direct']['https_status'] ); + unset( $tests['async']['https_status'] ); } foreach ( $tests['direct'] as $test ) { @@ -1498,6 +1498,10 @@ class WP_Site_Health { * @return array The test results. */ public function get_test_https_status() { + // Enforce fresh HTTPS detection results. This is normally invoked by using cron, but for Site Health it should + // always rely on the latest results. + wp_update_https_detection_errors(); + $result = array( 'label' => __( 'Your website is using an active HTTPS connection' ), 'status' => 'good', @@ -1521,27 +1525,53 @@ class WP_Site_Health { ); if ( ! wp_is_using_https() ) { + // If the website is not using HTTPS, provide more information about whether it is supported and how it can + // be enabled. $result['status'] = 'critical'; $result['label'] = __( 'Your website does not use HTTPS' ); - if ( is_ssl() ) { - $result['description'] = sprintf( - '

%s

', - sprintf( - /* translators: %s: URL to General Settings screen. */ - __( 'You are accessing this website using HTTPS, but your WordPress Address is not set up to use HTTPS by default.' ), - esc_url( admin_url( 'options-general.php' ) ) - ) - ); + if ( wp_is_site_url_using_https() ) { + if ( is_ssl() ) { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: %s: URL to Settings > General > Site Address. */ + __( 'You are accessing this website using HTTPS, but your Site Address is not set up to use HTTPS by default.' ), + esc_url( admin_url( 'options-general.php' ) . '#home' ) + ) + ); + } else { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: %s: URL to Settings > General > Site Address. */ + __( 'Your Site Address is not set up to use HTTPS.' ), + esc_url( admin_url( 'options-general.php' ) . '#home' ) + ) + ); + } } else { - $result['description'] = sprintf( - '

%s

', - sprintf( - /* translators: %s: URL to General Settings screen. */ - __( 'Your WordPress Address is not set up to use HTTPS.' ), - esc_url( admin_url( 'options-general.php' ) ) - ) - ); + if ( is_ssl() ) { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: 1: URL to Settings > General > WordPress Address, 2: URL to Settings > General > Site Address. */ + __( 'You are accessing this website using HTTPS, but your WordPress Address and Site Address are not set up to use HTTPS by default.' ), + esc_url( admin_url( 'options-general.php' ) . '#siteurl' ), + esc_url( admin_url( 'options-general.php' ) . '#home' ) + ) + ); + } else { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: 1: URL to Settings > General > WordPress Address, 2: URL to Settings > General > Site Address. */ + __( 'Your WordPress Address and Site Address are not set up to use HTTPS.' ), + esc_url( admin_url( 'options-general.php' ) . '#siteurl' ), + esc_url( admin_url( 'options-general.php' ) . '#home' ) + ) + ); + } } if ( wp_is_https_supported() ) { @@ -1561,6 +1591,36 @@ class WP_Site_Health { __( 'Talk to your web host about supporting HTTPS for your website.' ) ); } + } elseif ( ! wp_is_https_supported() ) { + // If the website is using HTTPS, but HTTPS is actually not supported, inform the user about the potential + // problems. + $result['status'] = 'critical'; + $result['label'] = __( 'There are problems with the HTTPS connection of your website' ); + + $https_detection_errors = get_option( 'https_detection_errors' ); + if ( ! empty( $https_detection_errors['ssl_verification_failed'] ) ) { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: %s: URL to Settings > General > WordPress Address. */ + __( 'Your WordPress Address is set up to use HTTPS, but the SSL certificate appears to be invalid.' ), + esc_url( admin_url( 'options-general.php' ) . '#siteurl' ) + ) + ); + } else { + $result['description'] = sprintf( + '

%s

', + sprintf( + /* translators: %s: URL to Settings > General > WordPress Address. */ + __( 'Your WordPress Address is set up to use HTTPS, but your website appears to be unavailable when using an HTTPS connection.' ), + esc_url( admin_url( 'options-general.php' ) . '#siteurl' ) + ) + ); + } + $result['description'] .= sprintf( + '

%s

', + __( 'Talk to your web host about resolving this HTTPS issue for your website.' ) + ); } return $result; @@ -2200,10 +2260,6 @@ class WP_Site_Health { 'label' => __( 'MySQL utf8mb4 support' ), 'test' => 'utf8mb4_support', ), - 'https_status' => array( - 'label' => __( 'HTTPS status' ), - 'test' => 'https_status', - ), 'ssl_support' => array( 'label' => __( 'Secure communication' ), 'test' => 'ssl_support', @@ -2248,6 +2304,12 @@ class WP_Site_Health { 'has_rest' => true, 'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_loopback_requests' ), ), + 'https_status' => array( + 'label' => __( 'HTTPS status' ), + 'test' => rest_url( 'wp-site-health/v1/tests/https-status' ), + 'has_rest' => true, + 'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_https_status' ), + ), 'authorization_header' => array( 'label' => __( 'Authorization header' ), 'test' => rest_url( 'wp-site-health/v1/tests/authorization-header' ), @@ -2614,7 +2676,7 @@ class WP_Site_Health { // Don't run https test on development environments. if ( $this->is_development_environment() ) { - unset( $tests['direct']['https_status'] ); + unset( $tests['async']['https_status'] ); } foreach ( $tests['direct'] as $test ) { diff --git a/wp-includes/https-detection.php b/wp-includes/https-detection.php index 87c331bf24..f6562bbca0 100644 --- a/wp-includes/https-detection.php +++ b/wp-includes/https-detection.php @@ -9,28 +9,53 @@ /** * Checks whether the website is using HTTPS. * - * This is based on whether the home and site URL are using HTTPS. + * This is based on whether both the home and site URL are using HTTPS. * * @since 5.7.0 + * @see wp_is_home_url_using_https() + * @see wp_is_site_url_using_https() * * @return bool True if using HTTPS, false otherwise. */ function wp_is_using_https() { - if ( 'https' !== wp_parse_url( home_url(), PHP_URL_SCHEME ) ) { + if ( ! wp_is_home_url_using_https() ) { return false; } + return wp_is_site_url_using_https(); +} + +/** + * Checks whether the current site URL is using HTTPS. + * + * @since 5.7.0 + * @see home_url() + * + * @return bool True if using HTTPS, false otherwise. + */ +function wp_is_home_url_using_https() { + return 'https' === wp_parse_url( home_url(), PHP_URL_SCHEME ); +} + +/** + * Checks whether the current site's URL where WordPress is stored is using HTTPS. + * + * This checks the URL where WordPress application files (e.g. wp-blog-header.php or the wp-admin/ folder) are + * accessible. + * + * @since 5.7.0 + * @see site_url() + * + * @return bool True if using HTTPS, false otherwise. + */ +function wp_is_site_url_using_https() { // Use direct option access for 'siteurl' and manually run the 'site_url' - // filter because site_url() will adjust the scheme based on what the + // filter because `site_url()` will adjust the scheme based on what the // current request is using. /** This filter is documented in wp-includes/link-template.php */ $site_url = apply_filters( 'site_url', get_option( 'siteurl' ), '', null, null ); - if ( 'https' !== wp_parse_url( $site_url, PHP_URL_SCHEME ) ) { - return false; - } - - return true; + return 'https' === wp_parse_url( $site_url, PHP_URL_SCHEME ); } /** @@ -104,7 +129,7 @@ function wp_update_https_detection_errors() { if ( ! is_wp_error( $response ) ) { if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { $support_errors->add( 'bad_response_code', wp_remote_retrieve_response_message( $response ) ); - } elseif ( false === wp_is_owned_html_output( wp_remote_retrieve_body( $response ) ) ) { + } elseif ( false === wp_is_local_html_output( wp_remote_retrieve_body( $response ) ) ) { $support_errors->add( 'bad_response_source', __( 'It looks like the response did not come from this site.' ) ); } } @@ -159,7 +184,7 @@ function wp_cron_conditionally_prevent_sslverify( $request ) { * @param string $html Full HTML output string, e.g. from a HTTP response. * @return bool|null True/false for whether HTML was generated by this site, null if unable to determine. */ -function wp_is_owned_html_output( $html ) { +function wp_is_local_html_output( $html ) { // 1. Check if HTML includes the site's Really Simple Discovery link. if ( has_action( 'wp_head', 'rsd_link' ) ) { $pattern = esc_url( site_url( 'xmlrpc.php?rsd', 'rpc' ) ); // See rsd_link(). diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php index f790dadb04..d5d78255cf 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php @@ -85,6 +85,25 @@ class WP_REST_Site_Health_Controller extends WP_REST_Controller { ) ); + register_rest_route( + $this->namespace, + sprintf( + '/%s/%s', + $this->rest_base, + 'https-status' + ), + array( + array( + 'methods' => 'GET', + 'callback' => array( $this, 'test_https_status' ), + 'permission_callback' => function () { + return $this->validate_request_permission( 'https_status' ); + }, + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + register_rest_route( $this->namespace, sprintf( @@ -199,6 +218,18 @@ class WP_REST_Site_Health_Controller extends WP_REST_Controller { return $this->site_health->get_test_loopback_requests(); } + /** + * Checks that the site's frontend can be accessed over HTTPS. + * + * @since 5.7.0 + * + * @return array + */ + public function test_https_status() { + $this->load_admin_textdomain(); + return $this->site_health->get_test_https_status(); + } + /** * Checks that the authorization header is valid. * diff --git a/wp-includes/version.php b/wp-includes/version.php index c0257acbb1..ea6cf4736d 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.7-alpha-50071'; +$wp_version = '5.7-alpha-50072'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.