diff --git a/wp-admin/includes/class-wp-site-health.php b/wp-admin/includes/class-wp-site-health.php index fcc1ed7247..711fe19dc1 100644 --- a/wp-admin/includes/class-wp-site-health.php +++ b/wp-admin/includes/class-wp-site-health.php @@ -1493,12 +1493,13 @@ class WP_Site_Health { * enabled, but only if you visit the right site address. * * @since 5.2.0 + * @since 5.7.0 Updated to rely on {@see wp_is_using_https()} and {@see wp_is_https_supported()}. * * @return array The test results. */ public function get_test_https_status() { $result = array( - 'label' => __( 'Your website is using an active HTTPS connection.' ), + 'label' => __( 'Your website is using an active HTTPS connection' ), 'status' => 'good', 'badge' => array( 'label' => __( 'Security' ), @@ -1519,15 +1520,11 @@ class WP_Site_Health { 'test' => 'https_status', ); - if ( is_ssl() ) { - $wp_url = get_bloginfo( 'wpurl' ); - $site_url = get_bloginfo( 'url' ); - - if ( 'https' !== substr( $wp_url, 0, 5 ) || 'https' !== substr( $site_url, 0, 5 ) ) { - $result['status'] = 'recommended'; - - $result['label'] = __( 'Only parts of your site are using HTTPS' ); + if ( ! wp_is_using_https() ) { + $result['status'] = 'critical'; + $result['label'] = __( 'Your website does not use HTTPS' ); + if ( is_ssl() ) { $result['description'] = sprintf( '

%s

', sprintf( @@ -1536,17 +1533,34 @@ class WP_Site_Health { esc_url( admin_url( 'options-general.php' ) ) ) ); + } 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' ) ) + ) + ); + } - $result['actions'] .= sprintf( + if ( wp_is_https_supported() ) { + $result['description'] .= sprintf( + '

%s

', + __( 'HTTPS is already supported for your website.' ) + ); + + $result['actions'] = sprintf( '

%s

', esc_url( admin_url( 'options-general.php' ) ), __( 'Update your site addresses' ) ); + } else { + $result['description'] .= sprintf( + '

%s

', + __( 'Talk to your web host about supporting HTTPS for your website.' ) + ); } - } else { - $result['status'] = 'recommended'; - - $result['label'] = __( 'Your site does not use HTTPS' ); } return $result; diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php index 96ef7d82eb..334a12a929 100644 --- a/wp-includes/default-filters.php +++ b/wp-includes/default-filters.php @@ -337,6 +337,11 @@ if ( ! defined( 'DOING_CRON' ) ) { add_action( 'init', 'wp_cron' ); } +// HTTPS detection. +add_action( 'init', 'wp_schedule_https_detection' ); +add_action( 'wp_https_detection', 'wp_update_https_detection_errors' ); +add_filter( 'cron_request', 'wp_cron_conditionally_prevent_sslverify', 9999 ); + // 2 Actions 2 Furious. add_action( 'do_feed_rdf', 'do_feed_rdf', 10, 0 ); add_action( 'do_feed_rss', 'do_feed_rss', 10, 0 ); diff --git a/wp-includes/https-detection.php b/wp-includes/https-detection.php new file mode 100644 index 0000000000..895b3a8516 --- /dev/null +++ b/wp-includes/https-detection.php @@ -0,0 +1,181 @@ + array( + 'Cache-Control' => 'no-cache', + ), + 'sslverify' => true, + ) + ); + + if ( is_wp_error( $response ) ) { + $unverified_response = wp_remote_request( + home_url( '/', 'https' ), + array( + 'headers' => array( + 'Cache-Control' => 'no-cache', + ), + 'sslverify' => false, + ) + ); + + if ( is_wp_error( $unverified_response ) ) { + $support_errors->add( + $unverified_response->get_error_code(), + $unverified_response->get_error_message() + ); + } else { + $support_errors->add( + 'ssl_verification_failed', + $response->get_error_message() + ); + } + + $response = $unverified_response; + } + + 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 ) ) ) { + $support_errors->add( 'bad_response_source', __( 'It looks like the response did not come from this site.' ) ); + } + } + + update_option( 'https_detection_errors', $support_errors->errors ); +} + +/** + * Schedules the Cron hook for detecting HTTPS support. + * + * @since 5.7.0 + * @access private + */ +function wp_schedule_https_detection() { + if ( ! wp_next_scheduled( 'wp_https_detection' ) ) { + wp_schedule_event( time(), 'twicedaily', 'wp_https_detection' ); + } +} + +/** + * Disables SSL verification if the 'cron_request' arguments include an HTTPS URL. + * + * This prevents an issue if HTTPS breaks, where there would be a failed attempt to verify HTTPS. + * + * @since 5.7.0 + * @access private + * + * @param array $request The Cron request arguments. + * @return array $request The filtered Cron request arguments. + */ +function wp_cron_conditionally_prevent_sslverify( $request ) { + if ( 'https' === wp_parse_url( $request['url'], PHP_URL_SCHEME ) ) { + $request['args']['sslverify'] = false; + } + return $request; +} + +/** + * Checks whether a given HTML string is likely an output from this WordPress site. + * + * This function attempts to check for various common WordPress patterns whether they are included in the HTML string. + * Since any of these actions may be disabled through third-party code, this function may also return null to indicate + * that it was not possible to determine ownership. + * + * @since 5.7.0 + * @access private + * + * @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 ) { + // 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(). + return false !== strpos( $html, $pattern ); + } + + // 2. Check if HTML includes the site's Windows Live Writer manifest link. + if ( has_action( 'wp_head', 'wlwmanifest_link' ) ) { + // Try both HTTPS and HTTP since the URL depends on context. + $pattern = preg_replace( '#^https?:(?=//)#', '', includes_url( 'wlwmanifest.xml' ) ); // See wlwmanifest_link(). + return false !== strpos( $html, $pattern ); + } + + // 3. Check if HTML includes the site's REST API link. + if ( has_action( 'wp_head', 'rest_output_link_wp_head' ) ) { + // Try both HTTPS and HTTP since the URL depends on context. + $pattern = esc_url( preg_replace( '#^https?:(?=//)#', '', get_rest_url() ) ); // See rest_output_link_wp_head(). + return false !== strpos( $html, $pattern ); + } + + // Otherwise the result cannot be determined. + return null; +} diff --git a/wp-includes/version.php b/wp-includes/version.php index 0eb9a6a6ad..d52e7a1115 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.7-alpha-49903'; +$wp_version = '5.7-alpha-49904'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. diff --git a/wp-settings.php b/wp-settings.php index 71868ecc89..63c59c6cef 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -171,6 +171,7 @@ require ABSPATH . WPINC . '/class-wp-date-query.php'; require ABSPATH . WPINC . '/theme.php'; require ABSPATH . WPINC . '/class-wp-theme.php'; require ABSPATH . WPINC . '/template.php'; +require ABSPATH . WPINC . '/https-detection.php'; require ABSPATH . WPINC . '/class-wp-user-request.php'; require ABSPATH . WPINC . '/user.php'; require ABSPATH . WPINC . '/class-wp-user-query.php';