Page MenuHomePhabricator

Cross-domain AJAX request support
Closed, ResolvedPublic

Description

The W3C working draft on cross-origin resource sharing ( http://www.w3.org/TR/cors/ ) specifies how browsers can send AJAX requests which normally wouldn't be allowed by same-origin rules. Specifically, the repsonse of the server must contain an Access-Control-Allow-Origin header with the list of domains which are allowed to send requests. At least Firefox 3.5 and Explorer 8 already support this. Support for such a setting in the MediaWiki API could allow user scripts to perform functions that affect multiple sites (such as moving images to Commons, or combining watchlists from multiple sites), toolserver scripts to access the wikis with a sound security model (the script can instruct the browser to do stuff on a wiki without asking for passwords or session cookies), and 3rd party MediaWiki installations to have a public read/write API suitable for widgets and mashups.

The only possible security problem I can think of would be if a MediaWiki installation would allow both user scripts and page edit requests from untrusted domains. You could either disallow remote API calls to write .js pages, or leave this to be the responsibility of the one configuring the site (ie. do not enable $wgAllowUserJs / $wgUseSiteJs when API requests from untrusted domains are enabled).


Version: unspecified
Severity: enhancement

Details

Reference
bz19907

Event Timeline

bzimport raised the priority of this task from to Medium.Nov 21 2014, 10:42 PM
bzimport set Reference to bz19907.

Could you be more clear as to what exactly should be changed?

There is some more discussion about the Access-Control-Allow-Origin header here:

https://developer.mozilla.org/En/HTTP_Access_Control

I imagine this would be controlled by a configuration variable, something like $wgCrossSiteAJAXdomains containing an array of domains allowed to make cross-site AJAX requests to the API by the Access-Control-Allow-Origin header.

https://developer.mozilla.org/En/Server-Side_Access_Control gives some PHP examples of implementations.

Created attachment 6407
Patch against r54044

Patch to add support for Access-Control-Allow-Origin in api.php
This also uses the Access-Control-Allow-Credentials header, which I believe is required for anything that requires cookies.

I can't really test this on a localhost test wiki, so would appreciate some review.

Should we only allow this for the API, or should we do this for any entry point?

For reference: http://dev.w3.org/2006/waf/access-control/

Attached:

Another place where this might be useful, if not now then in future browser versions, is likely cross wikimedia project login for people with strict security settings for cookies.

(In reply to comment #4)

Created an attachment (id=6407) [details]
Patch against r54044

Patch to add support for Access-Control-Allow-Origin in api.php
This also uses the Access-Control-Allow-Credentials header, which I believe is
required for anything that requires cookies.

A break; statement should be added after the second header() call in the foreach(). Other than that, this patch looks good.

"Note that in the case of credentialed requests, the Access-Control-Allow-Origin: header must not have a wildcard value of "*". It must mention a valid origin domain."

This means that even if $wgCrossSiteAJAXdomains is set to '*', we have to output the origin domain instead of '*' in the header.

It would be great if the toolserver could be whitelisted on Wikipedia. I am planning a userscript/gadget that would use Diberri's Template Filler http://toolserver.org/~diberri/cgi-bin/templatefiller. Due to cross-site scripting limitations this is currently not possible.

mike.lifeguard+bugs wrote:

(In reply to comment #9)

It would be great if the toolserver could be whitelisted on Wikipedia. I am
planning a userscript/gadget that would use Diberri's Template Filler
http://toolserver.org/~diberri/cgi-bin/templatefiller. Due to cross-site
scripting limitations this is currently not possible.

That should be made as a site request after this feature goes live.

I understand from https://bugzilla.wikimedia.org/show_bug.cgi?id=28700#c4 that CORS, though implemented, is not configured. Can I ask what the hold-up here is? Was the code accepted simply to be available to other Mediawiki installations, or is it planned for the Wikimedia sites after some condition is met?

(In reply to comment #11)

I understand from https://bugzilla.wikimedia.org/show_bug.cgi?id=28700#c4 that
CORS, though implemented, is not configured. Can I ask what the hold-up here
is? Was the code accepted simply to be available to other Mediawiki
installations, or is it planned for the Wikimedia sites after some condition is
met?

It was planned to be enabled at WMF, but no one ever got to it. I'll probably set it up tomorrow.

Roan, is your comment 12 something you will be able to get to, or should I start a new bug for it?

(In reply to comment #13)

Roan, is your comment 12 something you will be able to get to, or should I
start a new bug for it?

I'm sorry for the delay, thank you for reminding me. There were serious issues with the implementation, mostly regarding caching. Those issues would have to be addressed in MediaWiki before CORS can be enabled for the API.

The root of the caching issue is comment #7: the Access-Control-Allow-Origin header we send back contains the origin domain of the foreign request, and if that header gets cached, we're screwed.

Fortunately, there's a way around this if I'm reading this correctly. For non-credentialed requests, we can send Allow-Origin: * . For credentialed requests, we have to send Allow-Origin: $ORIGIN and Allow-Credentials: true , but we can just set a no caching header for those, as credentialed requests aren't supposed to be cached anyway.

The catch here is in the definition of "credentialed request". We don't want this to mean "any request that passes a cookie" because that would be excessive; rather, we want this to mean "any request that would actually use the cookie information", i.e. requests with user-specific or privileged things. Maybe we can tie this to the cache mode?

This is interesting stuff and I have new inspiration for it now :) , so I'll poke at it today or tomorrow.

Normally, only credentialed requests pass cookies. When a client tries to send a credentialed requests, the browser will send a preflight request first (an OPTIONS request with some CORS-specific headers like Origin), and only sends the request (with cookies) if the Allow-Origin and Allow-Credentials response headers are OK. Uncredentialed requests are transmitted without cookies ( http://www.w3.org/TR/cors/#make-a-request-steps - "...and include user credentials if the credentials flag is true". Though apparently not all implementations honor this: http://stackoverflow.com/questions/6096919/android-credentials-always-sent-with-cors-requests ).

(In reply to comment #14)

The catch here is in the definition of "credentialed request". We don't want
this to mean "any request that passes a cookie" because that would be
excessive; rather, we want this to mean "any request that would actually use
the cookie information", i.e. requests with user-specific or privileged things.

Makes sense. Although it might get a bit complicated when put into perspective of the CORS point of view.

So from the browser perspective, for requests that are NOT "credentialed" the browser will not send cookies that it has stored for that domain/path, meaning that the API will not receive them and user is treated as logged-out user.

So it looks like this looks good for us on both sides (we can't cache user-specific stuff, user-specific stuff should be in a "credentialed request", CORS specification / browsers make it impossible for non "credentialed requests" to be user specific anyway.

by the way, before we get things mixed up:

  • MediaWiki supports this already and has for over a year now. So I'm marking this bug fixed.
  • Then, to have it enabled for Wikimedia sites is bug 20814
  • That wasn't done yet due to cache restrictions, so the second request is to make this even better and be compatible in a special with with WMF's cache infrastructure. That feature request was logged a while ago under bug 30881.