Skip to main content

Concurrent sessions control bypass

Need

Enforcement of concurrent session control

Context

  • Usage of Ruby 2.0+ for developing Ruby applications
  • Usage of Rails framework for building web applications

Description

Non compliant code

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :check_concurrent_session

private

def check_concurrent_session
if session[:user_id]
@current_user = User.find(session[:user_id])
else
redirect_to login_url, alert: "Please login to continue"
end
end
end

In the above code, the ApplicationController has a before_action callback named check_concurrent_session. This method checks if the session[:user_id] exists, and if it does, it assigns the @current_user with the user found by the session[:user_id]. If the session[:user_id] does not exist, it redirects the user to the login page.

The vulnerability here is that the check_concurrent_session method only checks if a session exists, but it does not verify if there is already an active session for the same user. This allows a user to bypass the concurrent session control by simply visiting any valid URL in the application when the error that there is already a session started appears.

This can lead to situations where a single user can have multiple active sessions at the same time, potentially leading to security issues such as session hijacking or privilege escalation.

Steps

  • Implement a session management mechanism that keeps track of active sessions.
  • When a user logs in, store the session ID in a secure manner (e.g., encrypted) and associate it with the user.
  • When a user tries to access a URL, check if the session ID is valid and associated with the user.
  • If the session ID is invalid or not associated with the user, redirect the user to the login page with an appropriate message.
  • Ensure that the session ID is invalidated and removed from the session management mechanism when the user logs out or the session expires.
  • Consider implementing additional security measures such as limiting the number of concurrent sessions per user or implementing session timeout.

Compliant code

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :check_concurrent_session

private

def check_concurrent_session
if session[:user_id]
@current_user = User.find(session[:user_id])
if @current_user.session_id != session.id
reset_session
redirect_to login_url, alert: "Your session has expired. Please login again."
end
else
redirect_to login_url, alert: "Please login to continue"
end
end
end

# app/models/user.rb
class User < ApplicationRecord
after_create :generate_session_id

def generate_session_id
self.session_id = SecureRandom.hex(20)
self.save
end
end

The updated code now includes a session management mechanism that keeps track of active sessions.

In the ApplicationController, the check_concurrent_session method now checks if the session ID stored in the user's session matches the session ID associated with the user in the database. If the session IDs do not match, the session is reset and the user is redirected to the login page with an appropriate message.

In the User model, a generate_session_id method is added which is called after a user is created. This method generates a secure random session ID and saves it to the user's record in the database.

This way, even if a user tries to bypass the concurrent session control by going to any valid URL in the application when the error that there is already a session started appears, they will be redirected to the login page because the session IDs will not match.

This solution also ensures that the session ID is invalidated and removed from the session management mechanism when the user logs out or the session expires.

For additional security, consider implementing measures such as limiting the number of concurrent sessions per user or implementing session timeout.

References