刚才修复了 Ruby China 一处潜在漏洞:
https://github.com/ruby-china/ruby-china/commit/78f7cb4a73fe5972a534e01a2ef7b7353ef5fe00
起因都是 raw
,它和 html_safe
是同义的。我发现很多人都误解了这两个 helper 的用法,再强调一次它的意思是:
我要裸奔!
Rails 框架本身做了很多安全措施,在默认情况下,template 里的所有字符串都会被过滤:
<%= danger_string %> <!-- 安全 -->
这段代码是安全的,但是这段代码:
<%= raw danger_string %> <!-- 危险 -->
就告诉模板系统关掉了安全过滤,这是非常危险的。通常谈到 raw 的时候都因为要输出 html 内容,这时候应该用 sanitize
,这是一个基于白名单的过滤方法:
<%= sanitize danger_string %> <!-- 只要不开危险的标签属性名单就安全 -->
sanitize 可以在方法级别和全局级别设置白名单标签和属性,详细可以看文档 http://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html
总结:如果你不知道自己知不知道自己在干什么,别用 raw 和 html_safe,用 sanitize。
补充 1:helper 里面也要避免 raw,避免在 helper 拼接 HTML 补充 2:sanitize 要放在字符串处理链的末尾
看这里的 9 楼
http://ruby-china.org/topics/16628,
修复之前,点了就可以触发红心,而且传 cookie 给外网。上面已有四个红心。
#25 楼 @chechaoyang 有人会觉得自己已经处理过了所以安全了,我总是为所有需要输出 HTML 的内容加 sanitize,除非是 SiteConfig 这种只有管理员输入、可能带有 javascript 的内容。
#26 楼 @Rei 嗯,是的,应该这样。必须基于白名单来做 sanitize,XSS 的手段很多都在大家的认识之外,而且还会产生新的攻击方式,所以如果输出的内容需要支持更多的标签和属性,用一个专用的 gem 来做净化很有必要。这里有一份清单 https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
另外大部分 rails helper 都被加过 html_safe
, <%= sanitize ... %>
相当于 <%== sanitize ... %>
, <%= form_for ... %>
相当于 <%== form_for ... %>
... 效果一样但 ==
执行快一点...
我觉得 html_safe
是个产生反效果的东西,反而把 helper 搞得很复杂不容易看出来到底是不是安全了。更喜欢裸 sinatra 这种没 html_safe
的,出问题搜 ==
就可以。
而 rails 出了问题你要 ==
, raw
和 html_safe
搜三遍...
Don't watch the animate labeled Boku It's just like yoooooo♂ THREE♂IT‘S THREEEEEEEEEE♂
@Rei @jasl 今天遇到了 HTML 转义和过滤的问题,看了好多资料,没有哪篇文章指出转义和过滤的区别,什么时候该用转义,什么时候该用过滤,还是两者一起用。
我对下面三种表述做了一下对比 (ruby 2.3.3 & rails 5.0)
<%= danger_string %>
<%= sanitize danger_string %>
<%= raw danger_string %>
假设 danger_string
原始值是 <script>alert("xss");</script>
,三者在 HTML 源码中分别是:
<script>alert("xss");</script>
alert("xss");
<script>alert("xss");</script>
可见,第一种表述,即 rails 的默认行为,我觉得并不是过滤,而是转义。在前端界面中你可以看到完整的 <script>alert("xss");</script>
内容。
第二种表述,sanitize 才是过滤,把 <script>
标签去掉了。在前端界面中你只能看到 alert("xss");
的内容。
第三种表述毫无疑问是会执行其中的 JavaScritp 代码的,并在前端页面中看不到内容。
如果 danger_string
的原始值是 <em>haha</em>
,三者在 HTML 源码中分别是:
<em>haha</em>
<em>haha</em>
<em>haha</em>
第一种表达,在前端界面中,可以看到完整的 <em>haha</em>
内容。
第二种和第三种,在前端界面中,看到的都是斜体的 haha。可见,sanitize 默认只对部分标签进行过滤。
回到开头的疑问,想请教一下大家,转义和过滤,它们之间是一种什么关系?什么时候该用转义,什么时候该用过滤。
另外,转义和过滤,可以发生在将数据存入到数据库时,也可以将原始内容原原本本存入数据库,只是在渲染时,将其转义和过滤,哪一种方式是更常采用的?
谢谢!
@jasl ,有点明白了,是不是可以这样理解。
转义并不能输出富文本,它输出的是原始文本。
如果你想输出富文本,简单粗暴的方法是 <% raw danger_string %>
来实现的,但这样会有安全影患,所以要用 sanitize 来将富文本中危险的标签,比如 <script>
过滤掉。
got it! 谢谢!
可以在 config/application.rb 下设置
class Application < Rails::Application
config.action_view.sanitized_allowed_tags = ['table', 'tr', 'td'] #安全的标签
config.action_view.sanitized_allowed_attributes = ['id', 'class', 'style'] #安全的属性
end
详情可以看看这篇文章