前两天在写代码的时候,突然收到警告说项目代码中存在 XSS 漏洞,遂立即根据报告的 URL 排查页面代码,虽然很快就修复了,而且同样问题的讨论两年前就有了,一般来说相对有经验的同学也应该都知道这个点,但是还是觉得有必要写出来,再次提醒一下其他小伙伴,避免踩坑。
问题根源
其中,在找到的漏洞出现的地方,都存在类似以下这样的 slim 代码:
问题就出在双等号==上,因为在 slim 跟 ERB 模板(其他模板比如 HAML 之类的就不清楚了)中,双等号其实是 Rails 的 raw 这个 helper 方法的缩写
<%=raw @cms.current_template %> <%# inserts @cms.current_template as is %>
or, equivalently, use <%==:
<%==@cms.current_template %> <%# inserts @cms.current_template as is %>
也就是说上面的代码等同于:
其中 raw 方法在 Rails 文档中的解释是这样子的:
大概意思就是,这个方法将会跳过对传入的字符串进行标签过滤以及其他处理,直接将字符串输出到 HTML 中。
所以到现在原因就很清晰了,因为不小心在代码里多加了一个等号,变成了双等号,导致将会直接把用户的输入输出到待渲染的 HTML 中,在不自知的情况下留下了 XSS 漏洞。于是乎,修复方案仅需去掉一个等号即可:
这样,Rails 就能继续自动过滤输入的 :account 的参数并且自动过滤恶意内容了。
raw、String#html_safe 以及 <%==%>
在查看 raw 方法的文档时,顺便看了其源码,极其简单,只有一行:
def raw(stringish)
stringish.to_s.html_safe
end
raw 只是先确保将 stringish 参数转化为字符串,然后调用了 String#html_safe 方法而已。而且在 String#html_safe 的文档中,同样反复强调慎重使用这两个方法:
所以,可以总结一下,以下三种写法的代码都是等价的,都是不安全的:
input class=’xxx’ value=raw(params[:account])
input class=’xxx’ value=params[:account].html_safe
那在切实需要输出包含 HTML 内容比如富文本编辑器编辑的内容时,如何保证安全?
方案很简单,只需要使用文档中推荐的 sanitize helper 方法:
(#sanitize)Sanitizes HTML input, stripping all tags and attributes that aren’t whitelisted.
或者使用一些其他第三方的 gem 用来做过滤处理。
总结
不要使用双等号缩写的方式,以避免其他人(比如项目里的 Rails 新手)在不了解的情况下照着滥用;
尽可能不用 raw helper 或者 String#html_safe 方法,尽可能使用 #sanitize;
多借助工具进行自动扫描,比如 brakeman,能够快速高效检测出包括 XSS 漏洞在内的多种安全隐患。