Silverlight HTTP Networking Stack – Part 3 (Configuring a Cross Domain Policy File)

Here’s the final post in this 3 part Silverlight HTTP networking series.

In the first post, we discussed basic site of origin HTTP communication.  In the second, we gave an overview of Silverlight’s cross domain communication support.

Today, we’ll drill in to how to configure your web service to enable Silverlight cross domain callers.  If you haven’t already, please be sure to read (at least) part 2 of this series – it provides a lot context for this discussion.

Note: this tutorial has been updated for Silverlight 2 RTW

 

(Series Links:  Part 1, Part 2, Part 3)

 

So, You Want To Make Your Web Service Callable By 3rd Parties…

Exposing your web service to 3rd parties is an important decision that should be done with care and diligence.  As you’re designing your web service APIs, please keep the following in mind:

1. Using cookies/auth on your main site?  Create a separate domain for your 3rd party-accessible APIs

In Silverlight, we send cookies & authentication with each cross domain request.  Because of this, web service authors need to be careful to separate their 3rd party accessible APIs from their main site.

Let’s look at example.  http://cool.com uses cookie authentication.  Once a user is logged on, she can access and update her public profile as well as her personal account information.

Now, http://cool.com wants to enable 3rd party apps to access public profile information.   However, they don’t want to these 3rd party apps to get at a user’s billing information.

If the primary site and the web service is hosted on the one domain, then once a user logs in, cookies & auth are sent from http://cool.com applications well as 3rd party applications.

This could allow 3rd party sites to access protected information on the user’s account.

Instead, the web service apis should be hosted on a separate domain.

This way, even though cookies and auth are always sent, 3rd party applications cannot call (authenticated) web services on the main site.

Do you want to expose your web service to more than one partner, or to the public at large?  If so, then basic/digest authentication and/or cookies aren’t sufficient for that web service  authentication.  Which leads us to…

2.  Requiring 3rd party apps to authenticate?  Use an in-message authentication approach.

You should not rely on cookies or basic/digest authentication to authenticate a request on a 3rd party exposed API.  This is because multiple 3rd parties will be using the same APIs, and the same cookies and/or auth will be sent to http://api.cool.com regardless of the specific caller.

Let’s say a user trusts http://bar.com and authenticates to http://api.cool.com from http://bar.com/app.xap.   That same user does NOT trust http://foo.com.

However, since http://api.cool.com uses cookie authentication, once the user has signed in, she is signed in for all calls from that browser session, not just a particular site.  This means  http://foo.com/app.xap can access private information, even though that wasn’t the user’s intent.

If you want to authenticate the particular application calling a web service method, it is better to use an in message authentication.  For instance, you could specify the particular application’s key as a parameter in the query string.

With an in-message approach to authentication, you can determine which 3rd party application is calling your web service.

3.  Set the cache policy on your cross domain policy file

As explained last time, we use the browser plugin networking APIs to issue requests for the cross domain policy file.  This means that the normal browser rules around request caching apply to the policy file.

We recommend that you do NOT allow the browser to cache the policy file – this makes changing the policy file at later time much easier.  Turning off client caching is done by configuring your server to set a “Cache-Control:no-cache” response header on the policy file.

Remember, we only check a site’s cross domain policy once per application session, so the bandwidth/latency cost of not caching the policy file is limited.

4.  Restrict the policy as much as possible

The last piece of advice is the easiest, but the one most prone to copy & paste mistake:  don’t open up a web service to everyone unless you *need* everyone to be able to call it.

If you’re just trying to enable certain particular partner apps to call your web service, allow access to that handful of domains.

Similarly, if you only really want public callers on a certain path, then just open up that subpath.

That being said, don’t configure a policy with 400 different <resource> tags – as that is also difficult to audit and maintain.  As always, good security is a balance between reducing surface area and maintaining simplicity.

 

Configuring a Silverlight Policy File

The Silverlight policy file is called clientaccesspolicy.xml.  A Silverilght policy file that opens up an entire domain to the public & allows Content-Type header to be sent looks like:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <access-policy>
   3:   <cross-domain-access>
   4:     <policy >
   5:       <allow-from http-request-headers="Content-Type">
   6:         <domain uri="*"/>
   7:       </allow-from>
   8:       <grant-to>
   9:         <resource path="/" include-subpaths="true"/>
  10:       </grant-to>
  11:     </policy>
  12:   </cross-domain-access>
  13: </access-policy>
  14:  

Specifying WHO is allowed by a policy

