【问题标题】:Create a generic template for JSON from a rails application从 rails 应用程序为 JSON 创建通用模板
【发布时间】:2013-09-22 03:04:02
【问题描述】:

我正在编写一个带有 AngularJS 前端的 Rails 应用程序,这是我在 connecting rails and angularjs 上编写的教程系列的一部分。这意味着我的 Rails 应用程序仅以 JSON 格式与浏览器通信。

angularjs $http documentation 中,它描述了一个潜在的 json 安全漏洞,其中 json 请求可以嵌入到脚本标签中,加上一些棘手的 jsonp 使用,以允许类似于跨站点脚本攻击的东西。我找到了其他几页,特别是我认为是 described this well 的一页,日期为 2008 年,所以这不是一个新问题。

显然这不是标准 rails json 渲染中的漏洞,因为 rails 默认提供包含数组的对象。但是在使用 angularjs 时,我们似乎设置了 root: false (虽然我不得不承认我找不到我这样做的地方,但它绝对没有给出根节点)。

无论如何,底线是角度文档建议在任何 json 响应前加上 )]}' 前缀,所以:

['one','two']

变成

)]}',
['one','two']

Angular 然后会自动将其再次剥离。

我正在寻找一种优雅的方法。我在 stackoverflow 上看到了很多关于此的问题和答案,但其中大多数要么与更早版本的 rails 相关,在 JSON 处理被更彻底地嵌入之前,要么似乎需要我创建大量样板代码。我正在寻找一种可以应用于应用程序控制器的方法,或者作为一种辅助方法,它可以在任何地方工作。

我目前使用的控制器如下:

class ClubsController < ApplicationController
  respond_to :json

  # GET /clubs.json
  def index
    @clubs = Club.all        
    render json: @clubs
  end
end

这不会调用任何模板 - 渲染操作会跳过模板引擎。我可以通过将渲染线改为:

respond_with json: @clubs

并创建包含模板文件views/clubs/index.json.erb

)]}',
<%= raw(@clubs.to_json) %>

但是我必须为每个控制器上的每个操作创建一个模板,这感觉就像样板。我希望能够将视图/布局/application.json.erb 更改为:

)]}',
<%= yield %>

但这不起作用,因为我们只有在调用 respond_with 时才能得到模板。如果我们调用 respond_with,我们无法将 @clubs 放入响应中 - 所以我们最终得到:

)]}',

作为整个响应。

另一种选择可能是覆盖 as_json 方法来添加我想要的东西,但这似乎有点像大锤。理想情况下,我可以在某个地方引入辅助方法,例如:

render prepend_vulnerability_protection(json: @clubs)

那么,毕竟,有两个问题:

  1. 这是否是一个真正的问题,或者 Rails 是否已经有其他一些保护措施,这意味着我根本不需要担心这个问题
  2. 有没有办法集中执行此操作,还是我需要硬着头皮创建所有样板模板?我可以修改脚手架生成器来做到这一点,所以这不是世界末日,但它确实看起来像很多样板代码

【问题讨论】:

    标签: ruby-on-rails json angularjs


    【解决方案1】:

    所以,目前还没有回复。我将写下我从研究中发现的内容以及我目前的答案。

    首先,我认为这是 Rails 中真正的漏洞。不幸的是,Rails 和 JSON/JSONP 领域最近出现了一些与 Rails 端的 JSON 解析器相关的其他漏洞。这确实淹没了与这个特定 XSS 问题相关的任何谷歌搜索。

    有几种方法可以解决这个问题:

    1. 让您的应用程序只响应 put/post/delete 请求。在集成到 Angular 时,这并不是一个真正的选择——嗯,确实如此,但这意味着覆盖一堆标准行为
    2. 在返回的 JSON 的前面插入一些内容 - 这可以是根节点(rails 3 中的默认 rails 行为,不再在 3.1 中),像 )]}; 这样的闭包,或者像 while (1); 这样的循环; . Angular 期望并且可以处理 )]}',

    我研究过在我的 rails 应用程序中使用 json 模板。您可以使用众多 gem 中的一种来做到这一点,我喜欢 JBuilder (railscast 320) 的外观,但 RABL 可能更强大 (railscast 322)。

    这确实意味着每个控制器上的每个操作都有一个模板。但是,我也刚刚完成了如何拥有rails scaffold those for me automatically 的工作,所以它不像我第一次问这个问题时那么可怕,而且我可以看到一些其他原因,我可能想要更多地控制 json,即从我的应用程序返回。

    话虽如此,我无法立即看到让 JBuilder 预先添加任意字符串的方法 - 它似乎只想准备有效的 JSON(我认为这不是有效的 JSON)。 RABL 看起来可以做到,但它有点复杂。绝对可以通过只使用ERB来完成,但我觉得这样做有点不对。

    我确定的另一个替代方法是 application_controller.rb 中的辅助方法,然后我在每个控制器方法中调用它。这是相当优雅的,我可以很容易地改变我的模板来做到这一点。所以我现在就这样做:

    class ApplicationController < ActionController::Base
      def render_with_protection(json_content, parameters = {})
        render parameters.merge(content_type: 'application/json', text: ")]}',\n" + json_content)
      end
    end
    
    class ClubsController < ApplicationController
      respond_to :json
    
      # GET /clubs.json
      def index
        @clubs = Club.all
    
        render_with_protection @clubs.to_json
      end
    
      # GET /clubs/1.json
      def show
        @club = Club.find(params[:id])
    
        render_with_protection @club.to_json
      end
    
      # POST /clubs.json
      def create
        @club = Club.new(params[:club])
    
        if @club.save
          render_with_protection @club.to_json, {status: :created, location: @club}
        else
          render_with_protection @club.errors.to_json, {status: :unprocessable_entity}
        end
      end
    end
    

    请注意,您还应该在应用程序控制器中包含 CSRF 保护 - 所以将其视为您已经采取的安全预防措施的补充,而不是替代。

    【讨论】:

    • 对这种方法的一个小改进是创建一个after_filter,将所需的字符串添加到响应正文中。可能是这样的:def protect_json; if response.content_type == "application/json"; response.body = ")]}',\n" + response.body; end; end;(这里有一个更好的格式版本:gist.github.com/dwaltrip/fd5e7ef420c401851fd4
    猜你喜欢
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 2013-08-28
    • 2022-07-11
    • 2012-05-14
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    相关资源
    最近更新 更多