grack.com

Blog

3rd-Party Cookies, DOM Storage and Privacy

We’re in the process of launching our new publisher integration feature at DotSpots and needed to gain an understanding of how browsers deal with “third-party cookies”, or cookies that are set on domains that differ from the top-level domain.

Each of the major browsers, Firefox, Chrome, Safari, Opera and Internet Explorer have their own quirks about how cookies are accepted, which vary wildly depending on whether they were set by a top-level page, or a page in a third-party iframe.

Executive summary: Based on this study of browsers, the ideal method of storing information in iframes is a combination of localStorage for modern browsers and persistent cookies with a privacy policy for downlevel IE and Firefox versions. The default privacy settings are permissive enough on most of the old browsers to make this approach feasible. Earlier Safari versions that don’t support localStorage are out-of-luck, but the market share is too small to worry about.

These are the browsers I tested:

  • Firefox Default (checked ‘accept third-party cookies’)
  • Firefox (unchecked ‘accept third-party cookies’)
  • Chrome Default (allow all cookies)
  • Chrome (accept cookies only from sites I visit)
  • Safari Default (only from sites I visit)
  • Safari (accept cookies always)
  • Opera Default (accept cookies)
  • Opera (only from the site I visit)
  • IE6 Default
  • IE7 Default
  • IE8 Default

All browsers that support localStorage support setting and retrieving storage values from any frame. This includes Firefox, Chrome, Safari and IE8. HTML5 localStorage is by far the most reliable way to store information at this time for browsers that support it. There is one small difference: in Firefox, blocking all cookies will also block localStorage. In Chrome and Safari, blocking cookies does not block localStorage.

Chrome and Safari are based on the same WebKit engine and, as expected, share the same cookie policies for the same modes. Chrome defaults to the more permissive ‘Allow all cookies’ setting, while Safari defaults to ‘Allow Cookies from Sites I Visit’. When third-party cookies are disabled, frames can read cookies set by top-level pages but not write them.

Firefox defaults to a permissive mode in which cookies can be set and retrieved from all locations. Unlike WebKit browsers, disallowing third-party cookies means that a third-party iframe cannot read or write cookies at all.

Opera also defaults to permissive mode. In “accept cookies only from the site I visit” mode, it behaves the same way as Firefox does when third-party cookies are disallowed.

Internet Explorer is a bit finicky about privacy. If you add a basic privacy policy header to your responses, cookies will be accepted from iframes. Without the policy, most cookies can’t be set or retrieved in iframes at all (the exception is that iframes can read session cookies set at the top level). The following P3P header is sufficient to fix cookies in iframes.

P3P: CP="CAO PSA OUR"

To configure the header in Apache, you can use a simple mod_header line:

Header append P3P "CP=\"CAO PSA OUR\""

For those interested, here is a breakdown of cookie handling by browser and mode. Unlike cookies, HTML5 localStorage has no known limitations, so it has been omitted from the following charts:

WebKit, Allow All Cookies (Chrome default):

Set / Can be read Top level Iframe
Top level X X
Iframe X X

WebKit, Only From Sites I Visit (Safari default):

Set / Can be read Top level Iframe
Top level X X
Iframe

Firefox, default:

Set / Can be read Top level Iframe
Top level X X
Iframe X X

Firefox, unchecked ‘accept third-party cookies’:

Set / Can be read Top level Iframe
Top level X
Iframe

Opera, default:

Set / Can be read Top level Iframe
Top level X X
Iframe X X

Opera, Only From Sites I Visit:

Set / Can be read Top level Iframe
Top level X
Iframe

IE6/IE7/IE8 default mode, without Privacy policy:

Set / Can be read Top level Iframe
Top level X *
Iframe

* session cookies set by the top-level page may be read by iframes, but persistent cookies may not

IE6/IE7/IE8 default mode, with Privacy policy:

Set / Can be read Top level Iframe
Top level X X
Iframe X X

More on window.name Cross-Domain Transports