Within a policy, you specify allowed domains by:

  • a specific domain  (e.g. ‘http://bar.com’, ‘https://bar.com’)
    • Represents only the domain with that specific protocol, host name & port.
  • subdomain wildcard (e.g. ‘http://*.bar.com’)
    • Represents all subdomains
  • http wildcard (i.e. ‘http://*’)
    • Represents all http:// domains
  • general wildcard (i.e. ‘*’)
    • Represents all domains (http:// & https://), if an http service
    • Represents all secure domains (https://), if an https service

Note: If your service is an HTTPS service, you need to explicitly allow insecure HTTP callers with the ‘http://*’ literal.  Otherwise, allowing ‘*” will only grant access to secure HTTPS callers.

 

Specifying WHAT is allowed by a policy

Also, within a policy, you specify the granted resources on the server by either:

  1. Supplying a path and setting include-subpaths to be false.  This means that only requests that exactly match the specified path are allowed.
  2. Supplying a path and setting include-subpaths to be true.  This mean requests whose paths are prefixed by the specified path are allowed.

 

Specifying HOW a request can be sent

By default, no request headers can be sent.  You  can designate which request headers should be allowed on POSTs by:

  1. “*” wildcard representing all non-blacklisted headers
  2. a comma seperated list of headers (e.g. “SOAPAction, Content-Type”)

It’s also possible to specify more than one policy within a policy file.

 

Examples

Here are some more clientaccesspolicy.xml examples:

Example1:  http://cool.com/clientaccesspolicy.xml:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <access-policy>
   3:   <cross-domain-access>
   4:     <policy>
   5:       <allow-from http-request-headers="SOAPAction">      
   6:         <domain uri="http://sub.cool.com/"/>
   7:         <domain uri="http://partner.com"/>
   8:         <domain uri="http://friend.com"/>
   9:       </allow-from>      
  10:       <grant-to>      
  11:         <resource path="/shipments" include-subpaths="true"/>
  12:         <resource path="/creditcards" />
  13:       </grant-to>      
  14:     </policy>  
  15:   </cross-domain-access>
  16: </access-policy>

The above policy allows requests coming from Silverlight applications on:

They can send on POSTs:

  • SOAPAction request headers
  • Content-Type request headers.  (Is allowed by default.)

They have access to:

 

Note: The allowed domains do NOT have access to http://cool.com/creditcards/ or http://cool.com/creditcards/numbers.xml, as the “/creditcards” resource tag does not have an include-subpath=”true” attribute.

 

Example2:  http://cool.com/clientaccesspolicy.xml:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <access-policy>
   3:   <cross-domain-access>
   4:     <policy>
   5:       <allow-from>
   6:         <domain ur="http://sub.cool.com"/>
   7:       </allow-from>
   8:       <grant-to>
   9:         <resource path="/partner/feeds/favorites.rss" />
  10:       </grant-to>
  11:     </policy>
  12:   </cross-domain-access>
  13:   <cross-domain-access>
  14:     <policy>
  15:       <allow-from>
  16:         <domain ur="*"/>
  17:       </allow-from>
  18:       <grant-to>
  19:         <resource path="/api" include-subpaths="true"/>
  20:       </grant-to>
  21:     </policy>
  22:   </cross-domain-access>
  23: </access-policy>

The above policy file contains two policies.  Requests are allowed if…

  • they come from an app on http://sub.com.com and are requesting http://cool.com/partners/feeds/favorites.rss
  • OR they are for any subpath of http://cool.com/api

Note:  If you’re trying to create your own policy file, Tim Heuer has written a blog post that helps you get VS intellisense when authoring a clientaccesspolicy.xml.

 

Configuring a Silverlight-Supported Flash Policy File

The Flash policy file is called crossdomain.xml.  Silverlight supports crossdomain.xml files that allow public access to an entire domain.  Specifically, this mean supporting crossdomain.xml of the format:

   1: <?xml version="1.0"?>
   2: <!DOCTYPE cross-domain-policy SYSTEM
   3:      "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
   4: <cross-domain-policy>
   5:   <allow-access-from domain="*"/>
   6: </cross-domain-policy>

So, that’s our 3 part HTTP networking series.  Hope you found it useful.  Like always, we love to hear your feedback.

Can’t wait to see the applications you’re going to build!

9 comments

  1. This is great stuff! But why do people insist on posting sample code with line numbers at the left? If i want to copy the code, then I have to go and remove all the line numbers before I can use it!

  2. I understand that challenges and appreciate the initial work done heere to get cross-domain requests up and running. However, in-message authentication is – by nature – a security risk. Until HTTPS and Basic/Digest is properly supported, it is much wiser to simply *not* expose private content via cross-domain requests.

  3. Great article! Thanks.
    Minor bug: It should be “uri” in example 2 (“i” missing).
    Question: Is there a way to explictly deny access to a subpath while giving access to a path.

  4. Pingback:Recent Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, Silverlight « Tad Wang’s Weblog

Leave a Reply

Your email address will not be published. Required fields are marked *