tufao  1.3.0
An asynchronous web framework for C++ built on top of Qt
Tufao::SessionStore Class Referenceabstract

SessionStore class can be used to store data that must persist among different requests. More...

+ Inheritance diagram for Tufao::SessionStore:
+ Collaboration diagram for Tufao::SessionStore:

Public Member Functions

 SessionStore (const SessionSettings &settings=defaultSettings(), QObject *parent=0)
 Constructs a SessionStore object. More...
 
 ~SessionStore ()
 Destructs a SessionStore object.
 
virtual bool hasSession (const HttpServerRequest &request) const =0
 Returns true if a session has been set the request.
 
virtual void removeSession (const HttpServerRequest &request, HttpServerResponse &response)=0
 Removes the session, if any, in the request. More...
 
void resetSession (HttpServerRequest &request) const
 This method removes all cookies matching with this store's settings from request. More...
 
virtual QList< QByteArray > properties (const HttpServerRequest &request, const HttpServerResponse &response) const =0
 Returns a list of set properties to this session. More...
 
virtual bool hasProperty (const HttpServerRequest &request, const HttpServerResponse &response, const QByteArray &key) const =0
 Returns true if the session has a property named key. More...
 
virtual QVariant property (const HttpServerRequest &request, HttpServerResponse &response, const QByteArray &key) const =0
 Returns the property referenced by key in the session, or a null QVariant if the property isn't found. More...
 
virtual void setProperty (const HttpServerRequest &request, HttpServerResponse &response, const QByteArray &key, const QVariant &value)=0
 Sets the property's value referenced by key to value. More...
 
virtual void removeProperty (const HttpServerRequest &request, HttpServerResponse &response, const QByteArray &key)=0
 Removes the property referenced by key in the session, if any. More...
 
void setMacSecret (const QByteArray &secret)
 Sets the secret key of the message authentication code. More...
 

Static Public Member Functions

static SessionSettings defaultSettings ()
 Returns the default settings, used when you don't specify the settings argument in SessionStore constructor. More...
 

Protected Member Functions

QByteArray session (const HttpServerRequest &request) const
 Returns the value of the first cookie that is compatible with this store's properties, as defined in its settings. More...
 
QByteArray session (const HttpServerRequest &request, const HttpServerResponse &response) const
 Returns the value of the first cookie that is compatible with this store's properties, as defined in its settings. More...
 
void setSession (HttpServerResponse &response, const QByteArray &session) const
 Sets a cookie that matches the store's settings in the response object. More...
 
void unsetSession (HttpServerResponse &response) const
 Invalidates the user's session. More...
 

Protected Attributes

SessionSettings settings
 This attribute represents the session's settings. More...
 

Detailed Description

SessionStore class can be used to store data that must persist among different requests.

This is used in web applications, among other, to measure online users, create a shopping list per user and implement a login system.

Using sessions

The session data is stored in properties. You can create a session to any pair of request and response objects and each one will have an independent set of properties.

Note
You can configure a session in any response (including 400-level and 500-level status responses), but the user agent may ignore sessions with a 100-level status line.

To access the session properties, you must pass the request and response objects each time you want to manipulate, but this step can be eliminated using the Session helper class.

Note
You can configure multiple sessions to the same user agent, but only if you are using settings that have different scope. The settings have the same scope if the name, domain and path attributes are equal.
Warning
Despite the possibility of use different stores having SessionSetting objects with the same name attribue, it's recomended to avoid the use of this feature because a store don't have knowledge about the settings of other stores and can easily use the wrong request's cookie in a misconfigured application.

One session only persists for a specified period of time (set through SessionSettings) and the storage details depends upon the class implementing the SessionStore interface.

Note
The use of sessions don't preclude HTTP caches from storing and reusing a response.

Security concerns

Warning
Tufão uses cookies as base for its session support, but cookies have a number of security pitfalls and you should read this section if you are going to use them to store any sensitive information, such as login systems.

To better understand the problems shown in the following subsections, you should understand how cookies works.

