缺点和支持采用自定义`redirect_to`方法

时间:2015-01-04 10:14:59

标签: ruby-on-rails ruby redirect ruby-on-rails-4 routing

为了redirect users to a custom web page each time params contain a redirect_uri value我正在评估使用custom redirect_to method这种方式(@kiddorails):

class ApplicationController < ActionController::Base
  def custom_redirect_to(url)
    redirect_to (params[:redirect_uri].present? ? params[:redirect_uri] : url)
  end
end

def create
  @post = Post.new(params)
  if @post.save
    custom_redirect_to @post
  else
    render 'new'
  end
end

但是,我想知道可能的缺点,并获得采用上述解决方案的支持。

2 个答案:

答案 0 :(得分:1)

允许通过网址参数设置重定向目标而不进行任何验证对您的用户来说可能会有危险,因为它可以让捕鱼尝试更轻松。

攻击者可以向您的用户发送链接,例如

http://my-trusted-site.com/some/action/path?redirect_uri=malicious-site-that-looks-like-trusted-site.com

,许多用户只会看到域部分,并且无法在点击该链接后意识到他们最终的位置。

Open Web Application Security Project(OWASP)因此considers this a vulnerability

  

Web应用程序可以实现未经验证的重定向和转发   接受可能导致Web应用程序的不受信任的输入   将请求重定向到不受信任的输入中包含的URL。通过   攻击者可能会将不受信任的URL输入修改为恶意站点   成功启动网络钓鱼诈骗并窃取用户凭据。   因为修改后的链接中的服务器名称与   原始网站,网络钓鱼尝试可能会更值得信赖   出现。也可以使用未经验证的重定向和转发攻击   恶意制作一个可以通过应用程序访问的URL   控制检查然后将攻击者转发给特权函数   他们通常无法访问。

在执行重定向之前仔细检查redirect_uri参数非常重要。

但是,由于正确的验证很棘手并且容易出错,因此更好的想法是不首先接受URI参数,而是允许某些关键字指定用户将被重定向的位置。

class ApplicationController < ActionController::Base

    protected

  def dynamic_redirect_to(default_route, options)
    options.stringify_keys!

    redirect_to options.fetch(params[:redirect_to], default_route)
  end
end

您现在可以提前定义任意数量的允许关键字,这些关键字可以用作?redirect_to=参数:

def create
  @post = Post.new(params)
  if @post.save
    dynamic_redirect_to post_path(@post), edit: edit_post_path(@post)
  else
    render 'new'
  end
end

如果设置了?redirect_to=edit,则会将用户重定向回编辑页面。如果未设置参数或包含未指定的关键字,则会将其重定向为默认post_path(@post)

答案 1 :(得分:1)

我同意janfoeh上面所说的话。 但为了实现您的要求,我在Rails code of redirection中进行了攻击,以使其更简单。

使用以下命令创建文件 config / initializers / redirecting.rb

require 'abstract_controller/base'
module ActionController
  module Redirecting
    extend ActiveSupport::Concern

    include AbstractController::Logger
    include ActionController::RackDelegation
    include ActionController::UrlFor

    def _compute_redirect_to_location(options)
      if redirect_url = session.delete(:redirect_uri)
        options = redirect_url
      end
      case options
        # The scheme name consist of a letter followed by any combination of
        # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
        # characters; and is terminated by a colon (":").
        # See http://tools.ietf.org/html/rfc3986#section-3.1
        # The protocol relative scheme starts with a double slash "//".
      when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
        options
      when String
        request.protocol + request.host_with_port + options
      when :back
        request.headers["Referer"] or raise RedirectBackError
      when Proc
        _compute_redirect_to_location request, options.call
      else
        url_for(options)
      end.delete("\0\r\n")
    end
  end
end

app / controllers / application_controller.rb

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.

  before_action :set_redirect_uri
  protect_from_forgery with: :exception

  def set_redirect_uri
    session[:redirect_uri] = params[:redirect_uri] if params[:redirect_uri].present?
  end
end

而且瞧!现在,您可以继续使用原始redirect_to,并且只要在网址中提供redirect_uri,它就会在会话中设置该网址并自动覆盖。 :)

注意:仅在调用session[:redirect_uri]时才会清除redirect_to。您可以根据需要轻松修改该行为以重置此会话。