From: Brad Jorsch Date: Fri, 8 Apr 2016 17:05:25 +0000 (-0400) Subject: API: Allow anonymous CORS from anywhere, when specifically requested X-Git-Tag: 1.31.0-rc.0~6408^2 X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/membres/fiche.php?a=commitdiff_plain;h=d83e655b8e033019b445de948bee00bec5fdb948;p=lhc%2Fweb%2Fwiklou.git API: Allow anonymous CORS from anywhere, when specifically requested This allows any external site to do with CORS what they can already do with jsonp: submit a request that will be processed as if logged out. This is done by accepting '*' as a value for the existing 'origin' URL parameter that is currently required in order to do any CORS requests against MediaWiki. The response to such a request will specifically include "Access-Control-Allow-Credentials: false" to instruct the browser not to send cookies or other authentication data, and further the API will apply all the same restrictions (forcing an anonymous user and forbidding certain actions such as token fetch) that it currently does for jsonp requests. Bug: T62835 Change-Id: I30e359fb23f0511242dfb4bff68718668947aaf5 --- diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index ce9587f399..d9bbbcb8f1 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -265,6 +265,12 @@ class ApiMain extends ApiBase { return true; } + // Anonymous CORS + if ( $request->getVal( 'origin' ) === '*' ) { + $this->lacksSameOriginSecurity = true; + return true; + } + // Header to be used from XMLHTTPRequest when the request might // otherwise be used for XSS. if ( $request->getHeader( 'Treat-as-Untrusted' ) !== false ) { @@ -611,31 +617,49 @@ class ApiMain extends ApiBase { $request = $this->getRequest(); $response = $request->response(); - // Origin: header is a space-separated list of origins, check all of them - $originHeader = $request->getHeader( 'Origin' ); - if ( $originHeader === false ) { - $origins = []; + $matchOrigin = false; + $allowTiming = false; + $varyOrigin = true; + + if ( $originParam === '*' ) { + // Request for anonymous CORS + $matchOrigin = true; + $allowOrigin = '*'; + $allowCredentials = 'false'; + $varyOrigin = false; // No need to vary } else { - $originHeader = trim( $originHeader ); - $origins = preg_split( '/\s+/', $originHeader ); - } + // Non-anonymous CORS, check we allow the domain - if ( !in_array( $originParam, $origins ) ) { - // origin parameter set but incorrect - // Send a 403 response - $response->statusHeader( 403 ); - $response->header( 'Cache-Control: no-cache' ); - echo "'origin' parameter does not match Origin header\n"; + // Origin: header is a space-separated list of origins, check all of them + $originHeader = $request->getHeader( 'Origin' ); + if ( $originHeader === false ) { + $origins = []; + } else { + $originHeader = trim( $originHeader ); + $origins = preg_split( '/\s+/', $originHeader ); + } - return false; - } + if ( !in_array( $originParam, $origins ) ) { + // origin parameter set but incorrect + // Send a 403 response + $response->statusHeader( 403 ); + $response->header( 'Cache-Control: no-cache' ); + echo "'origin' parameter does not match Origin header\n"; - $config = $this->getConfig(); - $matchOrigin = count( $origins ) === 1 && self::matchOrigin( - $originParam, - $config->get( 'CrossSiteAJAXdomains' ), - $config->get( 'CrossSiteAJAXdomainExceptions' ) - ); + return false; + } + + $config = $this->getConfig(); + $matchOrigin = count( $origins ) === 1 && self::matchOrigin( + $originParam, + $config->get( 'CrossSiteAJAXdomains' ), + $config->get( 'CrossSiteAJAXdomainExceptions' ) + ); + + $allowOrigin = $originHeader; + $allowCredentials = 'true'; + $allowTiming = $originHeader; + } if ( $matchOrigin ) { $requestedMethod = $request->getHeader( 'Access-Control-Request-Method' ); @@ -659,10 +683,12 @@ class ApiMain extends ApiBase { $response->header( 'Access-Control-Allow-Methods: POST, GET' ); } - $response->header( "Access-Control-Allow-Origin: $originHeader" ); - $response->header( 'Access-Control-Allow-Credentials: true' ); + $response->header( "Access-Control-Allow-Origin: $allowOrigin" ); + $response->header( "Access-Control-Allow-Credentials: $allowCredentials" ); // http://www.w3.org/TR/resource-timing/#timing-allow-origin - $response->header( "Timing-Allow-Origin: $originHeader" ); + if ( $allowTiming !== false ) { + $response->header( "Timing-Allow-Origin: $allowTiming" ); + } if ( !$preflight ) { $response->header( @@ -671,7 +697,10 @@ class ApiMain extends ApiBase { } } - $this->getOutput()->addVaryHeader( 'Origin' ); + if ( $varyOrigin ) { + $this->getOutput()->addVaryHeader( 'Origin' ); + } + return true; }