Subject: [http-state] Simple HTTP State Management

From: Adam Barth <[email-masked]>
Date: Sat, 17 Jul 2010 14:22:44 -0700

It's good to see various folks sending in proposals for new HTTP state
management mechanisms.  I thought I would throw my hat in the ring
with some ideas I've been discussing with Andrew Bortz and others:

https://docs.google.com/document/pub?id=1l_YcZzF82OJdld5cJX38abSlIwG3dfW37gZTk6PDHwU

I've also included the document inline below.  Let me know if you have
any feedback.

Adam

= Simple HTTP State Management =

Adam Barth (UC Berkeley)
Andrew Bortz (Stanford University)

=== Abstract ===

This document describes a simple HTTP state management mechanism,
called cake, which a server can use to re-identity HTTP clients.  In
the protocol, the user agent persists a nonce for each security
origin.  When issuing an HTTP request for a URL, the user agent
includes the nonce for that URL’s origin.

== Design Principles ==

There are a number of principles that have influenced the design of
the cake state management mechanism.  In this section, we highlight
some of those design principles.

=== Cookies are here to stay ===

Cookies are very widely used on the web and support a vast array of
use cases.  In designing an HTTP state management mechanism, we should
consider that most user agents will continue to support cookies for
the foreseeable future.  That means we do not need to design a
mechanism that meets all the use cases covered by cookies.  If cookies
are sufficient to meet a particular use case, then server can happily
use cookies instead of our new mechanism.

=== Sessions, not storage ===

Instead of trying to provide a mechanism for offloading storage from
the server to the client (as cookies do), we should aim to provide a
robust mechanism for session management.  In particular, we should
help the server link a sequence of HTTP requests as originating from
the same client.

If servers want to offload state storage to the client, servers can
either use cookies or an application-layer storage mechanism.  For
example, HTML applications have access to the localStorage key-value
store or a more robust storage mechanism in the form of IndexedDB.
Nothing we supply at the network can compete with these mechanisms on
features or robustness.

=== Minimal mechanism ===

It’s tempting to design a complex mechanism because cookies are
complex.  However, complexity is a harsh mistress.  Whenever possible,
we should aim to provide a minimal, hard-working primitive that
applications can use robustly.  For every aspect of the protocol, we
should ask ourselves whether that aspect can be removed.

In particular, we should not aim to design a mechanism that meets all
the use cases of cookies.  Such a mechanism would be far more complex
that we need.  Instead, we should narrow our focus to a specific set
of use cases and design the minimal mechanism for those use cases.

=== Privacy ===

One of the biggest failings of cookies is that they offer a poor
privacy trade-off for users.  As a goal, privacy is in tension with
state management because user’s desire not to be tracked or
fingerprinted runs contrary to having stateful interactions with
servers.  In designing a new state management mechanism, we should be
mindful of its privacy implications.  In particular:

* Because cookies are here to stay, we should remember that folks that
want to track users can use cookies to track them.  We shouldn’t
provide a mechanism that makes tracking easier, but, by the same
token, there’s a limit to how much we can improve privacy in the face
of cookies.

* Most user-initiated stateful interactions with servers begin in a
first-party context.  Safari has had success blocking servers from
setting cookies in third-party contexts.  We should learn from our
experience with third-party cookie blocking and consider adopting a
similar policy of initiating a session only in a first-party context.

* Most stateful interactions between users and servers persist for a
moderate length of time (e.g., two weeks).  Servers that require
longer stateful interactions with users often have another mechanism
for reestablishing the user’s state (e.g., a password).  We should
consider letting the user agent limit how long it stores state.

=== Align with browser security model ===

Cookies were designed and implemented before browsers had a clear
security model.  Now that browsers have a clear security model (i.e.,
the same-origin policy), we should align our state management
mechanism with that security model.  In particular:

* Separate fully-qualified domain names should have separate state
stores.  For example, the user’s session with foo.example.com and
bar.example.com should not share state.  Cookies use an extremely
dubious heuristic based on public suffixes to try to make up for their
historical error of sharing state between hosts.  We should not repeat
their mistake.

