Skip to main content

Insecure session management - CSRF Fixation

Need

Secure session management and protection against CSRF Fixation

Context

  • Requirement of Ruby 2.0 or higher for running the application
  • Usage of Rails framework for building web applications

Description

Non compliant code

class TransactionsController < ApplicationController
before_action :set_transaction, only: [:show, :edit, :update, :destroy]

# POST /transactions
def create
@transaction = Transaction.new(transaction_params)

respond_to do |format|
if @transaction.save
format.html { redirect_to @transaction, notice: 'Transaction was successfully created.' }
format.json { render :show, status: :created, location: @transaction }
else
format.html { render :new }
format.json { render json: @transaction.errors, status: :unprocessable_entity }
end
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_transaction
@transaction = Transaction.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def transaction_params
params.require(:transaction).permit(:amount, :receiver_id)
end
end

The issue lies in the create method of the TransactionsController. The method does not validate the authenticity of the request, which allows for CSRF (Cross-Site Request Forgery) attacks.

In a CSRF attack, an attacker tricks a victim into submitting a malicious request. It uses the identity and privileges of the victim to perform an undesired function on their behalf. In this case, an attacker can create a button with the content of a request and trick a user running a transaction to receive the app push notification and complete the request.

The vulnerability is further compounded by the fact that the Authorization header, which is the public key, is always the same for payment links. This means that once an attacker has the public key, they can easily generate a CSRF attack.

In the current code, the application trusts all incoming requests and processes them without any check for CSRF tokens. This is a major security flaw that needs to be fixed.

Steps

  • Implement CSRF protection in the application.
  • Use a CSRF token to validate the authenticity of requests.
  • Generate a unique CSRF token for each session and include it in the forms and requests.
  • Verify the CSRF token on each request to ensure it matches the one generated for the session.
  • Ensure that the CSRF token is not exposed in URLs or publicly accessible locations.
  • Consider using the built-in CSRF protection mechanisms provided by the Ruby on Rails framework.

Compliant code

class TransactionsController < ApplicationController
before_action :set_transaction, only: [:show, :edit, :update, :destroy]
protect_from_forgery with: :exception

# POST /transactions
def create
@transaction = Transaction.new(transaction_params)

respond_to do |format|
if @transaction.save
format.html { redirect_to @transaction, notice: 'Transaction was successfully created.' }
format.json { render :show, status: :created, location: @transaction }
else
format.html { render :new }
format.json { render json: @transaction.errors, status: :unprocessable_entity }
end
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_transaction
@transaction = Transaction.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def transaction_params
params.require(:transaction).permit(:amount, :receiver_id)
end
end

The updated code includes the protect_from_forgery with: :exception directive at the top of the TransactionsController class. This directive is a built-in Rails method that provides CSRF protection. It generates a unique CSRF token for each session and includes it in forms and AJAX requests.

When a form is submitted or an AJAX request is made, Rails will check the CSRF token in the request against the one generated for the session. If they match, the request is allowed to proceed. If they don't match, an exception is raised and the request is halted.

This CSRF protection mechanism ensures that only forms and AJAX requests originating from the same site can be submitted to the server, preventing CSRF attacks.

The protect_from_forgery with: :exception directive should be included in all controllers that handle forms or AJAX requests. If it's not included, the application is vulnerable to CSRF attacks.

In addition to this, it's important to ensure that the CSRF token is not exposed in URLs or other publicly accessible locations. This is because if an attacker can obtain the CSRF token, they can bypass the CSRF protection mechanism.

By implementing these changes, the application is now protected against CSRF attacks.

References