Skip to main content

Session Fixation

Need

Prevention of session fixation attacks

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])
if cookies[:user_id]
session[:user_id] = cookies[:user_id]
else
session[:user_id] = user.id
end
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 the create method of SessionsController, the application is trying to authenticate a user based on their email and password. If the user is authenticated, the application checks if there is a user_id cookie. If this cookie exists, the application sets the session[:user_id] to the value of the cookie. If the cookie does not exist, it sets the session[:user_id] to the user's ID.

The problem with this code is that it allows for session fixation. An attacker can set the user_id cookie to a value they know and then trick a user into logging in. Since the application sets the session[:user_id] to the value of the user_id cookie if it exists, the attacker can hijack the user's session.

In the destroy method, the application sets session[:user_id] to nil, effectively logging the user out. However, this does not change the user_id cookie, so an attacker can still hijack the session if they know the value of the user_id cookie.

This vulnerability can be exploited by an attacker to gain unauthorized access to a user's session, potentially leading to unauthorized actions and data exposure.

Steps

  • Generate a new session ID for the user after a successful login.
  • Invalidate the existing session ID and create a new session ID for the user upon logout.
  • Ensure that the session ID is not predictable or easily guessable.
  • Implement secure session management practices, such as using secure cookies and enabling secure flag for cookies.
  • Consider implementing additional security measures like session expiration and session timeout.
  • Regularly review and update the session management code to address any new vulnerabilities or best practices.

Compliant code

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

if user && user.authenticate(params[:password])
reset_session
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
reset_session
redirect_to root_url, notice: "Logged out!"
end
end

The updated code now includes a call to reset_session in both the create and destroy methods. This ensures that a new session ID is generated for the user after a successful login and that the existing session ID is invalidated upon logout.

In the create method, reset_session is called before setting session[:user_id] = user.id. This ensures that a new session ID is generated for the user after they successfully authenticate.

In the destroy method, reset_session is called to invalidate the existing session ID when the user logs out.

This change addresses the session fixation vulnerability by ensuring that the session ID changes after login and logout, making it harder for an attacker to hijack the user's session.

In addition to this, it's recommended to implement secure session management practices, such as using secure cookies and enabling the secure flag for cookies. Also, consider implementing additional security measures like session expiration and session timeout. Regularly review and update the session management code to address any new vulnerabilities or best practices.

References