Last Update: 2018-07-18 08:00:08 -0700

New Features

  • A sessions plugin has been added that supports encrypted and signed sessions. This plugin is now the recommended way to implement sessions, replacing the previously recommended Rack::Session::Cookie middleware.

    The sessions plugin encrypts session data using the AES-256-CTR cipher, and then signs the encrypted data with HMAC-SHA-256. By doing this, attackers must be able to forge a valid HMAC before they can try to exploit possible weaknesses in the encryption, such as timing attacks during decryption that are dependent on attacker chosen initialization vectors or ciphertext.

    In addition to encryption and a stronger default signature algorithm compared to Rack::Session::Cookie, the sessions plugin has the following benefits:

    • Built in session expiration enabeld by default, to mitigate possible session replay issues (default: 30 days since session creation, 7 days since last update).

    • Padding by default to minimize information leakage due to differing session data sizes (session data padded to a multiple of 32 bytes by default before encryption).

    • Automatic deflate compression of large sessions before encryption (by default if session data is over 128 bytes).

    • JSON is used for serialization instead of Marshal, preventing remote code execution vulnerabilities if the session secret is disclosed. Note that this means that many ruby types do not round trip in the session, such as Symbol and Time instances. This will probably be the largest barrier to adoption, as you need to make sure your application only uses types that round-trip through JSON before you start using the sessions plugin.

    • A plain hash is used for the session, instead of a hash-like object. One consequence of this is that keys in the session are not automatically converted to strings. Rack::Session::Cookie converts session keys to strings for keys at the top level, but not for keys in subhashes.

    • In general sessions are smaller even if deflate compression is not used, despite requiring 16 bytes for the cipher initialization vector. The main reason for this is that the sessions plugin does not set a session id, since one is not needed for cookie sessions.

    • The sessions plugin requires a :secret option be set that is at least 64 bytes, so that users have to make a determined effort to use weak secrets.

    • The HMAC calculation considers the cookie key, so that if the same session secret is used for multiple applications with different cookie keys, an attacker cannot use the session from one application in a different application.

    The sessions plugin ties into the Roda#session method instead of being a rack middleware. This makes it about twice as fast as Rack::Session::Cookie if the session is not accessed. If the session is accessed, the sessions plugin is roughly as fast as Rack::Session::Cookie, even though it uses a stronger HMAC and has to encrypt and decrypt the session.

    Because the sessions plugin is not a middleware, it does not offer session support to other middleware, only to the app itself. If you would like to use the same approach as the sessions plugin uses but would like support for middleware to access the sessions, a roda/session_middleware file has been added. This file contains RodaSessionMiddleware, which is a middleware that can be used by any other Rack app for session support, and which uses a SessionHash class similar to the one used by Rack::Session::Cookie.

    To integrate with other plugins that can optionally use symbols or strings in sessions, the sessions plugin sets the :sessions_convert_symbols application option to true. Other plugins can check for this application option, and if set, should use strings instead of symbols in the session.

    The sessions plugin should be loaded after the flash plugin if both are used in the same application, so that the flash is rotated correctly in the session.

  • The middleware plugin now supports a :handle_result option, which can be any callable object. If set, this object is called with the environment of the request and the rack response after either the Roda app or next middleware returns the rack response. The rack response can be modified by the callable object, and the response (after possible modification) will be returned to the previous middleware. Example:

    plugin :middleware, :handle_result=>(proc do |env, res|
      res[1]['MyHeader'] = 'HeaderValue'
  • The :json_parser and :json_serializer application options are now supported. If set, these options are used for parsing and serializing JSON instead of the default of JSON.parse and .to_json.

Other Improvements

  • RodaRequest initialization is now faster by avoiding 1-2 method calls.

  • typecast_params.Integer in the typecast_params plugin now handles numeric input as long the numeric input does not have fractional parts. This makes it more usable when handling JSON input.

  • If the flash is empty after the request is processed, the flash session key is removed from the session instead of being left as an empty hash. If addition to making the session smaller, this makes the session appear empty if there are no other keys in the session, which works better with the sessions plugin as empty sessions will remove the session cookie completely.

Backwards Compatibility

  • The flash plugin now uses ‘_flash’ instead of :_flash as the session key. When using session middleware that uses Rack::Session::Abstract::SessionHash to store the session (e.g. Rack::Session::Cookie), session keys are converted internally to strings, so this change will not affect you unless you are using alternative session support. Even if your session does treat :_flash different than ‘_flash’ in keys, the plugin will still work because it will try :_flash if there is no value for ‘_flash’. This change was made to support the sessions plugin, which doesn’t convert keys to strings.

  • This DEFAULT_PARSER and DEFAULT_SERIALIZER constants from the the json_parser and json plugins have been removed.