Re: Replay attacks with cookie session

Brad Ediger
2007-03-29T01:48:39Z

On Mar 27, 2007, at 9:46 PM, S. Robert James wrote:
> As I said earlier, I''m waiting for a spec and sample app before
> working on a full review.

I went ahead and wrote up a spec. Here''s my first crack at it.

Everyone: Please feel free to make suggestions / corrections.

--------

-- Rails Sessions

The Rails web application framework provides storage for sessions,
which are small pieces of data that persist across a stream, or
"session," of requests by the same user.

In this document, "user" means a person that makes requests via HTTP
to a server. "Client" refers to a software program (usually a web
browser) acting on the user''s behalf.

A session may be used to store any type of data, but it is most
commonly used for the following information:

* A user ID number, which identifies the currently logged-in user
* A "return to" URL, which indicates the location the user should be
sent to after logging in (used when an unauthenticated user tries to
access a restricted page)
* "Flash" information, or short notification and error messages
concerning the status of the user''s most recent request

Clearly, the key security requirement is data integrity. The client
is not trusted, and must not be allowed to modify the session, create
sessions, or assume another client''s session. All session
modification is performed by the server, which is trusted.

In some cases, the client may not be allowed to read the session
(e.g., account numbers, third-party passwords), but this use of the
session is strongly discouraged. However, it is desired that Rails be
forgiving of session misuse, in that such misuse should not cause
hidden security vulnerabilities where at all possible.

The only action that the client is allowed to directly perform on the
session is abandonment. At any time, the client may make a request
that does not include the session identifier. The server treats this
as a new session. Rails applications are written so that abandonment
of a session at any time does not cause problems.

Rails aims for a "shared-nothing" style of scalability, in which
there may be many application servers running Rails. All shared state
is held in either the database or some custom-engineered repository
(such as memcached). A load balancer is placed in front of the
application servers to distribute requests. This way, any server can
handle any incoming requests. A stream of requests comprising a
session may hit several servers over the course of the session.

-- Server-Side Session Storage

Until February 2007, all storage for sessions took place on the
server. There were several backends that stored sessions on the
filesystem, in a database, or in memory. This provided data integrity
(since these resources can only be modified by the server) and
confidentiality (as the sessions were only trafficked between the
application server or servers). Confidentiality may have been a
detail of implementation rather than a design goal in this instance.

Under server-based storage, the session is indexed by a session ID,
which is an MD5 hash of:

* POSIX time (seconds since epoch)
* Microseconds part of current time
* A pseudorandom number in the interval [0,1), converted to a string
* The application server''s process ID
* The string ''foobar''

All of this randomness serves to mitigate session hijacking attacks,
as session IDs cannot be easily guessed. The client is given the
generated session ID in an HTTP cookie. Sessions can expire either
when the browser is closed or after a fixed period of time (between
15 minutes and months to years, depending on the application); this
is controlled by the expiration time on the cookie.

-- Client-Side Session Storage

In February 2006, a new session storage method was introduced and is
now the default for edge (trunk) Rails. The release branches have not
yet switched to this method. This method, the CookieStore, stores the
entire session in Base64 encoding in a cookie.

The CookieStore provides no confidentiality, but integrity is assured
by signing the cookie with an HMAC. SHA-1 is the default. The server
verifies the HMAC every time the cookie is deserialized. The HMAC is
keyed off of a user-supplied secret. For new applications, Rails
creates a secret when the application is generated. The secret is
generated from the session ID algorithm described above, where
''foobar'' is replaced with the application''s name.

There is a 4 KB limit to the total Base64-encoded data, including the
HMAC. This limit reflects the standard architectural limitation
imposed on HTTP cookies. There is no graceful recovery from an
oversized session -- it is an error to attempt to store more than 4
KB of data in a session under the CookieStore.

The advantages of the CookieStore are twofold: (1) The cookies are
transmitted as part of the standard HTTP request/response cycle, so
there is little session-lookup overhead. The roundtrip to the
database server or shared memory store is eliminated. (2) The server
does not need to keep track of open sessions. This mitigates a
potential denial-of-service vulnerability where a client could open
many sessions and exhaust a server''s inodes.

There are some disadvantages, though. All sessions are considered
equal to the server -- it is a consequence of not having to keep
track of every session in progress. Without some shared state (such
as flags on a user account in the database), the server must either
honor all sessions or none of them. There is no way to selectively
delete or expire sessions. Session expiration can be achieved without
shared state if an expiration time is included in the cookie, but
this requires sending a new cookie with each request (cookies are
normally only sent when session data changes).

A more serious problem with the CookieStore is the possibility of
replay attacks. Since the cookie consists only of the session data
signed with an HMAC, a valid session can be saved and replayed later;
it will always be valid at the framework level unless the secret is
changed. The application programmer can introduce time-variant
information to invalidate old sessions (as described above), but from
the framework''s perspective, if the server signed it at some point,
it is a valid session.

It has been suggested that a nonce be incorporated into the cookie to
prevent replay attacks. This presents several logistical problems,
though none of them is insurmountable. First, the nonce will
necessarily change with each request and therefore the cookie must be
regenerated, re-signed, and retransmitted to the client with each
request. Secondly, and most importantly, the nonces must be
synchronized among the application servers. This necessitates hitting
the database or another shared store on every request for a nonce
lookup and update.

-----------


--be