Belay Lessons: Smarter Web Programming
This post comes from the keyboard of Matt Carroll, who has worked with us for the past two years. He's the main implementer of desugaring for S5, and spent this semester rebuilding and improving in-house Brown PLT web applications. He writes about his experience here.
The Brown computer science department uses a home-grown web application called Resume to conduct its faculty recruitment process. This semester, Joe and I re-wrote Resume with Belay. Belay is the product of Joe and Arjun's summer research at Google: it's an ongoing inquiry into web development best practices, specifically concerning identity, account management, and security. From my perspective (that of a novice web programmer), getting to grips with the Belay philosophy was a thought-provoking experience, and a great education in the pitfalls that a web developer must (unfortunately) bear in mind.
I Am Not My Cookies
Your everyday possibly-CSRF-vulnerable site probably has a URL scheme with
well-known endpoints that lead directly to application functionality. For
example, to post to your blog, you (typically via your browser) send a POST
www.blog.com/post with your cookies and the
blog body's text. The server-side handler finds your account in the database using
your cookie, checks that your account can post to that blog, and adds a new
post. If the whole surface of the site's URL space is well-known, a CSRF-ing
attacker can excercise the entirety of a user's view of the site with one
A Belay app uses its limited URL scheme as its primary security mechanism, ignoring requests unless they come along trusted capability URLs created by a prior, explicit grant. As long as we can rely on our platform's ability to generate unguessable large random numbers, attackers are out of luck. And, even if a capability URL is leaked from its page, it is scoped to only a small set of data on the server, so the vulnerability is limited. This is a much-improved situation compared to a site using cookie-based authentication---leaking a cookie leaks access to the user's entire view of the site.
Grants and Cap Handlers
Here's a Belay request handler, taken from Resume:
This handler simply looks up the filename associated with a reference and
returns it (using a few helper functions). Accessing a letter written by an
applicant's reference is quite a sensitive operation---letting the wrong
person access a letter would be a serious security bug. Yet,
GetLetterHandler is a two-liner with no apparent security checks
or guards. How can this be safe?
To answer this, we need to consider how a client can cause
GetLetterHandler to be invoked. The Belay server library will
only invoke this handler via capability URLs created with a
GetLetterHandler. So, we can search the codebase for code
that granted such access. A quick search shows one spot:
GetApplicantsHandler is invoked, it will return a
structure that, for each applicant, shows something like:
On the server, the string
f7327056-4b91-ad57-e5e4f6c514b6 was created and mapped to
the pair of
GetLetterHandler and the
database item for Theodore Roosevelt. A GET request to the URL above will
return the reference letter. Note a nice feature of this setup: the server
doesn't use any information from the client, other than the capability URL, to
decide which reference's letter to return. Thus, a client cannot try
providing different id's or other parameters to explore which letters they
have access to. Only those explicitly granted are accessible.
Poking around in the codebase more, we can see that
GetApplicantsHandler is only granted to reviewers, who can only
create accounts via an email from the administrator. This reasoning is how we
convince ourselves, as developers, that we haven't screwed up and given away
the ability to see a letter to the wrong user. We do all of this without
worrying about a check on accessing the letter, instead relying on the
unguessability of the URLs generated by
grant to enforce our
This may seem like a new-concept overload, and indeed, I had that exact reaction at first. Over time I gained familiarity with the Belay style, and I became more and more convinced by the benefits it offers. Porting Resume became a fairly straightforward process of identifying each server-side request handler, converting it to a Belay handler, and ensuring that whatever pages needed that functionality received grants to call the handler. There were wrinkles, many due to the fact that Resume also uses Flapjax (a language/library for reactive programming in the browser). Flapjax is another Brown PLT product and it is certainly worthy of its own blog post. We had to account for the interaction between Belay's client-side library and Flapjax.
Note that Belay isn't the first place these ideas have surfaced. Belay builds on foundational research: Waterken and PLT Web Server both support cookie-less, capability-based web interactions. The Belay project addresses broader goals in identity management and sharing on the web, but we've leveraged its libraries to build a more robust system for ourselves.
After my experience with the Resume port, I'm certainly a Belay fan. The project has more to say about topics such as cross-site authorization, sharing, and multi-site identity management, so check out their site and stay tuned for future updates:Belay Research