* Different schemes should have separate state stores.  For example,
http://example.com and https://example.com should not share state.
This provision is essential for providing protection from an active
network attacker (who can control http://example.com but not
https://example.com) and aligns with the browser security model.

* We should protect the integrity (as well as the confidentiality) of
sessions.  Cookies provide very weak integrity guarantees, which makes
it quite difficult to use them securely in the presence of an active
network attacker.  If we align with the browser security model,
integrity follows as a natural consequence.

A common objection to aligning with the browser security model is that
many sites today share cookies between subdomains.  If sites wish to
coordinate state across different security origins, we have some very
nice protocols they can use, both at the network layer (e.g., OAuth)
and at the application layer (e.g., postMessage).

== Protocol ==

=== Syntax ===

The Cake protocol consists of a single HTTP request header, called Cake:

cake-header = "Cake:" OWS cake-nonce OWS
cake-nonce  = 1*BASE64CHAR
BASE64CHAR  = ALPHA / DIGIT / "+" / "/" / "="

=== Semantics == =

The Cake header contains a nonce that identifies the network endpoint
from which an HTTP request originated.

The user agent stores an independent random nonce for each origin#.
When issuing an HTTP request for a URL, the user agent includes the
nonce for that URL’s origin in the Cake header.

The user agent MAY regenerate an origin’s nonce at any time.  We
recommend that the user agent persists an origin’s nonce for two weeks
after the last time it issued a “first-party” request to that origin.

The user agent MAY omit the Cake header from any request.  We
recommend that the user agent omit the header for requests to a
particular origin until the user agent issues a “first-party” request
to that origin.

The user agent MAY send a nonce of any non-zero length.  We recommend
that the user agent choose a nonce that is long enough to make online
guessing attacks extremely unlikely (e.g., 64 bits of entropy, 11
characters, are likely sufficient).

== Discussion ==

We expect servers to use the cake nonce as a session identifier.  In
particular, we expect the server to maintain a hash table that maps
cakes to server-side session information.  When receiving an HTTP
request, the server uses the nonce from the cake header to look up the
state for the session in the hash table.

Q: Why not let the server specify the header value?
A: Letting the server store information in the header is just an
opportunity for the server to shoot itself in the foot.  At worst, the
server might become vulnerable to a session fixation attack if the
server lets the attacker pick the cake value.  Even outside of an
attack scenario, letting the server write to global state creates a
race condition.  Moreover, at best, the server will just store an
opaque nonce in the header, so we might as well remove the sand traps.
 If the server wants to offload state to the client, there are plenty
of other mechanisms designed for storing state on the client.

Q: Why not let the server specify the lifetime of the nonce?
A: There are two cases: (1) the server wishes to have a shorter
lifetime than the one provided by the user agent, and (2) the server
wishes to have a longer lifetime than the one provided by the user
agent.  In the first case, the server can just flush the entry for the
nonce it its server-side state table.  In the second case, the default
expiry is a balance between privacy concerns about tracking and the
benefits of not having to constantly reestablish a session.  Note that
the server can always store more persistent state in application-layer
state stores, such as localStorage and IndexedDB.

Q: Why omit the nonce until the user agent issues a first-party
request to an origin?
A: Omitting the nonce before the user agent issues a first-party
request to an origin serves two purposes: privacy and performance.
For privacy, omitting the nonce provides some degree of privacy
protection against tracking by third parties (approximately the same
protections as Safari’s blocking writes to third-party cookies).  For
performance, omitting the nonce reduces the size of requests for
resources hosted on content distribution networks, which have no
interest in stateful interactions.  For example, the user never issues
requests to fbcdn.net in a first-party context, and so all the
requests to Facebook’s CDN will omit the nonce.

Q: Why not wait for the server to enable the protocol?
A: Essentially, we want the server to be able to link the first
request to the rest of the session.  Imagine your site wants to use
cake as a session identifier.  You receive a network request for your
home page and you response with some HTML and a request to enable
cake.  Now, you get a second request, this time with a cake nonce.
How do you link the second request to the first?  By contrast, if the
first request includes the cake nonce, then you can create an entry in
your server-side state table and you’re off to the races.

---

From: Adam Barth <[email-masked]>
Date: Sat, 17 Jul 2010 16:06:58 -0700

On Sat, Jul 17, 2010 at 3:54 PM, Dave Kristol <[email-masked]> wrote:
> On 07/17/2010 05:22 PM, Adam Barth wrote:
> That's pretty funny.  What goes around comes around.  In 1995 I proposed
> what I think may be a similar mechanism.  (See
> <http://kristol.org/cookie/session.txt> or .ps, if you prefer.)  It's
> mentioned in my ACM article, previously mentioned here
> (<http://arXiv.org/abs/cs.SE/0105018>).  When the state management sub-group
> started up, it was one of three proposals we considered, Netscape's cookies
> being one of the other two.

Great minds think alike?  :)

Some subtle differences:

1) State-Info looks to be chosen by the server instead of generated
randomly by the client.
2) State-Info looks to be scoped to an entire origin server instead of
divided by port and scheme.

Adam

---

From: Mark Pauley <[email-masked]>
Date: Mon, 26 Jul 2010 15:34:36 -0700

Awesome!  This is almost exactly the thing we spoke of in anaheim :)

Cake would certainly simplify any session based web application.

This solves some of my main issues with state management via cookies:
1) Cookies are too big.  They allow naive web applications (often via lazy web frameworks implementors) to waste tremendous bandwidth.
2) Cookies are insecure, web apps often expect cookies set via https to be sent via http and vice versa.  Cookies are sometimes shared between applications on a system.
3) Cookies are too complicated.
4) Cookies apply to too many requests.  Most of the requests (for example to img.foo.com) don't actually need cookies.  To eliminate a wasted round trip we currently send them anyhow.

I think the small, fixed length of the cake nonce along with the strict partitioning of server / schemes makes sending cake without prompting a much more digestible solution.
I agree that cake would make the world a better place without much overhead.

_Mark

On Jul 17, 2010, at 2:22 PM, Adam Barth wrote:

> _______________________________________________
> http-state mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/http-state