采用自定义“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,例如
,许多用户只会看到域部分,而在单击 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]
。您可以根据需要轻松修改该行为以重置此会话。
为了
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,例如
,许多用户只会看到域部分,而在单击 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]
。您可以根据需要轻松修改该行为以重置此会话。