首页 > 解决方案 > 如何防止在 Rails 中篡改 URL

问题描述

我的 Rails 学生规划器应用程序存在一些关于 URL 篡改的问题。我相信他们可能都有类似的解决方案,但我遇到了困难。

查看students/:id/assignments/:id作业assignments#show( '想只是重定向回他们的主页。

同样,作业的编辑页面 ( students/:id/assignments/:id/edit)、课程 ( students/:id/courses/:id) 和课程的编辑页面 ( students/:id/courses/:id/edit) 也会发生这种情况。有时我会Assignments#edit在查看作业的编辑页面时收到“ArgumentError in”。

我相信这些应该能够在我的控制器中得到纠正,所以我已经包含了我的assignments_controllerand courses_controller.

分配_控制器:

class AssignmentsController < ApplicationController
  before_action :require_logged_in
  before_action :set_student

  def new
    if @student && @student.id == current_student.id
      @assignment = Assignment.new
      @courses = Course.where(student_id: current_student.id)
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users assignments.'
    end
  end

  def create
    @assignment = Assignment.new(assignment_params)
    @assignment.student_id = current_student.id if current_student
    @courses = Course.where(student_id: current_student.id)

    if @assignment.save
      redirect_to student_assignments_path(@student)
    else
      render :new
    end
  end

  def index
    if @student && @student.id == current_student.id
      @assignments = Assignment.where(student_id: current_student.id)
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users assignments.'
    end
  end

  def show
    #student = Student.find_by(id: params[:student_id])
    if @student && @student.id == current_student.id
      #@assignment = student.assignments.find_by(id: params[:id])
      @assignment = Assignment.find_by(id: params[:id])
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users assignments.'
    end
  end

  def edit
    if @student && @student.id == current_student.id
      @assignment = Assignment.find_by(id: params[:id])
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users assignments.'
    end
  end

  def update
    student = Student.find_by(id: params[:student_id])
    @assignment = Assignment.find_by(id: params[:id])
    @assignment.update(params.require(:assignment).permit(:title, :due_date))
    redirect_to student_assignment_path(student, @assignment)
  end

  def destroy
    @student = Student.find_by(id: params[:student_id])
    @assignment = Assignment.find_by(id: params[:id]).destroy
    redirect_to student_path(@student), notice: 'Assignment was successfully completed.'
  end

  private

    def assignment_params
      params.require(:assignment).permit(:title, :due_date, :course_id, :student_id)
    end

    def set_student
      @student = Student.find_by(id: params[:student_id])
    end
end

Courses_controller:

class CoursesController < ApplicationController
  before_action :require_logged_in
  before_action :set_student

  def new
    if @student && @student.id == current_student.id
      @course = Course.new
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users courses.'
    end
  end

  def create
    if @student && @student.id == current_student.id
      @course = Course.create(course_params)
      @course.student_id = params[:student_id]

      if @course.save
        redirect_to student_courses_path(@student)
      else
        render :new
      end
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users courses.'
    end
  end

  def index
    if @student && @student.id == current_student.id
      @courses = Course.where(student_id: current_student.id)
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users courses.'
    end
  end

  def show
    @student = Student.find_by(id: params[:student_id])
    if @student && @student.id == current_student.id
      @course = @student.courses.find_by(id: params[:id])
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users courses.'
    end
  end

  def edit
    if @student && @student.id == current_student.id
      @course = Course.find_by(id: params[:id])
    else
      redirect_to student_path(current_student), error: 'Sorry, you can\'t view another Users courses.'
    end
  end

  def update
    student = Student.find_by(id: params[:student_id])
    @course = Course.find_by(id: params[:id])
    @course.update(params.require(:course).permit(:course_name))
    redirect_to student_course_path(student, @course)
  end

  def destroy
    @student = Student.find_by(id: params[:student_id])
    @course = Course.find_by(id: params[:id]).destroy
    redirect_to student_path(@student), notice: 'Course was successfully deleted.'
  end

  private

    def course_params
      params.require(:course).permit(:course_name)
    end

    def set_student
      @student = Student.find_by(id: params[:student_id])
    end
end

标签: ruby-on-railsruby

解决方案


这条线是所有问题的根源:

@assignment = Assignment.find_by(id: params[:id])

这是一个巨大的错误。我认为您永远不会使用顶级模型来获取必须保护的记录。这段代码的失败状态是用户看到了一切。这是一个无法通过使用访问控制列表修补它来解决的问题。这些可能并非每次都正确应用,有人可能会发现漏洞。

相反,你这样做:

@assignment = @student.assignments.find_by(id: params[:id])

最坏的情况是您收到未找到的错误。任何人都无法通过破解 URL 来绕过这一点。这里的失败状态是没有找到记录。

如果您希望您的 URL 能够抵抗篡改,您还需要使用非顺序标识符。在 MySQL 上,通常最好专门为此目的创建一个辅助列,例如称为paramor slugor ident,无论您喜欢什么,并用随机且无害的东西填充它,例如:

 before_validation :assign_slug

 def assign_slug
   self.slug ||= SecureRandom.uuid
 end

在您的架构中索引的位置以便快速检索。你有学生关系的地方:

 add_index :assignments, [ :student_id, :slug ]

Postgres 允许使用可能很冗长的 UUID 主键,但不允许人们修补和试验以公开信息。你真的不能“猜测”一个随机的 UUID 值。


推荐阅读