WordPress.org

Make WordPress Core

Ticket #47577: 47577.2.diff

File 47577.2.diff, 10.6 KB (added by miinasikk, 3 months ago)

Includes adding Content-Security-Policy header with filter to opt out for Content-Security-Policy-Report-Only

  • src/wp-admin/includes/class-wp-site-health.php

     
    12891289         */
    12901290        public function get_test_https_status() {
    12911291                $result = array(
    1292                         'label'       => __( 'Your website is using an active HTTPS connection.' ),
     1292                        'label'       => __( 'Your website is using an active HTTPS connection' ),
    12931293                        'status'      => 'good',
    12941294                        'badge'       => array(
    12951295                                'label' => __( 'Security' ),
     
    13001300                                __( 'An HTTPS connection is needed for many features on the web today, it also gains the trust of your visitors by helping to protecting their online privacy.' )
    13011301                        ),
    13021302                        'actions'     => sprintf(
    1303                                 '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
     1303                                '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p><p>%s</p>',
    13041304                                /* translators: Documentation explaining HTTPS and why it should be used. */
    13051305                                esc_url( __( 'https://wordpress.org/support/article/why-should-i-use-https/' ) ),
    13061306                                __( 'Learn more about why you should use HTTPS' ),
    13071307                                /* translators: accessibility text */
    1308                                 __( '(opens in a new tab)' )
     1308                                __( '(opens in a new tab)' ),
     1309                                sprintf(
     1310                                        '%s <a target="_blank" href="%s">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>',
     1311                                        __( 'Content-Security-Policy HTTP Header is set to <code>upgrade-insecure-requests</code> when HTTPS will be turned on.' ),
     1312                                        /* translators: Documentation explaining Update Insecure Requests CSP. */
     1313                                        esc_url( __( 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests' ) ),
     1314                                        __( 'Learn more about what this means.' ),
     1315                                        /* translators: accessibility text */
     1316                                        __( '(opens in a new tab)' )
     1317                                )
    13091318                        ),
    13101319                        'test'        => 'https_status',
    13111320                );
    13121321
    1313                 if ( is_ssl() ) {
    1314                         $wp_url   = get_bloginfo( 'wpurl' );
    1315                         $site_url = get_bloginfo( 'url' );
     1322                if ( ! wp_is_using_https() ) {
     1323                        $result['status'] = 'recommended';
    13161324
    1317                         if ( 'https' !== substr( $wp_url, 0, 5 ) || 'https' !== substr( $site_url, 0, 5 ) ) {
    1318                                 $result['status'] = 'recommended';
     1325                        $result['label'] = __( 'Your website does not use HTTPS' );
    13191326
    1320                                 $result['label'] = __( 'Only parts of your site are using HTTPS' );
    1321 
     1327                        if ( is_ssl() ) {
    13221328                                $result['description'] = sprintf(
    13231329                                        '<p>%s</p>',
     1330                                        __( 'You are accessing your site with HTTPS, but your site is not correctly configured to use HTTPS.' )
     1331                                );
     1332                        } else {
     1333                                $result['description'] = sprintf(
     1334                                        '<p>%s</p>',
     1335                                        __( 'Your site is not correctly configured to use HTTPS.' )
     1336                                );
     1337                        }
     1338
     1339                        if ( wp_is_https_supported() ) {
     1340                                $result['description'] .= sprintf(
     1341                                        '<p>%s</p>',
    13241342                                        sprintf(
    13251343                                                /* translators: %s: URL to Settings > General to change options. */
    1326                                                 __( 'You are accessing this website using HTTPS, but your <a href="%s">WordPress Address</a> is not set up to use HTTPS by default.' ),
     1344                                                __( 'HTTPS is supported for your website, so you should update your <a href="%s">WordPress Address</a> to use HTTPS by default.' ),
    13271345                                                esc_url( admin_url( 'options-general.php' ) )
    13281346                                        )
    13291347                                );
    1330 
    1331                                 $result['actions'] .= sprintf(
    1332                                         '<p><a href="%s">%s</a></p>',
    1333                                         esc_url( admin_url( 'options-general.php' ) ),
    1334                                         __( 'Update your site addresses' )
     1348                        } else {
     1349                                $result['description'] .= sprintf(
     1350                                        '<p>%s</p>',
     1351                                        __( 'Talk to your web host about supporting HTTPS for your website.' )
    13351352                                );
    13361353                        }
    1337                 } else {
    1338                         $result['status'] = 'recommended';
    1339 
    1340                         $result['label'] = __( 'Your site does not use HTTPS' );
    13411354                }
    13421355
    13431356                return $result;
  • src/wp-includes/default-filters.php

     
    332332        add_action( 'init', 'wp_cron' );
    333333}
    334334
     335// HTTPS detection
     336add_action( 'init', 'wp_cron_schedule_https_detection' );
     337add_action( 'wp_https_detection', 'wp_update_https_detection_errors' );
     338add_filter( 'cron_request', 'wp_cron_conditionally_prevent_sslverify', 9999 );
     339add_filter( 'the_content', 'wp_replace_insecure_self_links' );
     340add_filter( 'widget_text', 'wp_replace_insecure_self_links' );
     341add_filter( 'wp_headers', 'wp_add_upgrade_insecure_requests_header' );
     342
    335343// 2 Actions 2 Furious
    336344add_action( 'do_feed_rdf', 'do_feed_rdf', 10, 1 );
    337345add_action( 'do_feed_rss', 'do_feed_rss', 10, 1 );
  • src/wp-includes/https-detection.php

     
     1<?php
     2/**
     3 * HTTPS detection functions.
     4 *
     5 * @package WordPress
     6 * @since 5.4.0
     7 */
     8
     9/**
     10 * Checks whether the website is using HTTPS.
     11 *
     12 * This is based on whether the home and site URL are using HTTPS.
     13 *
     14 * @since 5.4.0
     15 *
     16 * @return bool True if using HTTPS, false otherwise.
     17 */
     18function wp_is_using_https() {
     19        if ( 'https' !== wp_parse_url( home_url(), PHP_URL_SCHEME ) ) {
     20                return false;
     21        }
     22
     23        if ( 'https' !== wp_parse_url( site_url(), PHP_URL_SCHEME ) ) {
     24                return false;
     25        }
     26
     27        return true;
     28}
     29
     30/**
     31 * Checks whether HTTPS is supported for the server and domain.
     32 *
     33 * @since 5.4.0
     34 *
     35 * @return bool True if HTTPS is supported, false otherwise.
     36 */
     37function wp_is_https_supported() {
     38        $https_detection_errors = get_option( 'https_detection_errors' );
     39
     40        // If option has never been set by the Cron hook before, run it on-the-fly as fallback.
     41        if ( false === $https_detection_errors ) {
     42                wp_update_https_detection_errors();
     43
     44                $https_detection_errors = get_option( 'https_detection_errors' );
     45        }
     46
     47        // If there are no detection errors, HTTPS is supported.
     48        return empty( $https_detection_errors );
     49}
     50
     51/**
     52 * Runs a remote HTTPS request to detect whether HTTPS supported, and stores potential errors.
     53 *
     54 * This internal function is called by a regular Cron hook to ensure HTTPS support is detected and maintained.
     55 *
     56 * @since 5.4.0
     57 * @access private
     58 */
     59function wp_update_https_detection_errors() {
     60        $support_errors = new WP_Error();
     61
     62        $response = wp_remote_request(
     63                home_url( '/', 'https' ),
     64                array(
     65                        'headers'   => array(
     66                                'Cache-Control' => 'no-cache',
     67                        ),
     68                        'sslverify' => true,
     69                )
     70        );
     71
     72        if ( is_wp_error( $response ) ) {
     73                $unverified_response = wp_remote_request(
     74                        home_url( '/', 'https' ),
     75                        array(
     76                                'headers'   => array(
     77                                        'Cache-Control' => 'no-cache',
     78                                ),
     79                                'sslverify' => false,
     80                        )
     81                );
     82
     83                if ( is_wp_error( $unverified_response ) ) {
     84                        $support_errors->add(
     85                                $unverified_response->get_error_code(),
     86                                $unverified_response->get_error_message()
     87                        );
     88                } else {
     89                        $support_errors->add(
     90                                'ssl_verification_failed',
     91                                $response->get_error_message()
     92                        );
     93                }
     94
     95                $response = $unverified_response;
     96        }
     97
     98        if ( ! is_wp_error( $response ) && 200 !== wp_remote_retrieve_response_code( $response ) ) {
     99                $support_errors->add( 'response_error', wp_remote_retrieve_response_message( $response ) );
     100        }
     101
     102        update_option( 'https_detection_errors', $support_errors->errors );
     103}
     104
     105/**
     106 * Schedules the Cron hook for detecting HTTPS support.
     107 *
     108 * @since 5.4.0
     109 * @access private
     110 */
     111function wp_cron_schedule_https_detection() {
     112        if ( ! wp_next_scheduled( 'wp_https_detection' ) ) {
     113                wp_schedule_event( time(), 'twicedaily', 'wp_https_detection' );
     114        }
     115}
     116
     117/**
     118 * Disables SSL verification if the 'cron_request' arguments include an HTTPS URL.
     119 *
     120 * This prevents an issue if HTTPS breaks, where there would be a failed attempt to verify HTTPS.
     121 *
     122 * @since 5.4.0
     123 * @access private
     124 *
     125 * @param array $request The Cron request arguments.
     126 * @return array $request The filtered Cron request arguments.
     127 */
     128function wp_cron_conditionally_prevent_sslverify( $request ) {
     129        if ( 'https' === wp_parse_url( $request['url'], PHP_URL_SCHEME ) ) {
     130                $request['args']['sslverify'] = false;
     131        }
     132        return $request;
     133}
     134
     135/**
     136 * Replaces insecure HTTP URLs with HTTPS URLs in the given content.
     137 *
     138 * @since 5.4.0
     139 *
     140 * @param string $content Content to replace insecure URLs in.
     141 * @return string Modified content.
     142 */
     143function wp_replace_insecure_self_links( $content ) {
     144        if ( ! wp_is_using_https() ) {
     145                return $content;
     146        }
     147
     148        return str_replace( home_url( '', 'http' ), home_url( '', 'https' ), $content );
     149}
     150
     151/**
     152 * Adds Upgrade-Insecure-Requests header.
     153 * Allows filtering to use Content-Security-Policy-Report-Only instead.
     154 *
     155 * Upgrades insecure requests, such as scripts and images, to HTTPS.
     156 * There's no fallback if the upgraded HTTPS request fails.
     157 * Navigational third-party resources are not upgraded.
     158 *
     159 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade-Insecure-Requests
     160 * @param array $headers The response headers.
     161 * @return array $headers The filtered response headers.
     162 */
     163function wp_add_upgrade_insecure_requests_header( $headers ) {
     164    if ( ! wp_is_using_https() ) {
     165        return $headers;
     166    }
     167
     168    /**
     169     * Filters the report URI for Content-Security-Policy-Report-Only header. If this is set then only CSPRO header is added.
     170     *
     171     * @since 5.4.0
     172     *
     173     * @param string $report_only_endpoint Content-Security-Policy-Report-Only report URI. Default is null.
     174     */
     175    $report_only_endpoint = apply_filters( 'wp_csp_report_only_endpoint', null );
     176
     177    if ( $report_only_endpoint && is_string( $report_only_endpoint ) ) {
     178        $headers['Content-Security-Policy-Report-Only'] = "default-src https:; report-uri $report_only_endpoint";
     179    } else {
     180        $headers['Content-Security-Policy'] = 'upgrade-insecure-requests';
     181    }
     182    return $headers;
     183}
  • src/wp-settings.php

     
    163163require( ABSPATH . WPINC . '/theme.php' );
    164164require( ABSPATH . WPINC . '/class-wp-theme.php' );
    165165require( ABSPATH . WPINC . '/template.php' );
     166require( ABSPATH . WPINC . '/https-detection.php' );
    166167require( ABSPATH . WPINC . '/class-wp-user-request.php' );
    167168require( ABSPATH . WPINC . '/user.php' );
    168169require( ABSPATH . WPINC . '/class-wp-user-query.php' );