采用自定义“redirect_to”方法的缺点和支持

Drawbacks and support for adopting a custom `redirect_to` method

为了 I am evaluating to use a this way (by @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

不过,我想了解可能存在的缺点,并希望获得采用上述解决方案的支持。

允许在没有任何验证的情况下通过 URL 参数设置重定向目标对您的用户有潜在的危险,因为它使钓鱼尝试变得更容易。

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

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

,许多用户只会看到域部分,而在单击 link 后无法意识到他们最终到达了哪里。

开放 Web 应用程序安全项目 (OWASP) considers this a vulnerability:

Unvalidated redirects and forwards are possible when a web application accepts untrusted input that could cause the web application to redirect the request to a URL contained within untrusted input. By modifying untrusted URL input to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts may have a more trustworthy appearance. Unvalidated redirect and forward attacks can also be used to maliciously craft a URL that would pass the application’s access control check and then forward the attacker to privileged functions that they would normally not be able to access.

在执行重定向之前仔细检查 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)

我同意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("[=10=]\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,只要在 url 中提供 redirect_uri,它就会在会话中设置 url 并自动覆盖。 :)

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