7

I am working for a company we will call "Company x". This company has a domain companyx.com. They have a cloud platform that manages things like SSL, but in order to do this you are required to use that domain. There are 3 apps app1, app2 and app 3 that have the following urls...

app1.companyx.com
app2.companyx.com
app3.companyx.com

These are need to use stateless auth since they are highly scalable. All of these "share" the same session (JWT) using a cookie that is http-only and secure but because there is no way to create a subdomain are all using the companyx.com domain for their cookie.

The security team has pushed back saying that we are "providing the cookie" to the whole domain. This seems a little weird to me since the company also controls the core domain but I also know there is a lot of focus today on internal as well as external threats.

So is there a way to structure the cookie so that app1, app2, and app3 have access but app4 does not? Is there a more secure way than just using the core domain? How big of a threat is this really?

Jackie
  • 171
  • 1
  • 1
  • 5
  • Just spitballing, or I'd write up a proper answer -- do they all _need_ to use the _exact same_ token? Is there any reason you can't give each their own token, and have people sign in individually to each subdomain? You could even streamline it by having people sign in once at `auth.companyx.com`, then when someone doesn't have a token, redirecting them there to 'authorize' the individual subdomain. Something like OAuth, if not literally just making `auth.companyx.com` an OAuth service. – Nic Jul 02 '19 at 23:25

2 Answers2

12

From RFC 6265:

5.1.3.  Domain Matching

 A string domain-matches a given domain string if at least one of the
   following conditions hold:

   o  The domain string and the string are identical.  (Note that both
      the domain string and the string will have been canonicalized to
      lower case at this point.)

   o  All of the following conditions hold:

      *  The domain string is a suffix of the string.

      *  The last character of the string that is not included in the
         domain string is a %x2E (".") character.

      *  The string is a host name (i.e., not an IP address).

That is, if the domain name in your cookie's domain parameter doesn't start with a period, then it will not let subdomains read that cookie. If it does start with the period, then all subdomains will have full access to that cookie's value.

For example:

Set-Cookie: lang=en-US; Path=/; Domain=example.com

Can only be read by example.com.

Set-Cookie: lang=en-US; Path=/; Domain=.example.com

Can be read by example.com and any subdomain, including foo.example.com, baz.bar.foo.example.com, and baz.example.com.

Set-Cookie: lang=en-US; Path=/; Domain=foo.example.com

Can only be read by foo.example.com.

Set-Cookie: lang=en-US; Path=/; Domain=.foo.example.com

Can be read by foo.example.com and any subdomain, including baz.bar.foo.example.com, but not example.com or baz.example.com.

If you want to share cookies across subdomains, but leave out other subdomains, you should explicitly state which subdomains you want to read them, setting a new cookie for each, rather than using wildcards.

Ghedipunk
  • 5,766
  • 2
  • 23
  • 34
  • 5
    According to the recent HTTP specifications (RFC 6265) modern browsers should no longer care about the leading dot, always readable from the subdomains (Although the dot may be needed by old browser implementing the deprecated RFC 2109) – Aviran Cohen Jan 18 '21 at 10:53
  • @AviranCohen, know if that's true in practice? Any browsers that come to mind that don't support the leading dot in domains in cookies for subdomains? – DiegoSalazar Oct 28 '21 at 19:41
  • 2
    https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes states _Contrary to earlier specifications, leading dots in domain names (.example.com) are ignored._ So `.example.com` is effectively the same as `example.com`. – ashu Nov 02 '21 at 14:06
2

There are a few options.

One is simple third-party cookies; when the user authenticates, the server returns a page that makes simple requests - each with a short-lived, single-use, verifiable token - to each subdomain that the user has access to. The subdomain servers verify the token and each set a session cookie, scoped only to their (sub)domain, in the response. The downsides of this approach include making a bunch of probably-unnecessary calls, needing a way to secure the tokens, and requiring that the user's browser accept third-party cookies (most do, but not all).

Another way would be to authenticate at - and receive a session cookie that is set only for - a fully trusted subdomain (auth.companyx.com). Whenever the user tries to visit another (sub)domain (app1.companyx.com), if the user doesn't have a cookie on that domain yet, the site returns a script that makes an authenticated CORS request to auth.companyx.com. The server at auth.companyx.com checks the Origin: app1.companyx.com header, verifies the user's token at auth.companyx.com is for a user authorized to use app1.companyx.com, and returns a token - possibly a JWT - to the browser. The browser forwards that token to app1.companyx.com (via same-origin XHR), app1.companyx.com verifies the token (either by simply checking the signature and validity locally, or by making a server-to-server call to auth.companyx.com or looking in some shared DB), and returns a session cookie scoped only to app1.companyx.com. If the user tries to visit app4.companyx.com, the server at auth.companyx.com says the user isn't authorized to access that site and doesn't supply a token for it. This is, in effect, a local SSO system.

One problem both of the above scenarios have is that session management ends up being on a per-subdomain basis. If the user logs out of one domain before their session expires, they should probably get logged out of all of them, but this requires additional work to clear out the remaining cookies (or invalidate them server-side, which is more secure but requires notifying all of the servers and getting them to store state about which session tokens are / are not valid).

The usual approach here is simply to distinguish highly-trusted domains from all other domains. If app1, app2, and app3 are trusted but app4 is not trusted, then app4 shouldn't be on the same root domain as the others. This is basically just good security practice in general. An untrusted site that shares a domain root with a trusted site can do things like plant malicious cookies to try and do things like bypass CSRF protection or launch XSS attacks or something against the trusted site(s).

Alternatively, if you trust app4 but just don't want it to see the session token for app3 and think that means the user is automatically also authorized to access app4, well, that's just a standard access control situation. There are lots of ways to handle that, such as using a JWT (or similar signed authentication token) and having each site handle authorization on their own, including "scope" in the session token (again, it needs to be signed) so that each server can see if the token is both authentic (un-tampered-with) and see if the auth server granted access to a particular app, and so on.

CBHacking
  • 40,303
  • 3
  • 74
  • 98