When orienting myself with the Phaser game library this week, I was struck by a section in the documentation that touched on a common browser gotcha. When previewing a web project on a local machine, a developer must often serve pages from a local HTTP server to avoid their JavaScript from being disabled by limitations imposed by the browser. This is because the file:// protocol is highly restricted, as explained by one of the authors of the Phaser library:

When you request anything over the web you're using http, and the server level security is enough to ensure you can only access files you're meant to. But when you drag a file in it's loaded via the local file system (technically file://) and that is massively restricted, for obvious reasons. Under file:// there's no concept of domains, no server level security, just a raw file system. Ask yourself this: do you really want JavaScript to have the ability to load files from your local file system? Your immediate answer should of course be "not in a million years!".

(From the Phaser documentation.)

While I understand the sentiment being expressed, I don't feel that the reasons for restricting the file:// protocol are entirely obvious. When I first started developing for web, I was unsure why I was forced to run a local HTTP server just to include in the Soundcloud API, load certain game assets, or see the properly-styled pages of my Pelican blog's output. Running a Python SimpleHTTPServer in the directory I was serving pages out of was simple enough, but how and why this fixed these problems was a mystery. On the surface, none of these things seemed like they ought to be disabled for security reasons: if code and media assets could be loaded manually into my HTML while working locally, why would it be so terrible to interact with them via JavaScript?

It turns out that if browsers maintained a looser policy toward the file:// protocol, it might be possible to run into code like this hypothetical attack described in a Chromium blog post:

  1. A local web page (accessed via “file://”) creates an \ with the source https://site-containing-your-sensitive-information.com.
  2. The local web page reads the contents of the iframe using JavaScript to access frames[0].document.documentElement.innerHTML.
  3. The local web page submits the contents it has read from the iframe via a form POST to a web server owned by the attacker.

The Chromium blog points out that, if run from the web rather than being accessed via file://, the same-origin policy would cause step #2 to fail consistently, making this hypothetical attack rather innocuous.

It wouldn't work if run from the local file system, either. However, the reason why it wouldn't work isn't particularly cut and dried: this hypothetical attack would actually fail in different places in different browsers, due to the way that different modern browsers deal with local web page security in the absence of a consistent way to apply the same-origin policy.

In other words, yes, it sometimes feels like a panacea to throw a simple HTTP server into a web project's directory. It's because the underlying mechanisms that disable interactions between local and external files in the browserare complicated, and for good reason!