Recently I have been seeing forum questions on silverlight.net, where people are having trouble with the Silverlight’s treatment of cross-domain calls. I wanted go over the cross-domain restriction and share two tips that might help when using cross-domain web services in Visual Studio.
What is this cross-domain thing?
Modern web browsers have restrictions in place to guard against Cross-Site Scripting (XSS) and Cross-Site Request Forgery (XSRF) vulnerabilities. Exploits of these vulnerabilities can (a) send confidential user data to malicious third-parties or (b) access websites on behalf of the user without the user ever knowing. The example we like to give goes something like this: Sally has an account with Bank XYZ and she uses online banking at http://bankxyz.com/. The http://bankxyz.com/ website stores Sally’s credentials in a cookie on her machine as a convenience, so Sally doesn’t have to authenticate every time. Tom finds out that Sally uses Bank XYZ, and creates http://maliciousgame.com/, which he sends to Sally. When Sally opens up http://maliciousgame.com/ in her browser, a script hosted at the site secretly accesses http://bankxyz.com/ and transfers money to Tom’s account, without Sally finding out. Sally is already authenticated at http://bankxyz.com/, so the script can carry out the transaction without Sally’s permission. The domain names above are meant to be fictitious… I’m happy to change them if a real “Bank XYZ” or a legitimate “malicious game” come around 🙂
As a browser plug-in Silverlight also needs to be mindful of these vulnerabilities. We maintain consistent behavior with the browser when it comes to cross-domain calls, and we enforce the site-of-origin restriction across all Silverlight HTTP calls. This applies to WebClient, HttpWebRequest, and the calls made by web service proxies (which use HttpWebRequest internally).
Now this is a bummer for mashups, which are all about pulling data from different web services (Flickr, Yahoo!, Ebay, Facebook, etc) hosted on different domains. So similarly to Adobe Flash, we provide a mechanism to allow cross-domain calls, as described in our docs. By placing a clientaccesspolicy.xml or crossdomain.xml file at the root of its domain, a web service owner declares “the service is safe to be called by any and all Silverlight controls, and no matter who makes the call, the service won’t release or mess up the user’s private data”. For example, Flickr just serves up images, so it is safe and allows cross-domain calls by having a http://api.flickr.com/crossdomain.xml file. A bank’s website should probably not have a cross-domain policy file at its root.
There are at least two places when working with web services in Silverlight where you might hit the cross-domain restriction without even knowing it. For the following to make sense, you must have Visual Studio 2008, the Silverlight 2 Beta 1 SDK, and the Silverlight 2 Beta 1 VS tools installed on your machine.
Don’t try network calls from TestPage.html on your hard drive
When you create a Silverlight project, Visual Studio gives you the choice of hosting the control (i) in the built-in Visual Studio web server or (ii) on your local file system.
The second choice is good if you are just playing with graphics and layout, but it is definitely the wrong choice if you want to use network calls. All HTTP requests (WebClient, HttpWebRequest, web service proxies) are bound to fail. The exact exception when trying to invoke an operation on a web service is shown below. The exception text itself is not that clear, partly because the Silverlight runtime does not contain the full exception strings. How to get the full exception text when debugging is the subject of a whole different post.
An exception of type ‘System.ServiceModel.CommunicationException’ occurred in System.ServiceModel.dll but was not handled in user code Additional information: [CrossDomainError]
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=2.0.30226.2&File=System.ServiceModel.dll&Key=CrossDomainError
To be fair, we do show you a warning when you try to debug a project on your local file system that uses a web service proxy:
This is why we have the first choice, which will host your Silverlight control in the Visual Studio web server at a domain like http://localhost:1234/ (the port number is randomly assigned). Which brings us to the second tip.
Don’t try to call services that don’t have a cross-domain policy file
Once you set up your Silverlight app in Visual Studio, it will be hosted by the built-in web server on a domain similar to http://localhost:1234/. So here the cross-domain restriction will kick in and only allow you to access web services at the http://localhost:1234/ domain. If you add a web service to your solution, you will be able to access that, since it will also live on http://localhost:1234/, and the cross-domain restriction is avoided.
Once you add the WCF service to your solution, you can generate a proxy by right-clicking the Silverlight project and selecting “Add Service Reference” and then using the “Discover” button.
Of course you don’t always want to use a local web service. Say you want to pull some weather data from Weatherbug (you can read the details of their API usage here). You can still use “Add Service Reference”, but you will end up with the situation where your Silverlight control is hosted at http://localhost:1234/ and the weather web service is hosted at http://yourApiCode.api.wxbug.net. This would fail due to the cross-domain restriction, unless Weatherbug specifically opted in by placing a policy file at the root of their domain. Fortunately they do 🙂
It is interesting to see Silverlight checking for the policy file, I used Fiddler to capture it.
Before it does anything, Silverlight searches for a policy file. First it looks for clientaccesspolicy.xml, doesn’t find it, and then it looks for crossdomain.xml, and finds it. The crossdomain.xml policy file content says that Silverlight is allowed to use the service at that domain, so Silverlight proceeds to call the .asmx web service. For details on the policy file format, see this article.
Hope this was helpful!