简体   繁体   English

使用 CSP 的 nonce 策略响应 *.js.erb

[英]Respond with *.js.erb using nonce strategy for CSP

I'm implementing a CSP using rails 5.2.1 content security policy DSL.我正在使用 Rails 5.2.1 内容安全策略 DSL 实现 CSP。 I've got my policy set to something like:我已将我的政策设置为:

Rails.application.config.content_security_policy do |policy|
  policy.default_src :self, :https
  policy.connect_src :self
  #...
  policy.script_src  :self
end

# If you are using UJS then enable automatic nonce generation
Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }

I also have <%= csp_meta_tag %> in my application.html.erb我的application.html.erb中也有<%= csp_meta_tag %> .html.erb

At this point I need to add a nonce: true flag to any inline scripts for these to satisfy the policy.此时,我需要向任何内联脚本添加一个nonce: true标志,以满足该策略。 I've done this and it works as expected.我已经这样做了,它按预期工作。 However, I'm having trouble maintaining existing AJAX style functionality.但是,我无法维护现有的 AJAX 样式功能。 For example, I have something like (note the remote: true ):例如,我有类似的东西(注意remote: true ):

# index.html.erb
<%= link_to create_object_path, id: "#{object.code}",method: :post, remote: true do %>
<button type="button">Create object</button>
<% end %>

In my controller在我的 controller

def create
  @object = current_user.object.create
  respond_to do |format|
    if @object
      format.js
    else
      redirect_back
      format.html
    end
  end
end

In my *.js.erb file在我的*.js.erb文件中

$("#<%= @object.service.id %>").text("Added!");

The object is successfully created but I believe the policy is blocking the above "Added" success message that I add to the DOM. object 已成功创建,但我相信该策略阻止了我添加到 DOM 的上述"Added"成功消息。 I have not seen any errors in the console so I'm not sure where to go from here.我没有在控制台中看到任何错误,所以我不确定从这里到 go 的位置。

My understanding in this scenario is script tags are temporarily inserted with the contents of the *.js.erb file and these script tags do not contain the nonce.在这种情况下,我的理解是脚本标签是临时插入*.js.erb文件的内容,并且这些脚本标签不包含随机数。 Or, it is a mismatch.或者,这是一个不匹配。

I've been stuck on how to troubleshoot from here.我一直坚持如何从这里进行故障排除。 Any guidance here is much appreciated even if different architectural pattern for sending data to client is the way forward.非常感谢这里的任何指导,即使向客户端发送数据的不同架构模式是前进的方向。 Thanks in advance.提前致谢。

I ran into a similar issue.我遇到了类似的问题。 In my case, it didn't refuse to run the js.erb file itself but rather scripts in templates nested within that file through the use of render .在我的例子中,它并没有拒绝运行 js.erb 文件本身,而是拒绝运行通过使用render嵌套在该文件中的模板中的脚本。 So, this answer may have limited utility to your specific case.因此,此答案对您的特定案例的实用性可能有限。 That said, I did try to reproduce your issue using Rails version 6.1.1 and couldn't.也就是说,我确实尝试使用 Rails 版本 6.1.1 重现您的问题,但没有成功。

However, even if you get past the initial hurdle of getting just your.js.erb file to run, you can still run into the issue of nested scripts: if your.js.erb file render sa template that contains a script tag.然而,即使您克服了让 .js.erb 文件运行的初始障碍,您仍然会遇到嵌套脚本的问题:如果您的 .js.erb 文件render包含script标签的模板。 That script won't run because the request from which it originated assigns it a new nonce, which won't match the nonce in the meta tag.脚本不会运行,因为发出它的请求为其分配了一个新的随机数,该随机数与元标记中的随机数不匹配。

So, to those coming here from a search engine as I did, here's the general strategy I pursue to get async embedded JS working with CSP for that nested case and assuming the.js.erb file itself runs.所以,对于那些像我一样从搜索引擎来到这里的人,这是我追求的一般策略,让异步嵌入式 JS 与 CSP 一起工作,用于嵌套的情况,并假设 .js.erb 文件本身运行。 Using your case as an example:以您的案例为例:

  1. Send the nonce along in the AJAX request.在 AJAX 请求中发送随机数。 I suppose you won't get around writing some custom JS to send the request.我想您不会绕过编写一些自定义 JS 来发送请求。 Something like:就像是:

     document.getElementById('<%= object.code %>').addEventListener('click', e => { e.preventDefault(); // So we don't send two requests fetch('<%= create_object_path %>', { method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify({ nonce: document.getElementsByName('csp-nonce')[0].content }) }); });

    This sends the nonce from the meta tag to the server in the form of a nonce parameter.这会将元标记中的随机数以nonce参数的形式发送到服务器。

    You may need to remove remote: true from your link for this to work.您可能需要从您的链接中删除remote: true才能正常工作。 And of course, this script will itself need to be 'nonced' or else it won't run!当然,这个脚本本身需要被“nonced”,否则它不会运行!

  2. Assign the nonce to a @nonce instance variable in the controller:将 nonce 分配给 controller 中的@nonce实例变量:

     @nonce = params[:nonce]
  3. Wherever you render scripts, do:无论您在何处呈现脚本,请执行以下操作:

     <%= javascript_tag nonce: @nonce || true do %>...

For those wondering how to get the same to work with their existing asynchronous forms:对于那些想知道如何使用他们现有的异步 forms 的人:

  1. Add this form field: <%= hidden_field_tag:nonce %>添加此表单字段: <%= hidden_field_tag:nonce %>

  2. On form submit, assign the nonce from the meta tag to the hidden field:在表单提交时,将元标记中的随机数分配给隐藏字段:

     document.getElementById('id_of_submit_button').addEventListener('click', async e => { document.getElementById('nonce').value = document.getElementsByName('csp-nonce')[0].content; });

    In this case, you don't want to prevent the default behavior on the event because you want the form to submit.在这种情况下,您不想阻止事件的默认行为,因为您希望提交表单。

Then continue with step 2 above (assigning the nonce to a controller instance variable).然后继续上面的步骤 2(将随机数分配给 controller 实例变量)。

I hope as a general strategy this is useful to some.我希望作为一般策略,这对某些人有用。 And I hope it can serve as inspiration for how to get the.js.erb file itself to run.我希望它可以作为如何让 .js.erb 文件本身运行的灵感。

UPDATE: Of course, for your specific (but limited) use case, you could simply return the object's service id as part of some JSON object you return to the client instead of rendering a.js.erb template.更新:当然,对于您的特定(但有限)用例,您可以简单地将对象的服务 ID 作为某些 JSON object 的一部分返回给客户端,而不是呈现 a.js.erb 模板。 I say "limited" because this won't work for people who really need to render templates.我说“有限”是因为这对真正需要渲染模板的人不起作用。

If you did want to render your.js.erb file, I suspect something like this could work for your case as well, where instead of checking whether the HTTP_TURBOLINKS_REFERRER header is present, you check for request.xhr?如果您确实想要呈现您的 .js.erb 文件,我怀疑这样的事情也适用于您的情况,而不是检查HTTP_TURBOLINKS_REFERRER header 是否存在,您检查request.xhr? . . Just know that starting in newer Rails versions , remote: true doesn't set the requisite header for request.xhr?只知道从较新的 Rails 版本开始remote: true不会为request.xhr? to work anymore.工作了。 But since you're on 5.2.1, it may work for you.但由于您使用的是 5.2.1,它可能适合您。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM