Monthly Archive for April, 2008

Rainy Pike Place Market

Catching the market in the late evening, after another day of seattle sprinkle.

Pike Place Market (2 of 4)

 Pike Place Market

 

Pike Place Market 

 

Pike Place Market

Around Pioneer Square

More photos!

 

 

 

 

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!

Photos

For my photo class, I’m supposed to be shooting 100 photos a week in addition to my assignments.  Here are some of my favorites so far…

 

Just Words 

 

Beauty by the Bangle 

 

Rushing 

 

 

 

Maiden Lane 

 

Never. Never.

Digital Photography 2 – Assignment #2

My photography class’s second assignment was:

Use the white balance of your camera, photograph all the colors of the rainbow.  Select a scene, object, etc for each color which adheres to your "inner association" of that color.

Here are the photos… I’m happier with some of them more than others.  :)  

RED: pain, passion

 ORANGE: fun, active

 

YELLOW: hope, lightness 

 

GREEN: growth, enrichment 

 

BLUE: timeless, solitude 

 

PURPLE: decadence

Silverlight HTTP Networking Stack – Part 2 (Cross Domain Communication Overview)

In part 1, we discussed how Silverlight applications talk to their site of origin server.  In this post, we’ll dive into the high level concepts around Silverlight’s cross domain HTTP communication.  Then next time, we’ll go into details of how to set up a cross domain policy file.

Note: this tutorial has been updated for Silverlight 2 RTW.

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

What is Cross Domain Communication?

As mentioned last time, your Silverlight application by default can only talk back to its site of origin server.  This is in line with the general browser sandbox, and is designed to prevent cross site forgery.

The site of origin server is determined by the app’s deployment URI: the location of the XAP for managed applications, the address of the XAML page for javascript applications.

If a HTTP request matches the origin server’s domain, protocol, and port number, then it’s considered a site of origin request and is allowed.  Otherwise, it’s classified as a cross domain call.

Silverlight’s Cross Domain Policy Support

In order for a cross domain request to a particular web service to succeed, that web service needs to explicitly opt-in to 3rd party callers.

In Silverlight 2, the primary way of enabling cross domain calls is through a policy file placed at the root of the server.  We support two types of policy files:

  1. Silverlight Cross Domain Policy File (clientaccesspolicy.xml)
  2. (A subset of the) Flash Cross Domain Policy File (crossdomain.xml)

What Can You Say to a Cross Domain Server?

Basic Capabilities

So, you’ve got permission from the appropriate policy file to make a cross domain request .  What can you do with that request?

  • Scheme:  HTTP and HTTPS    (file:// and other protocols are not supported)
  • Verb support:  GET & POST
  • Status codes:  200 (OK) or 404 (NotFound) only

Request Headers

By default, only Content-Type request headers are allowed to be sent.  A policy file can explicitly opt in to setting certain headers.  There is also a set of blacklist headers that can never be sent.

Request headers can only be sent on POSTs, not GETs.

Cookies & Authentication

All the requests you send will have have cookies and authentication sent with them.  This is a significant point, and we’ll dig into it deeper in the part 3 of this post series.

The above capabilities are determined by the browser plugin’s networking APIs.  Please see Part 1 for a deeper discussion of networking stack implementation.

Path Character Restrictions

Silverlight has placed certain restrictions on the path portion of a cross domain URI.

Specifically, a cross domain request path CANNOT contain:

  • “..”    (consecutive dots)
  • “./”    (dot and a forward slash)
  • “%”    (percent sign, thus preventing %-encoding)

This restriction was done to prevent attacks around path manipulation and %-encoded path manipulation, which malicious apps could use to try to escape out of an allowed path into a disallowed path.

Where does Silverlight Look for the Cross Domain Policy File?

When a request is detected as a cross domain request, we look first for a Silverlight policy file at the root of the request’s server.

Request URI Policy File Checked
http://foo.com/bar/data http://foo.com/clientaccesspolicy.xml
http://sub.foo.com/bar/data http://sub.foo.com/clientaccesspolicy.xml
http://foo.com:8080/bar/data http://foo.com:8080/clientaccesspolicy.xml

If such a clientaccesspolicy.xml file does not exist or is malformed, we then look for a crossdomain.xml file at the same location.

“Caching” Of Policy Files

Silverlight looks up the cross domain policy file for a particular server ONCE per application session.  (Where an application session is the lifetime of a particular xap or xaml page instance in memory.)

Also, we use the browser networking stack to issue the request for the policy file, so the normal browser caching of the request happens under the covers.

Redirects

Redirects for the policy file itself are not allowed.

Request URIs redirects will only be successful if the original and final URIs are allowed via the appropriate cross domain policy.  (Note:  The browser handles handles the actual redirect logic itself and ensures it matches w3 spec.)

So that’s the high level cross domain behavior overview.  Next time, we’ll drill into how to set up a cross domain policy file.

Digital Photography 2 – Assignment #1

I just started a "Digital Photography 2" course at the Photographic Center Northwest.  I’ve had two classes so far and am liking it a lot (though having 10+ hours of homework a week is both LOTS Of fun… and a bit tricky).

The first assignment was to photograph 15 letters – but not real letters.  :)

