Every professional community has its cryptic horror stories …
The story I’m about to tell is the one senior developers will tell interns on a dull winter late afternoon when the cold neon lights cast their shadows through an empty open space. More seriously this is the kind of software drama than can easily cost your company its reputation and you your career.
Everything starts with a good old ASP.Net like project using bullet proof out of the box forms authentication. Passwords are salted into network isolated databases, best security practices are implemented cautiously throughout, login page is carefully handcrafted, the authentication flow is resilient to timing attacks and the site is regularly tested by security experts. Rock solid we tell you!
Well, that was the case till this normal post patch morning where got reported cases of users ending up with the wrong identity while navigating the app. An identity swap , WHAT ? It’s as bad as if you’re browsing your Facebook feed when after clicking on a random notification icon you would end up impersonating your ex’s profile, having access to all their posts, pictures, instant chat, everything!! That’s really bad, isn’t it? Well hard to think about something worse… Impossible? No way such thing would happen you keep telling yourself. Well, that’s absolutely possible and even super easy to reproduce, how? Let’s dig in, shall we?
Before delving into the more specific of this use case let’s reintroduce one of the pillar of the web security. The Auth. cookie.
While all the examples given comes from the Microsoft stem, all the principles remain the same across the different modern frameworks and I’m sure we could emulate this use case easily with different stacks.
The Auth. cookie is a dictionary of few properties related to the user identities that are encrypted server side using a private key (machine key set in the configuration file)
and then passed on in the HTTP Response header to the client.
Generally the Auth. cookie is attached to the HTTP Response after the login flow succeeds and is then stored in the client’s browser to the specific domain.
This token remains valid for a given period of time (generally configurable) and is passed alongside every request in the HTTP header.
This cookie is decrypted by the web app which checks its validity and accesses the pieces of information encapsulated (generally some information regarding the Identity) and then performs all the set of authorization filtering.
When the cookie expires, the web server will naturally rejects the request. Most web sites implement some sort of cookie sliding expiration technique which causes a fresh cookie to be reissued if the user stays active.
If configured in ASP.Net a new cookie is issued once the initial cookie is half-expired.
Let’s assume that your web application contains some static content _ images, user documentation, marketing material_ which used to be publicly accessible and, for some business reasons, it’s been asked to our fellow developers to restrict this content only to the authenticated users.
There are many ways this can be achieved in ASP.Net / IIS, one way could be to remove the precondition in the ASP.Net URL Authorization HTTP modules
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" preCondition="managedHandler" />
https://docs.microsoft.com/en-us/iis/manage/configuring-security/understanding-iis-url-authorization
and tells specifically ASP.Net to consider the static content as restricted to the sole authenticated users
From this point on, every single request to the resources within the Images folder will go through the standard HTTP authorization pipeline… including the cookie setting process.
So what? Well you’re right that at this stage having the authorization layer on top of the static content isn’t a drama and fit the business requirement.
Sure, it’s not yet a bloodbath but we’re getting closer… and the final hit will come with the last piece of the puzzle … our loadbalancer
Let’s assume that our load balancer / reverse proxy is duly configured to interpret the cache-control header of our HTTP response and serves appropriately cached resources to our client.
This is a quite frequent behavior which can improve the performance of the application by reducing the traffic in the DMZ.
There are 3 main modes for cache-control attribute:
Our static content, being static, is naturally eligible to be cached and we’ve set our application to place the right header for the static resources.
There are multiple ways IIS would place the cache-control header onto the HTTP response.
https://docs.microsoft.com/en-us/iis/configuration/system.webserver/staticcontent/clientcache
Note that the HTTP caching doesn’t occur solely at the WebServer/Load balancer level, the client’s browser equally interpret the caching tags and fetches the resources only when necessary.
More information on the HTTP caching with this excellent article
Now we have everything for our disaster at scale, we just need to wait for having the right configuration which will ineluctably happen:
I swear no developer has been hurt during the redaction of this post !
While fictional for the most part, this article outlines how intricate things can get in modern software architectures. Taken in isolation, the different layers might behave accordingly to their specs but put together you can end up building a scaffold to hang yourself with if you don’t see the big picture of your system as well as intimately understand the key concepts of the frameworks you’re utilizing.
This is surely hard and building apps mostly web public facing services can get tricky as there are a lot of different moving parts in there.
While really extreme I hope this example shows how quickly things can conspire to crush the best possible design. It is beyond good or bad teams, best practices or custom stuff, bad things happen and we shall envisage this with constant humility.