Cross Site XMLHttpRequest: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
No edit summary
Line 15: Line 15:
A goal of the implementation is that it should be reusable for other things than XMLHttpRequest. For example document.load should be able to do the same cross-site loads with the same restrictions. As should XSLT and XBL.
A goal of the implementation is that it should be reusable for other things than XMLHttpRequest. For example document.load should be able to do the same cross-site loads with the same restrictions. As should XSLT and XBL.


One tricky aspect is that the access control information can show up in two places. Both as HTTP headers, and as PIs inside XML files. Because of this it is suggested that the implementation has two parts:
To do this we'll set up an [http://lxr.mozilla.org/mozilla/source/netwerk/base/public/nsIStreamListener.idl nsIStreamListener] that sits between the normal nsIStreamListener and the [http://lxr.mozilla.org/mozilla/source/netwerk/base/public/nsIChannel.idl nsIChannel]. Once onStartRequest is called we check for access control headers. If the headers deny access we cancel the channel with a network error failure. If headers allow access we pass through all calls to the outer caller.


* An [http://lxr.mozilla.org/mozilla/source/netwerk/base/public/nsIRequestObserver.idl nsIRequestObserver] that checked the headers during onStartRequest. The observer is given the uri of the requesting page when created and if the page doesn't have access throws an exception thereby aborting the request. The observer act as a wrapper and forwards all calls to the existing nsIRequestObserver implementation (in the case of XMLHttpRequest the [http://lxr.mozilla.org/mozilla/source/content/base/src/nsXMLHttpRequest.cpp nsXMLHttpRequest] class).  
If headers don't say either way and the content type is an XML one (do we have a good way to determine that?) we set up a parser and ourselfs as sink. We'll then listen to notifications until the first start-element notification. At the same time we have to store all incoming data that is fed to the parser. If the access control PIs doesn't indicate that access should be granted we cancel the channel.


* A flag in [http://lxr.mozilla.org/mozilla/source/parser/htmlparser/src/nsExpatDriver.cpp nsExpatDriver] that makes the driver not send any notifications to the sink until either it has passed the matched an access control PI, or it has reached the first element without finding a matching PI. If access should be denied the sink is canceled and we need to signal back to the XMLHttpRequest that loading failed.
If access control is granted we forward calls to the outer caller and stream the buffered data to it.
 
Unfortunately gecko architecture forces us to create a document before we create an nsExpatDriver. In fact, I'm not sure there is a way to even know that an nsExpatDriver will be created other than by calling [http://lxr.mozilla.org/mozilla/source/content/base/public/nsIDocument.h#148 StartDocumentLoad] on the document. We could possibly set up a dummy document until we get word from the nsExpatDriver that the access checks were successful and if they are set up a real document and restart the load.
 
If we set up a dummy document it might be possible to do all this inside the nsIRequestObserver wrapper if we expand it into an nsIStreamListener. It would set up a dummy document and while streaming data into its streamlistener also copy all received data. If access control checks pass we could simply call into the outer listener and stream all copied data to it.
 
In fact we wouldn't even need to create a dummy document. We could simply set up a custom sink that just checks the incoming processing instructions until the first element. This could even be implemented by the same object as the nsIRequestObserver wrapper.


=== Issues ===
=== Issues ===


* If the check in onStartRequest fails, should we call the wrapping onStartRequest? The nsIRequestObserver interfaces says we must, but there is no way to indicate that the request is aborted. Or will canceling the request indicate that?
* We have to check that the code in onStartRequest in the original streamlistener doesn't do things that are too late to do once the delayed onStartRequest is called.  
 
* If we set up the dummy document inside the nsIRequestObserver wrapper, will it matter that we call onStartRequest on the outer listener not until we've passed the access checks? Does data disappear from the channel once data start coming in? Maybe we could call onStartRequest even though we don't know that the load is going to succeed? This would allow things like mimetype overrides to XML to work seamlessly I think. We'd have to watch out for this causing onreadystatechange requests to be fired.


== Security worries ==
== Security worries ==
Line 44: Line 36:


* We have to make sure to not put data in .responseText until we've passed access control checks even for XML files.
* We have to make sure to not put data in .responseText until we've passed access control checks even for XML files.
* We have to make it impossible to distinguish between a access-control-failed error and network errors such as 404s. Can the implementation "recancel" a canceled channel?
* Should we check for PIs even if HTTP headers has said that access is granted? It'll always be possible to circumvent those headers using .mimetypeOverride which'll make us not treat the doc as XML and thus we won't even look for PIs. Alternatively we could ignore the .mimetypeOverride when checking for PIs but that might be a problem with poorly configured servers (which is the whole reason for .mimetypeOverride)
Confirmed users
716

edits

Navigation menu