Here are the results of my first assignment – can you guess which letter is which?  (You can click through to my flickr site to see if you’re right.)

A Penny for your Thoughts

Random:  I just finished last week’s New Yorker on the bus. 

Did you know that…

  • A penny costs 1.7 cents to make?
  • A nickel costs ~10 cents to make?
  • A lobby funded by Jarden Zinc Products (major supplier of penny cores for the U.S. Mint) prevented the last "get rid of pennies" legislature?
  • Coinstar is now one of the biggest pro-penny lobbies?

Just crazy. 

Ah, the way the world works.  :)

Silverlight HTTP Networking Stack – Part 1 (Site of Origin Communication)

This is going to be a three parter.  :)   Today, we’ll dig into the core networking capabilities of the HTTP stack for site of origin communication.  In the next two parts, we’ll go deep into understanding our HTTP cross domain support.

Note: this tutorial has been updated for Silverlight 2 RTW.

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

 

So, You Want To Phone Home…

In Silverlight 2, by default, you are only able to talk back to your site of origin server.  This is in line with the general browser sandbox, and is designed to prevent cross site forgery.

To determine if a HTTP request is going back to site of origin, we look at the deployment URI of the XAP and the request URI.  Comparing these two URIs, 3 things must match:

  1. Same domain. http://foo.com  is different than http://bar.foo.com or http://www.foo.com
  2. Same protocol. http://foo.com is different than https://foo.com
  3. Same port. http://foo.com is different than http://foo.com:8080

If all three things match, the request will be allowed to go out into the world.  If they don’t, by default, we’ll disallow the request and throw an exception.  (Cross domain policies will be covered in part 2 of this post series.)

What Can You Say to Your Site of Origin Server?

You can talk to your site of origin server with the below capabilities:

  • Verb support:  GET & POST
  • Request header support:  Most standard & custom headers
  • Status codes:  200 (OK) or 404 (NotFound) only

Also, all the requests you send will have the “right thing happen” to them from a cookies and authentication standpoint.  This is a result of us leveraging the hosting browser to make the HTTP request.

HTTP requests themselves are:

  • Asynchronous only

Why Those Capabilities?

The above capabilities are the result of:

  • Requirements for basic web services
  • Restrictions in the underlying browser plug-in networking APIs

To implement our Silverlight HTTP stack, we use the browser plug-in’s networking APIs.  The HTTP capabilities we expose are therefore bound by the common set of capabilities exposed by the browsers that Silverlight supports.

Note:  There are other ways we could have implemented the networking stack.  For instance, we could have proxied all calls through the browser’s XmlHttpRequest object.  However, this would have imposed a site of origin restriction on the requests, and we wanted to enable cross domain communication to existing web services.  Similarly, we could have gone directly to the operating system’s networking APIs, but then we would have lost the cookies and authentication integration with the browser.  In the future, exploring multiple of these stacks to expose more capabilities is definitely a possibility, and is something we would like feedback on.

What APIs Should I Use?

