Cross-Domain Data with Rack and Rails

By Jon Crosby | May 28th, 2009 at 10:05AM

An undeniable theme from RailsConf this year was the arrival of Rack in Rails as of version 2.3. The first topic that seemed to pique developer interest was Rails Metal. Most enthusiasm centered around the speed bump of using a lighter stack for high volume URIs. While this is a useful upgrade to Rails, the architectural shift provided by Rack brings new flexibility. In this post, I will demonstrate the unique value of mounting Rack middleware in your Rails stack.

Cross-domain Data

Web applications using AJAX to obtain third party data must deal with the browser’s same-origin policy. Typical workarounds include either JSONP or the use of a service proxy on the home domain. Both techniques bring trade-offs. JSONP removes the same-origin barrier but leaves the cross-domain security hole in place; a third party service can execute arbitrary JavaScript on the client site. Service proxies offer more safety at the cost of new infrastructure and code that must be maintained.

A Rails app serving API data can now provide a third option without opening security holes or requiring new infrastructure for client sites.

AJACSS Sounds Like AJAX

Asynchronous JavaScript and CSS, also known as CSSHttpRequest, is a method of URI-encoding data in 2KB chunks split over several CSS rules with a modified data URI scheme. Because CSS is not subject to the same-origin policy, no service proxy is required. Moreover, because this is CSS, the security concerns of JSONP no longer apply. Here’s an example CSS snippet with an encoded “Hello Rails” message:

 #c0 { background: url(data:,Hello%20Rails); }
Shopping for Rack Middleware

If you’re in need of a piece of Rack middleware, there are two places to check before rolling your own — the Rack wiki and rack-contrib on GitHub. In the case of CSSHttpRequest, we can just grab a pre-made piece of middleware and drop it into our Rails app thanks to Cameron Walters from nb.io.

In config/environment.rb, add the following code inside the Rails::Initializer block:

# First specify the required gems so that rake gems:install will install them
config.gem "rack-contrib", :lib => 'rack/contrib/csshttprequest'
config.gem "nbio-csshttprequest", :lib => "csshttprequest", :source => "http://gems.github.com/"

Next, load Rack::CSSHTTPRequest in your middleware stack

config.middleware.use 'Rack::CSSHTTPRequest' That’s it! Rack::CSSHTTPRequest will automatically encode the output for any request using the “_format=chr” convention used by Rails. Other web apps and services can use the data without exposing security holes or requiring new infrastructure.

This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

16 Responses to “Cross-Domain Data with Rack and Rails”

  1. Brent Brent says:

    Wow. That is truly weird.

  2. charlieok charlieok says:

    That is an affront to all that is good and holy. But I salute whoever came up with it.

    I was thinking of using a piece of Rack middleware for authentication, and another piece (after the authentication piece) for split testing. The idea would be that the split testing piece now has the id of the user making the request, and could have two or more different instances of an application running behind it. It could then randomly assign users to test groups and then route them to different instances of the application depending on which test group they are in.

    Would this kind of routing/deployment scheme of split testing be a good fit for the new Rails 3/Rack architecture?

  3. Michael Mullany Michael Mullany says:

    Charlie, you might want to take a look at the RailConf preso on reverse proxy for A/B testing: http://bit.ly/dxMKQ from Ilya Grigorik

  4. jcrosby jcrosby says:

    Charlie, authentication is a great use case for middleware. If you're interested, I covered a bit of this at MountainWest in my Rack Middleware talk. The video is here: http://mwrc2009.confreaks.com/13-mar-2009-11-05-i... Also, CloudKit (http://getcloudkit.com) contains an example of integrated OpenID and OAuth middleware.

  5. Jon Crosby Jon Crosby says:

    Looks like link parsing includes parenthesis and periods in the comment system. Here are the links on their own: http://mwrc2009.confreaks.com/13-mar-2009-11-05-i... and http://getcloudkit.com

  6. drnic drnic says:

    If we need a self-hosted proxy to encode 3rd party data then aren't we defeating the aim of not requiring a proxy to access cross-site content? If we're going to create a proxy then it might as well pass through the raw data for normal Ajax requests?

  7. ActsAsFlinn ActsAsFlinn says:

    I don't see the objection to JSON-P. I'm not going to trust a partner more if they are using AJACSS over JSON-P. If I don't trust partner I'm not going to embed their service data on my pages. There are old precedents for providing features/widgets/ads via external javascript includes.

    Also, isn't IE6 susceptible to XSS attack via CSS?

    One thing is certain browsers need to provide a cross-domain non-eval'd data source mechanism. Why wasn't this addressed in HTML5? Seems like a bigger issue than web sockets.

  8. Jon Crosby Jon Crosby says:

    Dr Nic, I should have explained more clearly in the original post. The encoding is done on the side of the data provider so that all clients can access it without needing to set up their own proxies.

  9. Jon Crosby Jon Crosby says:

    ActsAsFlinn, the difference between JSON-P and AJACSS is that the later is just read-only data.

  10. [...] Cross-Domain Data with Rack and Rails – Encoding data into CSS rules to get around cross-domain security policies is certainly one of the nastier perversions of web standards I’ve seen lately. [...]

  11. Randy Reddig Randy Reddig says:

    Hi, I created CSSHttpRequest. It implements a guard against expression evaluation in IE by specifically making the stylesheet target media=print.

    I suppose it’s possible, however unlikely, to construct a scenario where untrusted code might be evaluated. It would require the user to print a page at the instant a CHR transport iframe containing malicious CSS is present on the page.

  12. Does this enable a client to paste a widget in to their views (an email collection form, perhaps), to post data to the widget provider (i.e. emails post to another domain), and for widget provider to return a message to be rendered in the DOM via CSS, without another http request or redirect?

  13. Jon Crosby Jon Crosby says:

    Neil, CSSHttpRequest does not cover those use cases. It provides read-only data so posting would need to happen independently. For the second use case (getting a message), it requires a new HTTP request.

  14. Right, thanks Jon. I'm still a little fuzzy on this – your point about the 'second use case' (getting a message) has thrown me as I thought that your example was getting 'Hello Rails' – but this question could clear things up; what would you see as a good use case for CSSHttpRequest from UI perspective?

  15. Jon Crosby Jon Crosby says:

    Neil, one example might be displaying a widget on your blog that contains your latest 5 status updates on Twitter. Another might be a public transit site that embeds arrival and departure times for various services (trains, buses, etc.). These are cases where read-only data hosted on other sites (sites that use CSSHttpRequest) might be valuable.

  16. Stulseple Stulseple says:

    Your blog is so interesting! I have subscribed on rss and I will read it regullary/

Leave a Reply