Rails 3查询字符串

15

这里出现了非常奇怪的错误。我们正确地收到了一个请求,形如 "/users/8788234"。

在Rails中,我们调用:

redirect_to(:controller => 'login', :from_external_page => true, :on_login => request.env['REQUEST_URI']) and return

在Rails日志中我们看到了(正如我们所预期的):

重定向至

https://sampleapp.com/login?from_external_page=true&on_login=%2Fusers%2F8788234

但是接下来我们从该IP看到的请求具有查询字符串值混乱的情况:

Started GET "/login?from_external_page=gehr&on_login=%2Shfref%2S8788234" for xx.xxx.xxx.xxx at yyyy-mm-dd

这会使查询字符串的值变得毫无意义,并导致以下错误:

ArgumentError: invalid %-encoding

“%2F”已被更改为无效的“%2S”。查询字符串中每个键值对的每个值都向右移动13个字符。每次我们看到此情况时,用户代理都会显示:“Mozilla/5.0(兼容;MSIE 9.0;Windows NT 6.1;WOW64;Trident/5.0)”,但我们也看到用户代理成功地浏览了应用程序。有人见过类似的情况吗?http://www.whatismybrowser.com/告诉我这个用户代理是在运行Windows 7上的IE9,但我们无法再现这个错误。


3
你是否收到用户关于此事的报告,还是只在日志中看到这些事件?如果没有用户报告,那么这些可能是模糊测试的结果吗? - Coenwulf
有趣!问题是他们正在从电子邮件中跟随有效的注册链接。是否存在一种模糊攻击,可以感染电子邮件,然后使用凯撒密码? - kddeisz
4
这似乎不是Rails错误,而是MSIE9中的错误。重定向使用302状态代码发送回浏览器,然后浏览器会跟随该链接。为确保情况,请使用Wireshark(1)记录网络流量,特别检查302响应是否未损坏以及随后的GET请求实际上是否包含额外的文本(它不是由您的Web服务器或另一个Rack插件添加的)。如果在网络上找不到它,请尝试使用其他服务器(而不是Webrick)。 (1) 是的,我知道这很困难,因为您无法按需复制错误。但这是唯一确定的方法... - cliffordheath
1
听起来像是编码问题,你的字符串值被编码为rot13 / caesar密码,就像@kdeisz所建议的那样。Rails和浏览器可能在底层进行一些奇怪的编码协商。非常仔细地检查你的头文件和字符串编码。你使用的Ruby版本也可能会产生影响。1.9引入了适当的字符串编码。只有值(即字符串),而不是符号受到影响,这表明它是在urlencoding之前完成的。 - sj26
我们还看到类似 ROT13 编码的查询字符串在请求中,只有一个 URL 在我们系统生成的电子邮件中被引用。同样,UA 是“Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)”。目前我所知道的就是这些,但我们的 Web 应用程序是 Java,而不是 Rails。 - tx802
2个回答

1
这肯定是编码问题。我现在正在使用Rails 4,以下是我的当前项目的示例查询字符串。请注意,查询字符串中的第一个参数是“utf8 = ✓”,而您的查询字符串中缺少该参数。
profiles?utf8=✓&min_age=1&max_age=99&min_height=1&max_height=6&min_weight=1&max_weight=400

尝试在文件开头添加 "# 在文件开头添加编码:UTF-8"

0
如果有人感兴趣,我最终只是编写了一个中间件解析器来处理嵌套查询字符串,如果13个字符的移位可以使其有效,则将其旋转回来。我不会接受它作为答案,希望有人能够真正回答这个问题。无论如何,这是我的方法:
# used to parse out invalid query strings, attempt to fix them, and
# handle the resulting query appropriately
class InvalidEncodingParser

  # creates the middleware instance with a reference to the application
  def initialize(app)
    @app = app
  end

  # parse out bad queries, attempt to fix
  def call(env)
    begin
      # no need to scrub_nested_query if QUERY_STRING is blank or unescapable
      env['QUERY_STRING'].blank? or Rack::Utils.unescape(env['QUERY_STRING'])
    rescue ArgumentError
      env['QUERY_STRING'] = scrub_nested_query(env['QUERY_STRING'])
    end
    @app.call(env)
  end

  private

  # attempts to unescape both the query params and rot13 of the query params
  def scrub_nested_query(query_string = '')
    params = []
    (query_string || '').split(/[&;] */n).each do |param|
      if valid_query_param?(param)
        params << param
      elsif valid_query_param?(rotate_13_characters(param))
        params << rotate_13_characters(param)
      else
        raise ArgumentError
      end
    end
    params.join('&')
  end

  # applies a caesar cipher with a shift of 13 characters to the value of the
  # query key, value pair if the given param contains an equal sign
  def rotate_13_characters(param)
    key, value = param.split('=', 2)
    value.nil? ? param : (key + '=' + value.tr('A-Za-z', 'N-ZA-Mn-za-m'))
  end

  # attempts to unescape the param, returns false if it fails
  def valid_query_param?(param)
    param.blank? or Rack::Utils.unescape(param).present?
  rescue ArgumentError
    false
  end

end

...然后将以下内容添加到我的application.rb文件的底部

# Use invalid encoding middleware to parse out invalid encodings in the query string 
# of the url and handle them appropriately 
config.middleware.insert_before(ActionDispatch::ParamsParser, 'InvalidEncodingParser') 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接