There are two APIs in Silverlight for HTTP communication.  The first is WebClient and the second is HttpWebRequest.

WebClient

WebClient is a great API with a simple, events-based paradigm.  You can use it to easily download or upload a strings and Streams.

For a download, WebClient does a GET request and then gives you back the result in the form you wanted.  For an upload, WebClient does a POST and sends the data you passed it. It also automatically resolves relative URIs against the deployment URI of the XAP.

Sample: Downloading a string using WebClient

   1: private void DownloadString()
   2: {
   3:     WebClient webClient = new WebClient();
   4:  
   5:     // Hook up events
   6:     webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
   7:     webClient.DownloadStringC
ompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
   8:  
   9:     // Initiate download
  10:     webClient.DownloadStringAsync(new Uri("myfeed.xml", UriKind.Relative));
  11: }
  12:  
  13: void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
  14: {
  15:     // Update progress UI
  16:     progressTextBox.Text = e.ProgressPercentage;
  17: }
  18:  
  19: void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
  20: {
  21:     if (e.Error == null)
  22:     {
  23:         // Clear progress UI and show downloaded string
  24:         progressTextBox.Text = "";
  25:         feedTextBox.Text = e.Result;
  26:     }
  27: }
  28:  

HttpWebRequest & HttpWebResponse

HttpWebRequest and HttpWebResponse are the standard .NET HTTP apis.  They are much more powerful than WebClient but also more complex to use.

Sample:  Sending POST request using HttpWebRequest

   1: SynchronizationContext syncContext;
   2:  
   3: private void Page_Loaded(object sender, EventArgs e)
   4: {
   5:     // Grab SynchronizationContext while on UI Thread
   6:     syncContext = SynchronizationContext.Current;
   7:  
   8:    // Create request
   9:     HttpWebRequest request = WebRequest.Create("http://msite.com/myFeed") as HttpWebRequest;
  10:     request.Method = "POST";
  11:     request.Headers["x-custom-header"] = "value";
  12:  
  13:     // Make async call for request stream.  Callback will be called on a background thread.
  14:     IAsyncResult asyncResult = 
  15:              request.BeginGetRequestStream(new AsyncCallback(RequestStreamCallback), request);
  16: }
  17:  
  18: private void RequestStreamCallback(IAsyncResult ar)
  19: {
  20:     HttpWebRequest request = ar.AsyncState as HttpWebRequest;
  21:     Request.ContentType = "text/xml";
  22:     Stream requestStream = request.EndGetRequestStream(ar);
  23:     StreamWriter streamWriter = new StreamWriter(requestStream);
  24:     streamWriter.Encoding = Encoding.UTF8;
  25:     streamWriter.Write("<?xml version="1.0"?>"
  26:                        + "<entry xmlns="http://www.w3.org/2005/Atom">"
  27:                        + "<author>"
  28:                        + "<name>Elizabeth Bennet</name>"
  29:                        + "<email>liz@gmail.com</email>"
  30:                        + "</author>"
  31:                        + "<title type="text">Entry 1</title>"
  32:                        + "<content type="text">This is my entry</content>"
  33:                        + "</entry>");
  34:                                                                       
  35:     streamWriter.Close();
  36:  
  37:    // Make async call for response.  Callback will be called on a background thread.
  38:     request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
  39: }
  40: private void ResponseCallback(IAsyncResult ar)
  41: {
  42:     HttpWebRequest request = ar.AsyncState as HttpWebRequest;
  43:     WebResponse response = request.EndGetResponse(ar);
  44:  
  45:     // Invoke onto UI thread
  46:     syncContext.Post(ExtractResponse, response);
  47:  
  48:     // use response.  Could include reading response stream.
  49: }
  50:  
  51: private void ExtractResponse(object state)
  52: {
  53:     HttpWebResponse response = state as HttpWebResponse;
  54:     // use response.  Could include reading response stream.
  55: }

Feedback – We Love It

Do the capabilities of our HTTP stack satisfy your needs?  What do you think of the API set?  We’d love to hear your thoughts.

Stay Tuned for Cross Domain Support in Part 2….