hero

How to run third party applications securely on the same domain?

Have you wondered how companies like Heroku, Netlify and Vercel host and run your applications on a single domain? Is that even safe? What if people try to set/get cookies in other people's sub-domain or root domain? I recently ran into this problem at work, and we ended up going down the rabbit hole of how it all works. I was pleasantly surprised by how it all works, so let's talk about it!

The answer to the above questions is simple, there isn't a good solution, at least not unless we manually tell each browser that we want our domain handled differently. Just relying on the browsers alone, there is no good way around the fact rogue developers could do harmful things. For example, they could deploy an app on bad-app.mysite.com and then try and set cookies on good-app.mysite.com created by another developer like you or me. Worse still, there is even nothing stopping rogue devs from setting cookies via JavaScript on the root domain mysite.com. So for companies like Netlify and Heroku, hosting different apps on the shared domain seems impossible due to the lack of separation and security concerns, right?

Luckily, we have this thing called Public Suffix List (PSL). It is a cross-browser initiative that's created and maintained by Mozilla volunteers. It is essentially a list of domains put together under its unique specification (more on this later). Different browsers would then retrieve this list at various stages and treat these domains differently from others when it comes to setting and getting cookies (Chromium browsers pulls this list during compilation and bake it into the installable app).

For example, say we want to start hosting third-party apps on subdomains of mysite.com. We could submit a change to the PSL Github repository. After the maintainers verified that we are the owner of a given domain, they will merge in our pull request, and then our domain will be added to the official PSL. Going forward, when browsers release new versions they use the PSL with our domain inside, and JavaScript can no longer set cookies on the root domain or other subdomains.

But why a manual list?

As explained already, there is no good way to determine which domain and sub-domain we should treat differently without help. Here is part of the explanation on the official PSL website:

There was and remains no algorithmic method of finding the highest level at which a domain may be registered for a particular top-level domain (the policies differ with each registry), the only method is to create a list.

PSL specification

Here is the official specification of the PSL:

  • The list is a set of rules, with one rule per line.
  • Each line is only read up to the first whitespace; entire lines can also be commented using //.
  • Each line which is not entirely whitespace or begins with a comment contains a rule.
  • Each rule lists a public suffix, with the subdomain portions separated by dots (.) as usual. There is no leading dot.
  • The wildcard character * (asterisk) matches any valid sequence of characters in a hostname part. (Note: the list uses Unicode, not Punycode forms, and is encoded using UTF-8.) Wildcards are not restricted to appear only in the leftmost position, but they must wildcard an entire label. (I.e. ..foo is a valid rule: *bar.foo is not.)
  • Wildcards may only be used to wildcard an entire level. That is, they must be surrounded by dots (or implicit dots, at the beginning of a line).
  • If a hostname matches more than one rule in the file, the longest matching rule (the one with the most levels) will be used.
  • An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule. An exception rule takes priority over any other matching rule.

Given we have a basic PSL that looks something like this:

1. com
2. *.jp             // Hosts in .hokkaido.jp can't set cookies below level 4...
3. *.hokkaido.jp
4. *.tokyo.jp       // ...except hosts in pref.hokkaido.jp, which can set cookies at level 3.
5. !pref.hokkaido.jp
6. !metro.tokyo.jp

The browser will treat the above domains as follows:

  1. Cookies may be set for foo.com.
  2. Cookies may be set for foo.bar.jp. Cookies may not be set for bar.jp.
  3. Cookies may be set for foo.bar.hokkaido.jp. Cookies may not be set for bar.hokkaido.jp.
  4. Cookies may be set for foo.bar.tokyo.jp. Cookies may not be set for bar.tokyo.jp.
  5. Cookies may be set for pref.hokkaido.jp because the exception overrides the previous rule.
  6. Cookies may be set for metro.tokyo.jp, because the exception overrides the previous rule.

Real PSL examples

So what does submission in PSL looks like? Here's the official submission for Netlify and Heroku:

// Netlify : https://www.netlify.com
// Submitted by Jessica Parsons <jessica@netlify.com>
netlify.app
// Heroku : https://www.heroku.com/
// Submitted by Tom Maher <tmaher@heroku.com>
herokuapp.com
herokussl.com

If you are interested, you can find the full list on the PSL website.

Conclusion

In case you need to build a site that hosts apps from strangers, I hope now you know what to do!

I also want to say that when the web is evolving at a remarkable rate and so much innovation, it is crazy to think there is this list on the internet manually managed by a group of people that determines the behaviours of our browsers. It is nice to see some human power when we are surrounded by machines :)

Useful resources