Csrf

io.eezo.http.Csrf
object Csrf

The CSRF token: what a form carries and an unsafe request must return, proving the submission came from a page this application served to this browser and not from a stranger's.

One per session, in a reserved entry beside the flash, so the token is as private as the cookie that carries it and lives as long. Not a second cookie, because the session is already signed and the double submit pattern exists for frameworks whose session is server side; not one per form, because that breaks the back button and a second tab. Sent as is rather than masked: masking defends against BREACH, which needs response compression, and eezo compresses nothing. The day gzip arrives, masking is added here alone.

Both halves happen at dispatch, after a route has matched and before its handler runs, and protect is where: it mints a token into a session that has none, so every request past dispatch has one and reading it cannot fail, and it refuses a POST, PUT, PATCH or DELETE whose form does not return the session's token, derived and handwritten alike. The one place the token does not apply is an API route, where no session takes part: a program holds no session to keep a token in and serves no page to embed one, and the route's Guarded says who may call it instead. A route is an API route only when its handler takes an ApiRequest, so a page that reads the session cannot drop the check without its type saying so. The safe methods, GET, HEAD and OPTIONS, are never checked; a WebSocket upgrade is a GET. Only the form field is read: a header for JavaScript waits until an example fetches.

Attributes

Source
Csrf.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Csrf.type

Members list

Type members

Classlikes

object Token

Attributes

Source
Csrf.scala
Supertypes
class Object
trait Matchable
class Any
Self type
Token.type

Types

opaque type Token

A token, distinct from any other string so that a form cannot be handed the wrong one.

A token, distinct from any other string so that a form cannot be handed the wrong one.

Attributes

Source
Csrf.scala

Value members

Concrete methods

def hidden(token: Token): Html

The one hidden input, for Form.render, the derived show page's delete button and a handwritten form alike.

The one hidden input, for Form.render, the derived show page's delete button and a handwritten form alike.

Attributes

Source
Csrf.scala
def rotated(session: Session): Session

The session with a token of its own, minted now, replacing whatever it held.

The session with a token of its own, minted now, replacing whatever it held.

The one named operation for what happens to the token when a handler rebuilds the session, which in practice means signing in. Published here, rather than left for each login handler to remember, because it is a decision about the application and not about one page: a second login route that forgot to call it would be a session fixation hole nobody would see in a review of the line that was missing.

The decision is to rotate. Signing in rebuilds the session precisely so that nothing an attacker could have planted in the old one survives the privilege change, and the CSRF token is part of the old one. Carrying it across, which is what protect does for an ordinary rebuild, would leave the post-login session holding a value that was minted before eezo knew who this was. The cost is real and small: a form opened in a second tab before signing in carries the old token, and submitting it after signing in is refused once, with the message that says to reload.

protect leaves the result alone, because a session that already holds a token of its own is exactly what its second arm is for.

Attributes

Source
Csrf.scala

Concrete fields

val Field: String

The form field the token travels under, beside _method.

The form field the token travels under, beside _method.

Attributes

Source
Csrf.scala