Prevention of unauthorized privilege escalation
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
@users = User.all
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email, :password, :role)
end
end
The above code is a typical UsersController in a Ruby on Rails application. There's a major security vulnerability in the
update
action and
user_params
method.
In the
update
action, any user can update any other user's details because there is no check to see if the user making the request is allowed to update the details of the user specified by
params[:id]
.
The
user_params
method is where the parameters for updating a user are whitelisted. The problem here is that the
:role
parameter is whitelisted. This means that any user can change their role to any value they want, including 'admin'. This allows any authenticated user to escalate their privileges to admin level.
This can be exploited by a malicious user to gain admin access and perform actions they are not authorized to do.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_action :check_role, only: [:update]
# GET /users
# GET /users.json
def index
@users = User.all
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Check if the current user has the right role to perform the action
def check_role
unless current_user.admin?
redirect_to root_path, alert: 'You do not have permission to perform this action.'
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:email, :password)
end
end
The updated code includes a new
before_action
filter,
check_role
, which is only applied to the
update
action. This filter checks if the current user has the admin role before allowing them to perform the update action. If the user is not an admin, they are redirected to the root path with an alert message.
The
user_params
method has also been updated to remove
:role
from the list of permitted parameters. This prevents standard users from being able to escalate their privileges by changing their role.
This code ensures that only users with the appropriate role or privilege can access the
update
action, effectively mitigating the privilege escalation vulnerability.