Insecure session management - Ruby

Insecure session management - Ruby

Need

Secure session management

Context

  • Usage of Ruby 2.5.0 as the programming language for development
  • Usage of Ruby on Rails for building web applications

Description

Non compliant code

        class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])

    if user && user.authenticate(params[:password])
      session[:user_id] = user.id
      redirect_to root_url, notice: "Logged in!"
    else
      flash.now.alert = "Email or password is invalid"
      render "new"
    end
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url, notice: "Logged out!"
  end
end
        
        

In this Ruby on Rails application, a session is created when a user logs in. The user's ID is stored in the session object, which is a hash that Rails provides to track data over multiple requests from the same user. When a user logs out, their user ID is simply set to nil in the session hash.

However, this code does not properly manage the session token. The session token is not expired or invalidated when the user logs out. This means that if an attacker were to gain access to a session token, they could use it to make requests on behalf of the user even after the user has logged out. This is a significant security vulnerability, as it could allow an attacker to impersonate a user and perform actions on their behalf. This type of attack is known as session hijacking or session sidejacking.

Steps

  • Implement session expiration mechanism
  • Set an expiration time for the session token
  • Invalidate the session token when the user logs out
  • Use secure session storage mechanism
  • Encrypt the session token to prevent tampering
  • Implement session token regeneration after login

Compliant code

        class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])

    if user && user.authenticate(params[:password])
      session[:user_id] = user.id
      session[:expires_at] = Time.current + 2.hours
      redirect_to root_url, notice: "Logged in!"
    else
      flash.now.alert = "Email or password is invalid"
      render "new"
    end
  end

  def destroy
    session[:user_id] = nil
    session[:expires_at] = nil
    redirect_to root_url, notice: "Logged out!"
  end

  private

  def check_session_expiration
    if session[:expires_at] && session[:expires_at] < Time.current
      session[:user_id] = nil
      session[:expires_at] = nil
      flash[:alert] = "Your session has expired. Please log in again."
      redirect_to login_url
    end
  end
end
        
        

The updated code now includes a session expiration mechanism. When a user logs in, an expiration time is set for the session token ( session[:expires_at] = Time.current + 2.hours). This means that the session will automatically expire 2 hours after it was created, even if the user does not manually log out.

In the destroy method, which is called when a user logs out, the session token and its expiration time are both set to nil. This invalidates the session token, so it can no longer be used to post requests.

A new private method check_session_expiration is added to check if the session has expired every time a request is made. If the session has expired, the user is logged out and redirected to the login page with a flash message informing them that their session has expired.

This code does not yet include secure session storage, session token encryption, or session token regeneration after login. These features would require additional changes to the application's configuration and possibly the use of additional gems.

References