Secure session management and protection against CSRF Fixation
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.
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.