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

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to