Security, Site Health: Detect HTTPS support and encourage switching.

This changeset modifies the Site Health panel for HTTPS to provide more accurate recommendations based on whether the environment is already set up for HTTPS.

* Introduces `wp_is_using_https()` to check whether the site is configured to use HTTPS (via its Site Address and WordPress Address).
* Introduces `wp_is_https_supported()` to check whether the environment supports HTTPS. This relies on a cron job which periodically checks support using a loopback request.

Props Clorith, flixos90, miinasikk, westonruter.
Fixes #47577.

Built from https://develop.svn.wordpress.org/trunk@49904


git-svn-id: http://core.svn.wordpress.org/trunk@49603 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Felix Arntz 2020-12-23 19:13:04 +00:00
parent 9f4be0f698
commit c81db1f6e6
5 changed files with 216 additions and 15 deletions

View File

@ -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(
'<p>%s</p>',
sprintf(
@ -1536,17 +1533,34 @@ class WP_Site_Health {
esc_url( admin_url( 'options-general.php' ) )
)
);
} else {
$result['description'] = sprintf(
'<p>%s</p>',
sprintf(
/* translators: %s: URL to General Settings screen. */
__( 'Your <a href="%s">WordPress Address</a> 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(
'<p>%s</p>',
__( 'HTTPS is already supported for your website.' )
);
$result['actions'] = sprintf(
'<p><a href="%s">%s</a></p>',
esc_url( admin_url( 'options-general.php' ) ),
__( 'Update your site addresses' )
);
} else {
$result['description'] .= sprintf(
'<p>%s</p>',
__( '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;

View File

@ -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 );

View File

@ -0,0 +1,181 @@
<?php
/**
* HTTPS detection functions.
*
* @package WordPress
* @since 5.7.0
*/
/**
* Checks whether the website is using HTTPS.
*
* This is based on whether the home and site URL are using HTTPS.
*
* @since 5.7.0
*
* @return bool True if using HTTPS, false otherwise.
*/
function wp_is_using_https() {
if ( 'https' !== wp_parse_url( home_url(), PHP_URL_SCHEME ) ) {
return false;
}
// Use direct option access for 'siteurl' and manually run the 'site_url'
// 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;
}
/**
* Checks whether HTTPS is supported for the server and domain.
*
* @since 5.7.0
*
* @return bool True if HTTPS is supported, false otherwise.
*/
function wp_is_https_supported() {
$https_detection_errors = get_option( 'https_detection_errors' );
// If option has never been set by the Cron hook before, run it on-the-fly as fallback.
if ( false === $https_detection_errors ) {
wp_update_https_detection_errors();
$https_detection_errors = get_option( 'https_detection_errors' );
}
// If there are no detection errors, HTTPS is supported.
return empty( $https_detection_errors );
}
/**
* Runs a remote HTTPS request to detect whether HTTPS supported, and stores potential errors.
*
* This internal function is called by a regular Cron hook to ensure HTTPS support is detected and maintained.
*
* @since 5.7.0
* @access private
*/
function wp_update_https_detection_errors() {
$support_errors = new WP_Error();
$response = wp_remote_request(
home_url( '/', 'https' ),
array(
'headers' => 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;
}

View File

@ -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.

View File

@ -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';