I previously discussed using window.name as a transport last year, but there wasn’t enough information and theory in the previous post to implement your own transport. I’ll fill in the gaps a bit more here to help developers looking to implement this themselves. You can also see the implementation we’re using at DotSpots in the gwt-rpc-plus project: Cross­Domain­Frame­Transport.java and Cross­Domain­Frame­Transport­Request.java.

The process of making a window.name request is described below. Note that you can inspect the HTML and HTTP traffic on my blog to see how our DotSpots plugin performs the cross-domain requests.

  1. Create the iframe and form to post. In IE, we use ActiveXObject as previously discussed to work around the machine-gun navigation sounds. You’ll need to stash a reference to the ActiveXObject somewhere where IE won’t garbage collect it.

    if ("ActiveXObject" in window) {
        var doc = new ActiveXObject("htmlfile");
        doc.open();
        doc.write("<html><body><iframe id='iframe'></iframe></body></html>");
        doc.close();
        var iframe = doc.getElementById('iframe');
    } else {
        var doc = document;
        var iframe = doc.createElement('iframe');
        doc.body.appendChild(iframe);
    }
    
    var form = doc.createElement('form');
    doc.body.appendChild(form);
    
  2. Set the the iframe’s name to a globally unique request ID. Using a unique request ID is very important, as two frames with the same name in Safari will result in one of them being set to a random name.

    iframe.contentWindow.name = requestId;
    form.target = requestId;
    
  3. POST the form to the endpoint, including the unique request ID and URL on the host domain. You can usually use /favicon.ico as a local path to return to. This is what gets posted by the DotSpots plugin on my blog:

    serial=0-8908858
    data=... (truncated JSON data)
    type=window.name
    redirect=http%3A%2F%2Fgrack.com%2Ffavicon.ico
    
  4. At this point, the iframe is now in a different security domain, so onload events won’t fire and window name is inaccessible.

  5. The server generates code to set the window name and redirect back to the original domain. Since the iframe is in a different security domain, the parent page won’t be able to tell that it was loaded. We redirect this iframe back to the URL requested once we’ve set the name.

    <html><body>
    <script>
    window.name='wnr-0-8908858(truncated JSON data)';
    window.location.replace('/favicon.ico');
    </script></body></html>
    
  6. The browser redirects back. When it gets back to the other domain’s favicon, onload will successfully fire and the window.name previously set by the server will be accessible. As multiple onload and onreadystatechange events might occur during the request/response process, you should always check the window.name for the appropriate prefix.

  7. Clean up the iframe and form. Remove the iframe and form after the request completes, but do this after a short delay. If you remove the iframe immediately after onload fires, the Firefox loading throbber will continue to animate indefinitely.

If you are considering using a transport like this for your own project, you should also consider using postMessage for uplevel browsers that support it. There are two HTTP requests required for window.name, adding a fair bit of latency to each cross-domain request. Using postMessage reduces the number of requests to one, cutting the round-trip latency by 50%.

Your server responses when using postMessage should look like this instead:

<html><body>
<script>
window.parent.postMessage('wnr-0-8908858(truncated JSON data)', '*');
</script></body></html>

If you’re using GWT, all of this work is already done for you in our open-source library, gwt-rpc-plus. We’ve tested it against all of the current desktop browsers (all the way back to IE6) and the mobile browsers too (Android and iPhone).

The Next Decade

The last ten years have been a wild ride for this planet, but I’m sure the next ten are going to be even more exciting. I’d like to offer my predictions of what we’ll see around now, ten years in the future.

The browser will continue its domination in the world of applications, absorbing more of what we do in desktop applications. A significant fraction of software development will happen in the browser, though most development will still happen on the desktop. WebGL will be starting to become a major platform for gaming. IE6 will be a war story told by greybeard developers, Microsoft having jumped back into the browser race and caught up to the leaders of the pack. No single browser will have a majority share worldwide. Javascript will still be the biggest language, but it will have gone through a few language iterations. The browser JS VM will be near the speed of native code, less than 25% slower.