A cookie is a piece of text built of a name and a value. The server includes a Set-Cookie header and the user agent will include this cookie in every subsequent request made to this server (when the cookie's scope applies), until the cookie expires.

For example, consider that user U made a request to server S and received the following reply:

HTTP/1.1 200 OK
Set-Cookie: Pants=On
...

Then, user U will include the following header in every request made to S:

Cookie: Pants=On

In SessionStore, cookies are abstracted, and they'll be created as needed by some operations. If it finds a valid cookie, it'll use, but if not, it'll create one.

The "valid cookie" term depends on the implementation used.

Attacks using session fixation

These attacks exploits applications that allows one user fixate another's user cookie. Consider the following code:

bool RequestHandler::handleRequest(Tufao::HttpServerRequest &request,
{
QByteArray username(getUsername(request));
QByteArray userpassword(getUserpassword(request));
if (!performLogin(username, userpassword)) {
loginFail(response);
return true;
}
store.setProperty(request, response, "user", username);
loginSuccess(response);
return true;
}

Try to answer: What could happen with an application running the previous code?

Here is what can happens:

  1. The attacker fixate the victim cookie's value to "I_KNOW_YOUR_ID". Depending on the application store's settings (including implementation and mac secret), an arbitrary value will be rejected by the store, but the attacker may still be able get an usable cookie value (maybe using the application itself to generate one).
  2. The victim access the application and perform the login.
  3. The application's store finds a cookie and reuses it to set the user property.
  4. The attacker uses the victim cookie's value and will have unlimited access to the victim's account.

How an attacker could set the victim cookie value is out of the scope of this document, because there are several techniques to achieve this.

Never trust users input.

How defend against session fixation

In Tufão, you can defend against session fixation calling SessionStore::resetSession to force a new session to be created.

bool RequestHandler::handleRequest(Tufao::HttpServerRequest &request,
{
// To make sense, this line must be inserted before call any session-related
// code
store.resetSession(request);
QByteArray username(getUsername(request));
QByteArray userpassword(getUserpassword(request));
if (!performLogin(username, userpassword)) {
loginFail(response);
return true;
}
store.setProperty(request, response, "user", username);
loginSuccess(response);
return true;
}

Attacks using cross site request forgery

After the user is authenticated in your application, it can perform some actions, such as transfer money to another account. If it's possible to create a script to automate these actions, then it's possible for an attacker to instruct the victim's user agent to perform this action. An example of a scriptable action is the url below:

http://bank.example.com/withdraw?account=foo&amount=1000000&for=bar

First, you shouldn't allow GET methods to mutate the server's state, but, in this case, use POST wouldn't defend our users against attackers. To prevent our actions from being scriptable, we need to require that an action only will be valid if it's originated from our application.

A technique to achieve the behaviour suggested in the previous paragraph is to generate some random data (a challenge token) and associate it with the user's current session. Then, we insert this token in the pages served to this user. The following code shows part of the solution:

bool RequestHandler::handleRequest(Tufao::HttpServerRequest &request,
{
Tufao::Session s(store, request, response);
// ...
// Here, we provide needed data for the application to perform an action.
// In the handler that will perform the action, we ignore requests with
// incorrect tokens.
response << /* ... */
<< "<input type=\"hidden\" name=\"CSRFToken\" value=\""
<< s["CSRFToken"] << "\">"
<< /* ... */;
response.end();
return true;
}

To generate a challenge token, you can use QUuid. To improve this design even further, you can regenerate the token and name parameters for each request.

Yet another option is to requiring the client to provide authentication data in the same request used to perform the action, but this solution add a usability issue.

Other problems

As defined in RFC 6265 and implemented in browsers, cookies don't provide strong confidentiality. They don't provide:

  • Isolation by port. A service running on one port has access to cookies of a service running on another port of the same host.
  • Isolation by scheme.
  • Full-isolation by path. User agents won't send cookies from one path to another, but it's still possible for a service to set a cookie of a service running in another path of the same host.

Cookies don't provide strong integrity also.

You can avoid the previous problems by signing or encrypting cookies, but it'll still be possible for an attacker to perform a replay attack.

You can signing cookies in Tufão using SessionStore::setMacSecret and hide the session data using a SessionStore implementation that don't store its data in the cookie.

See SessionSettings for more information.

Implementing your own storage backend

If the implementations shipped with Tufão don't supply your needs, you can provide your own storage backend. To do that, you must implement the pure virtual methods of this class. They give you full access to the cookies returned from the user agent and included in the response objects.

The protected methods may help you process and include the cookies. They already take care of the boring task of read and comply with the SessionSettings object and sign the cookies.

This flexible design is what allows you, among other, implement a pure cookie based storage mechanism.

Security concerns

If the application request your store to manipulate some property and the request provides a session's id that don't correspond to any session in the store, you should not reuse this value as an id. You should ignore it and create a new one. This design may add restrictions to some forms of attacks.

See also
SessionSettings Session
Since
0.4

Constructor & Destructor Documentation

Tufao::SessionStore::SessionStore ( const SessionSettings settings = defaultSettings(),
QObject *  parent = 0 
)
explicit

Constructs a SessionStore object.

settings Specifies session parameters.
parent is passed to the QObject constructor.

Member Function Documentation

static SessionSettings Tufao::SessionStore::defaultSettings ( )
static

Returns the default settings, used when you don't specify the settings argument in SessionStore constructor.

The default values to these settings are:

  • timeout: 15
  • httpOnly: true
  • key: "SID"
  • path: "/"
  • secure: false

If you use more than one store in the same application, you need to set a unique key to each one.

virtual bool Tufao::SessionStore::hasProperty ( const HttpServerRequest request,
const HttpServerResponse response,
const QByteArray &  key 
) const
pure virtual

Returns true if the session has a property named key.

The session is represented by the pair request, response.

Implemented in Tufao::SimpleSessionStore.

virtual QList<QByteArray> Tufao::SessionStore::properties ( const HttpServerRequest request,
const HttpServerResponse response 
) const
pure virtual

Returns a list of set properties to this session.

The session is represented by the pair request, response.

Implemented in Tufao::SimpleSessionStore.

virtual QVariant Tufao::SessionStore::property ( const HttpServerRequest request,
HttpServerResponse response,
const QByteArray &  key 
) const
pure virtual

Returns the property referenced by key in the session, or a null QVariant if the property isn't found.

The session is represented by the pair request, response.

Implemented in Tufao::SimpleSessionStore.

virtual void Tufao::SessionStore::removeProperty ( const HttpServerRequest request,
HttpServerResponse response,
const QByteArray &  key 
)
pure virtual

Removes the property referenced by key in the session, if any.

The session is represented by the pair request, response.

Implemented in Tufao::SimpleSessionStore.

virtual void Tufao::SessionStore::removeSession ( const HttpServerRequest request,
HttpServerResponse response 
)
pure virtual

Removes the session, if any, in the request.

response The object used to invalidate the user's cookie.

Implemented in Tufao::SimpleSessionStore.

void Tufao::SessionStore::resetSession ( HttpServerRequest request) const

This method removes all cookies matching with this store's settings from request.

The purpose is hide the cookies from other pieces of code handling request.

Call this if you need to reset the session id.

QByteArray Tufao::SessionStore::session ( const HttpServerRequest request) const
protected

Returns the value of the first cookie that is compatible with this store's properties, as defined in its settings.

Warning
If you get a session that doesn't exists, you MUST NOT reuse this id. Create a new one and discard this, or you will introduce a session fixation vulnerability.
QByteArray Tufao::SessionStore::session ( const HttpServerRequest request,
const HttpServerResponse response 
) const
protected

Returns the value of the first cookie that is compatible with this store's properties, as defined in its settings.

Besides searching in the request's headers named as "Cookie", search in response's headers named as "Set-Cookie". This allows you to manipulate sessions as soon as they are set.

Warning
If you get a session that doesn't exists, you MUST NOT reuse this id. Create a new one and discard this, or you will introduce a session fixation vulnerability.
void Tufao::SessionStore::setMacSecret ( const QByteArray &  secret)

Sets the secret key of the message authentication code.

If set, it will protects the cookies integrity and authenticity.

Warning
The value used here must remain secret.

The algorithm used by Tufão is HMAC + SHA1.

virtual void Tufao::SessionStore::setProperty ( const HttpServerRequest request,
HttpServerResponse response,
const QByteArray &  key,
const QVariant &  value 
)
pure virtual

Sets the property's value referenced by key to value.

The session is represented by the pair request, response.

Implemented in Tufao::SimpleSessionStore.

void Tufao::SessionStore::setSession ( HttpServerResponse response,
const QByteArray &  session 
) const
protected

Sets a cookie that matches the store's settings in the response object.

Note
It will also renew cokie's lifetime.
Warning
Call this method more than once to the same response object has undefined behaviour.
Note
If you need to create a new unique identifier, but don't know how, check the QUuid class.
See also
SessionSettings::cookie
void Tufao::SessionStore::unsetSession ( HttpServerResponse response) const
protected

Invalidates the user's session.

This is done by setting the expiration date to the past, as specified by RFC 6265.

Member Data Documentation

SessionSettings Tufao::SessionStore::settings
protected

This attribute represents the session's settings.

It will be used in the operations involving cookie's handling and is set automatically in SessionStore's constructor.


The documentation for this class was generated from the following file: