X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2FContentSecurityPolicy.php;h=62160461c9f0735fc7e01620a9c27a6b9504b22a;hp=66a3535ee85354ce24469ca46857cceecfe2f712;hb=0770f85a0a293e6c7af6f1d3d3a1dbd2d13c1e09;hpb=708f51c2c60b970549f463776faa38318fd11d21 diff --git a/includes/ContentSecurityPolicy.php b/includes/ContentSecurityPolicy.php index 66a3535ee8..62160461c9 100644 --- a/includes/ContentSecurityPolicy.php +++ b/includes/ContentSecurityPolicy.php @@ -27,8 +27,6 @@ class ContentSecurityPolicy { const REPORT_ONLY_MODE = 1; const FULL_MODE = 2; - /** Used for meta tag. Does not include report urls or nonce sources */ - const FULL_MODE_RESTRICTED = 3; /** @var string The nonce to use for inline scripts (from OutputPage) */ private $nonce; @@ -65,20 +63,6 @@ class ContentSecurityPolicy { } } - /** - * Return the meta header to use for after load restricted mode - * - * This should restrict browsers that don't support nonce-sources. - * Idea stolen from - * https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/ - * - * @param array $csp CSP configuration - * @return string Content for meta tag - */ - public function getMetaHeader( $csp ) { - return $this->makeCSPDirectives( $csp, self::FULL_MODE_RESTRICTED ); - } - /** * Send CSP headers based on wiki config * @@ -100,39 +84,13 @@ class ContentSecurityPolicy { $csp->sendCSPHeader( $cspConfig, self::FULL_MODE ); $csp->sendCSPHeader( $cspConfigReportOnly, self::REPORT_ONLY_MODE ); - // Include header which increases security level after initial load. - // This helps mitigate attacks on browsers not supporting CSP2. It also - // helps mitigate attacks due to the shared nonce that non-logged in users - // get due to varnish cache. - // Unclear if this is the best place to insert the meta tag, or if - // it should be in a RL module. I figure its best to do this as early - // as possible. - // FIXME: Needs testing to see if this actually works properly - $metaHeader = $csp->getMetaHeader( $cspConfig ); - if ( $metaHeader ) { - $context->getOutput()->addScript( - ResourceLoader::makeInlineScript( - $csp->makeMetaInsertScript( - $metaHeader - ), - $out->getCSPNonce() - ) - ); - } - } - - /** - * Makes javascript to insert a meta CSP header after page load - * - * @see https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/ - * @param string $metaContents content of meta tag - * @return string JS for including in page - */ - private function makeMetaInsertScript( $metaContents ) { - return "$('\\x3Cmeta http-equiv=\"Content-Security-Policy\"\\x3E')" . - '.attr("content",' . - Xml::encodeJsVar( $metaContents ) . - ').prependTo($("head"))'; + // This used to insert a tag here, per advice at + // https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/ + // The goal was to prevent nonce from working after the page hit onready, + // This would help in old browsers that didn't support nonces, and + // also assist for varnish-cached pages which repeat nonces. + // However, this is incompatible with how resource loader storage works + // via mw.domEval() so it was removed. } /** @@ -140,7 +98,6 @@ class ContentSecurityPolicy { * * @param int $reportOnly Either self::REPORT_ONLY_MODE or self::FULL_MODE * @return string Name of http header - * @throws UnexpectedValueException if you feed it self::FULL_MODE_RESTRICTED. */ private function getHeaderName( $reportOnly ) { if ( $reportOnly === self::REPORT_ONLY_MODE ) { @@ -155,7 +112,7 @@ class ContentSecurityPolicy { * Determine what CSP policies to set for this page * * @param array|bool $config Policy configuration (Either $wgCSPHeader or $wgCSPReportOnlyHeader) - * @param int $mode self::REPORT_ONLY_MODE, self::FULL_MODE or Self::FULL_MODE_RESTRICTED + * @param int $mode self::REPORT_ONLY_MODE, self::FULL_MODE * @return string Policy directives, or empty string for no policy. */ private function makeCSPDirectives( $policyConfig, $mode ) { @@ -171,7 +128,6 @@ class ContentSecurityPolicy { $additionalSelfUrls = $this->getAdditionalSelfUrls(); $additionalSelfUrlsScript = $this->getAdditionalSelfUrlsScript(); - $nonceSrc = "'nonce-" . $this->nonce . "'"; // If no default-src is sent at all, it // seems browsers (or at least some), interpret @@ -183,9 +139,10 @@ class ContentSecurityPolicy { $cssSrc = false; $imgSrc = false; $scriptSrc = [ "'unsafe-eval'", "'self'" ]; - if ( $mode !== self::FULL_MODE_RESTRICTED ) { - $scriptSrc[] = $nonceSrc; + if ( !isset( $policyConfig['useNonces'] ) || $policyConfig['useNonces'] ) { + $scriptSrc[] = "'nonce-" . $this->nonce . "'"; } + $scriptSrc = array_merge( $scriptSrc, $additionalSelfUrlsScript ); if ( isset( $policyConfig['script-src'] ) && is_array( $policyConfig['script-src'] ) @@ -197,7 +154,6 @@ class ContentSecurityPolicy { // Note: default on if unspecified. if ( ( !isset( $policyConfig['unsafeFallback'] ) || $policyConfig['unsafeFallback'] ) - && $mode !== self::FULL_MODE_RESTRICTED ) { // unsafe-inline should be ignored on browsers // that support 'nonce-foo' sources. @@ -244,10 +200,7 @@ class ContentSecurityPolicy { $cssSrc = array_merge( $defaultSrc, [ "'unsafe-inline'" ] ); } - if ( $mode === self::FULL_MODE_RESTRICTED ) { - // report-uri disallowed in tags. - $reportUri = false; - } elseif ( isset( $policyConfig['report-uri'] ) && $policyConfig['report-uri'] !== true ) { + if ( isset( $policyConfig['report-uri'] ) && $policyConfig['report-uri'] !== true ) { if ( $policyConfig['report-uri'] === false ) { $reportUri = false; } else { @@ -308,14 +261,11 @@ class ContentSecurityPolicy { /** * Get the default report uri. * - * @param int $mode self::*_MODE constant. Do not use with self::FULL_MODE_RESTRICTED + * @param int $mode self::*_MODE constant. * @return string The URI to send reports to. * @throws UnexpectedValueException if given invalid mode. */ private function getReportUri( $mode ) { - if ( $mode === self::FULL_MODE_RESTRICTED ) { - throw new UnexpectedValueException( $mode ); - } $apiArguments = [ 'action' => 'cspreport', 'format' => 'json' @@ -518,13 +468,28 @@ class ContentSecurityPolicy { } /** - * Is CSP currently enabled (i.e. Should we set nonce attribute) + * Should we set nonce attribute * * @param Config $config Configuration object * @return bool */ - public static function isEnabled( Config $config ) { - return $config->get( 'CSPHeader' ) !== false - || $config->get( 'CSPReportOnlyHeader' ) !== false; + public static function isNonceRequired( Config $config ) { + $configs = [ + $config->get( 'CSPHeader' ), + $config->get( 'CSPReportOnlyHeader' ) + ]; + foreach ( $configs as $headerConfig ) { + if ( + $headerConfig === true || + ( is_array( $headerConfig ) && + !isset( $headerConfig['useNonces'] ) ) || + ( is_array( $headerConfig ) && + isset( $headerConfig['useNonces'] ) && + $headerConfig['useNonces'] ) + ) { + return true; + } + } + return false; } }