Devices will continue to double in capacity and speed every few years. In 2020 you won’t have a desktop computer. You’ll have something in the form-factor of a laptop or tablet that you dock and charge wirelessly wherever you device to work. Hard-drives as we know them today won’t exist in most machines, replaced by various forms of multi-terabyte solid-state storage.

You’ll be carrying around a mini-computer in your pocket that runs at the equivalent speed of today’s MacBook Pro. It’ll multitask easily with a few GB of RAM and have nearly a terabyte of solid-state storage onboard. The mobile experience will be a scaled-down, synchronized version of your larger machine rather than an entirely separate device. In fact, some people may eschew the larger device and hook their mobile device wirelessly into display and input devices when they want an easier environment to work in.

Your phone and laptop will have high-end cameras with thin liquid lenses that will be good enough for most people to stop carrying around dedicated point-and-shoot cameras.

E-books will continue to grow, but the functionality will move out of dedicated devices and into portable computers with improved screens that work as well as e-paper today. Electronic textbooks will have taken over the majority share of post-secondary education and will start to make inroads in earlier school grades.

Land-lines will be a legacy technology in 2020. Most people will opt to forward their personal cell phones to an adapter that rings a home number as well when the phone is nearby. Telcos will start offering a much-higher-fidelity audio codec for cell phones that offers VoIP-quality conversations.

True electronic commerce will be starting to emerge in 2020, replacing wallets with your electronic devices for power users. Instead of carrying around a dozen ID and payment cards, people will have the option of storing them digitally and presenting them wirelessly. Electronic banking will take off, providing safe, standard web-based APIs around your personal finances and investments.

Our understanding of genetics today will look primitive compared to that of 2020. In 2020, genomics will have high-level structures that understand and codify the development and existence of organisms, allowing us to symbolically describe and modify how genes are turned on and off, like a computer program. We will have genetic fixes for a few of the big genetic disorders today. Some of these fixes will be applied to the human germline as well, wiping the diseases out entirely for descendants.

Car travel will take a number of big steps forward. In 2020, most modern cars will aware of each other to some degree and offer basic driving coordination like avoiding rear-end collisions and traffic management. Most cars will be LTE-capable and have online traffic updates, integration with your personal mail and text-to-speech for handsfree web ‘listening’. Rare features today such as heads-up night-vision displays and 360º visibility cameras will trickle down to a much larger segment of vehicles.

Personal space travel will be uncommon, but available for individuals for a cost around $100,000. Small space travel outfits will have small, but permanent space stations for the travellers to dock and stay for a few nights. Humans will be in the planning stages for the first extra-terran mission in our solar system since the moon landings which will involve nations from around the world.

Firefox Extensions written in GWT, revisited

I briefly blogged earlier this year about our internal project that allowed us to write Firefox extensions using the Google Web Toolkit framework. I’m happy to say that I’ve just pushed out the first version of the code for developers to start playing with.

Building a Firefox extension isn’t much different than writing a standard GWT web application. There are some caveats: there isn’t a global window ($wnd) or document ($doc) and the GWT widget system doesn’t work without some tweaks. You can, however, take advantage of GWT’s extensive DOM bindings to manipulate pages that the user loads and interact with the Chrome DOM to add toolbar buttons and menu items. I’m slowly working on extracting the code to work with these browser elements from our proprietary codebase, cleaning them up and pushing them into the open-source project.

For now, the current version of gwt-firefox-extension should be sufficient to write an application with the same functionality as a greasemonkey script without dipping into more advanced concepts. We’ve also generated bindings for the whole set of XPCOM IDL, so you’ll have access to most every service and component in the browser if you need to do something more complicated.

Try it out and join our open-source mailing list if you’ve got any feedback or suggestions.

My Presentation at Google Campfire One

I went down to Mountain View last week to show off the company we’ve been building, DotSpots, plug our brand-new Chrome extension and demo some of the great new features of GWT 2.0.

See it directly on YouTube here:

Kudos to the Google team for putting together such a great event. You don’t realize how much planning and preparation goes into an hour-long event like that.