Apps/PackagingProposal
Contents
Privileged Apps
We have the following assumptions about Privileged Apps, as stated at Apps/Security:
- They are reviewed in some form. I.e. a trusted party has indicated some level of trust in the application. In the initial version of B2G we've had as a requirement that the store should review the actual app contents. In future releases I'd also like to support stores indicating trust in the app developer rather than the app itself, but this isn't slated for the initial B2G release.
- They are signed. This is needed in order to verify that the app actually contains the reviewed content. It also protects against someone hacking the webserver which the app is served from.
- They use a minimum CSP policy in all pages in the app. This to ensure that the app doesn't get hacked using XSS attacks. I.e. we want to ensure that only the app developer's code runs, and not an attacker's code. Obviously this will never be perfect, but it will hopefully help a lot.
- The resources in a privileged app should use a different "cookie jar" than resources from the developer's website. This so that the privileged app can rely that the data it stores in cookies, indexedDB, etc isn't compromised. I.e. this is to further make it possible to hack a privileged app by hacking the developer's website and overwriting data which the privileged app relies on to make it behave in ways it otherwise wouldn't.
- The resources in a privileged app should not be "same origin" with any resources other than ones from the same privileged app. I.e. if a privileged app creates an <iframe> pointing to the developer's website, javascript running inside the iframe shouldn't be able to reach out and touch the objects in the privileged app. This is since otherwise it would obviously be significantly easier to hack a privileged app by hacking any websites that it opens in <iframe>s.
Delivering Privileged Apps
Our initial thoughts for how to implement these requirements were that privileged apps should use largely the same delivery mechanism as normal apps. I.e. they should live as normal files on the developer's web server. The resources would then be cached using the normal app cache. The signatures for the reviewed files would be gotten from the store at the time of installation as well as any time the app is updated.
We came up with this solution since we wanted to avoid "packaging" apps into some sort of archive (like zip) and delivering it through the store. W3C widget specs and Chrome apps use this solution.
However, there are a couple of problems with this solution.
Serving Privileged Apps from the Web Won't Work
As Brian Smith has pointed out, signing HTTP responses isn't trivial. There's a significant risk that proxies will change headers on the way between the server and the user's browser. There even some risk that proxies will change the contents of the response bodies themselves. During normal browsing this isn't a big deal. But when the responses are signed it means that the response is completely rejected, leading to a very bad user experience. The only way to really prevent this is to always use https to serve the resources. However, this can be a non-trivial cost for the app developer.
Once we have several stores, stores will inevitably take a differently long time to review an update of an app. This will cause confusion since the different stores will be serving different signatures for the resources of the app. One solution would be for the app developer to always use new URLs for every version of the app. However, this is problematic if you are developing a twitter app and want to enable sending emails containing links to individual tweets since those links would then point to a specific version of the app. Another solution would be for stores to not serve the new signatures until the developer says that the new version is published, and then for the developer to publish the new version only once all stores have reviewed the new version. But this limits publishing speed to the slowest store, which is likely not acceptable, and also means that no store has an incentive to review quickly.
Additionally, there are some things that are very non-webby with this solution.
Signing the resources means that you can no longer generate your HTML or image files on the fly using php, perl, ruby, etc. This is because you would have to generate a new signature at the same time as you generate the file contents. However generating a new signature would require having the private key on your web server, which largely defeats the purpose of signing since if someone hacks the web server, they can create their own signed resources. Hence signing effectively requires that the resources are static files located on the server.
Also, since it's the store that has to review the app and send new signatures for any updated version, it means that publishing a new version of your app no longer means simply updating the contents of your web server. You also have to contact the stores which you are selling your apps through and ask them to re-review your app and create new signatures for the app's resources.
When looking at the proposed solution this way it seems like we have come up with something that is the worst of both worlds. App developers are forced to create apps which consist of static resources and every time they want to publish a new version they have to contact all stores that they want to sell their app through. This feels a lot like a packaged app, the only difference is that the resources live as separate files on the developers server rather than as a zip file in the store. Yet app developers have to serve their apps through SSL and deal with coordinating publishing through multiple stores. This is non-webby, a pain to publish through and potentially expensive.
Packaging for Privileged and Certified apps
Hence we have changed the plan to instead base privileged apps on a "packaged" solution. The idea will be that to publish a privileged app the developer will create a zip archive which contains the app manifest as well as all resources needed for the app. The app can then be sent to all stores which the developer wants to publish the app trough. These stores can then review the app and sign the package. At installation time the signed package is downloaded by the B2G from the store and the package is verified against the signature.
One obvious question once you go with a packaged solution is what URLs the packaged resources will be available through. My initial thought was to keep things as webby as possible and to make the resources in the package available as normal http or https URLs. The goal was that packaged apps would behave as much as possible as non-packaged apps. This would mean that in a privileged app a page that was part of the app would have URL https://developer.com/myapp.html, and a page which was part of the developer's website would have the URL https://developer.com/index.html. Yet the two pages would use different cookies, and any same-origin checks would have to say that the two pages are different. This is to satisfy the last two requirements in the bullet list at the beginning of this email. This is certainly implementable, however it seems very confusing for developers.
Instead I think that we introduce a new protocol, app://, which is used to load all resources in privileged apps. This is an idea that Jim Straus came up with over in bug 707625. Introducing this new protocol certainly has the disadvantage that it feels less webby, however I think the benefit in clearing up the confusion above outweighs it. It makes it very clear which pages will receive which cookies, and which pages are same-origin and thus can reach in to each other. To make things as webby as possible though, I think such URLs should be given a "real" domain, like app:// developer.com/myapp.html. This would be the domain of the developer, i.e. a domain that the developer has to own. We would have to come up with some mechanism for verifying this ownership though.
To make things more similar to how web pages normally work, we could allow pages from app://developer.com/ to make network requests to http://developer.com. I.e. the app would be allowed to open XMLHttpRequest connections to http://developer.com/myapi.cgi without requesting any special privileges. Likewise images and videos loaded from http://developer.com would not be considered cross-origin for example for the purposes of tainting when drawn into a <canvas>. This way most of the code which would work for a website would work in a packaged app, except that the packaged app would have to ensure to use absolute URLs when wanting to connect to the website.
In order to keep applications isolated from each other (except through explicit APIs like WebActivities) the app:// protocol always maps to loading from the package that the load is initiated from. Consider a user which has a facebook app and a google maps app installed. Whenever the facebook app loads an app:// URL, we always look in the package which contains the facebook app. If the resource isn't found there we return a 404 error. I.e. we never even look at the google maps package, even the facebook app tries to load something like app://google.com/map.html, the result is simply a 404. This also means that it's not a problem to install two apps from google which both contains the same app:// google.com/library.js URL. Each app would simply load the library.js file from its own package.
There are still lots of parts that we need to figure out. First of all we need to define an exact format for the package. There are lots of formats out there for signed packages, .jar which is commonly used by java, .xpi which Firefox uses for addons, .crx used by Chrome, etc. We can either pick an existing one, or design a new one. I'll explicitly declare it off topic for this thread to come up with the specific format. Please start separate threads about that.
Updates
We also need to come up with an update mechanism. One solution would be to every now and then check the URL which we used to download the original package to see if a new version is available. Another solution would be to send a single request to the store enumerating all apps which we want to check for updates for. That would allow for less traffic in the common case of no apps needing any updates, but might be more complex to implement and could have privacy implications.
Webby-ness
All in all this definitely means that privileged apps won't be as webby as normal apps. However as long as we have the requirements around signing by the store, an enforced CSP policy and a separate cookie jar, privileged apps simply won't be very webby. Whether we zip the files up in an package and deliver them the store for signing, or we deliver them through the store through http, is a relatively small difference which only affects the deployment of the app, not the development.
In the long run I definitely want to make privileged apps more "webby". Both by changing our privileged apps work, and also by giving the web more capabilities so that they can meet somewhere in the middle. But I think the above proposal is a good way to start by doing something secure which will give us experience and help us develop something better in the future.