安全 别用 raw 和 html_safe

Rei · January 05, 2014 · Last by zzz6519003 replied at October 06, 2023 · 28019 hits
Topic has been selected as the excellent topic by the admin.

刚才修复了 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 要放在字符串处理链的末尾

我也要裸奔。

有攻击实例么?话说这里的 body_html 不是都在 markdown 转换成 html 的时候已经是过滤过了么,不能算是直接由用户生成的吧……

#3 楼 @jasl 目测你那个没有 CSS 白名单 ...

#4 楼 @aptx4869 markdown 的过滤漏了一些,毕竟它不是专门做过滤的。

我说怎么这么快就修复了,还在想怎么主动触发呢

#5 楼 @bhuztez 用的黑名单 - -

#8 楼 @jasl 感觉用正则表达式不牢靠啊

#6 楼 @Rei 这样应该在保存 body_html 之前先过滤一遍就行了吧,就不用每次显示都调用一次 sanitize 了,有点浪费 cpu 的感觉……

#10 楼 @aptx4869

看这里的 9 楼
http://ruby-china.org/topics/16628, 修复之前,点了就可以触发红心,而且传 cookie 给外网。上面已有四个红心。

#10 楼 @aptx4869 有片段缓存,其实 body_html 我觉得都不用。

#9 楼 @bhuztez 不过 style 是 String,所以只能用字符串的方式来搞,除非搞个 parser 给他弄成 ast 类似的形式

#13 楼 @jasl 我就是找了个 CSS 解析库去过滤的 ...

#14 楼 @bhuztez 来分享下?

#13 楼 @jasl #16 楼 @bhuztez 允许 style 属性会导致怎样攻击?

#17 楼 @Rei 至少 url() 可以随便插入个地址啥的,别的方法应该还有很多,一下子想不起来

#17 楼 @Rei 另外我印象里 Rails 的 Sanitize 是基于字符串方式的过滤,所以有可能会漏掉,sanitize 那种 gem 是利用 nokogiri 把 html 搞成 dom 再做过滤,相对而言更加稳妥

21 Floor has deleted

#7 楼 @hhuai 如果发现漏洞第一时间邮件管理员,我会很感激你的。

#23 楼 @Rei 我还准备 pull request 上来的,都改好了的话,就没然后了。

确切的说是别对 用户输入的内容 用 raw 和 html_safe

#25 楼 @chechaoyang 有人会觉得自己已经处理过了所以安全了,我总是为所有需要输出 HTML 的内容加 sanitize,除非是 SiteConfig 这种只有管理员输入、可能带有 javascript 的内容。

#26 楼 @Rei 嗯,是的,应该这样。必须基于白名单来做 sanitize,XSS 的手段很多都在大家的认识之外,而且还会产生新的攻击方式,所以如果输出的内容需要支持更多的标签和属性,用一个专用的 gem 来做净化很有必要。这里有一份清单 https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet

我也不用 raw 和 html_safe, 我直接用 <%== ......

#28 楼 @luikore 我也一直用== …听 Rei 这么一说还真不知道==等于哪个…

#30 楼 @Rei 嗯嗯,最喜欢神原猴子了

另外大部分 rails helper 都被加过 html_safe, <%= sanitize ... %> 相当于 <%== sanitize ... %>, <%= form_for ... %> 相当于 <%== form_for ... %> ... 效果一样但 == 执行快一点...

我觉得 html_safe 是个产生反效果的东西,反而把 helper 搞得很复杂不容易看出来到底是不是安全了。更喜欢裸 sinatra 这种没 html_safe 的,出问题搜 == 就可以。

而 rails 出了问题你要 ==, rawhtml_safe 搜三遍...

#32 楼 @luikore 我觉得不复杂啊,要输出 HTML 就用 sanitize,几乎不用 == raw html_safe。Rails 2 就是默认不 escape,当时爆了什么漏洞然后默认加上 h 了。

#33 楼 @Rei 感觉防不胜防…不如慢慢打补丁…

Don't watch the animate labeled Boku It's just like yoooooo♂ THREE♂IT‘S THREEEEEEEEEE♂

會造成怎樣的安全問題呢?

  1. 要显示富文本,必须采用白名单机制。
  2. 可以试试这个:https://github.com/rgrove/sanitize

很容易被 script 攻击,我之前就是裸奔,被裸打后,采用 Sanitize...

40 Floor has deleted

不过我现在仍然在用 raw 觉得挺好使的嘛

#42 楼 @lonely21475 唉 同学 你进来了吗?

#29 楼 @cassiuschen ==(两个等于好什么意思啊?),

#46 楼 @liangbin661831 就是 raw 输出,字符串里任何内容不转义

@Rei 我看到 home/twitter.html.erb 里还是<%= raw SiteConfig.twitter_page_html %>

#48 楼 @gsky 这块内容是管理员控制的,为了插入 javascript 需要用 raw。

讨论很激烈啊。

有些页面,要由管理员在富文本框编辑内容的,似乎也只能用 html_safe 了吧。

我觉得还有一个小遗憾,sanitize 会将不安全的标签砍掉,这样是安全了,但是仅仅只要这些不安全的标签不解析,保持原样输出的话,就不使了。

#3 楼 @jasl WoW,我现在才理解你说的内容。 👍

html_safe,用这个也不合适么?

看完这篇文章又涨姿势了👍

@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 源码中分别是:

&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;
alert("xss");
<script>alert("xss");</script>

可见,第一种表述,即 rails 的默认行为,我觉得并不是过滤,而是转义。在前端界面中你可以看到完整的 <script>alert("xss");</script> 内容。

第二种表述,sanitize 才是过滤,把 <script> 标签去掉了。在前端界面中你只能看到 alert("xss"); 的内容。

第三种表述毫无疑问是会执行其中的 JavaScritp 代码的,并在前端页面中看不到内容。

如果 danger_string 的原始值是 <em>haha</em>,三者在 HTML 源码中分别是:

&lt;em&gt;haha&lt;/em&gt;
<em>haha</em>
<em>haha</em>

第一种表达,在前端界面中,可以看到完整的 <em>haha</em> 内容。

第二种和第三种,在前端界面中,看到的都是斜体的 haha。可见,sanitize 默认只对部分标签进行过滤。

回到开头的疑问,想请教一下大家,转义和过滤,它们之间是一种什么关系?什么时候该用转义,什么时候该用过滤。

另外,转义和过滤,可以发生在将数据存入到数据库时,也可以将原始内容原原本本存入数据库,只是在渲染时,将其转义和过滤,哪一种方式是更常采用的?

谢谢!

Reply to baurine

sanitize 是为了安全的输出 HTML(富文本),如果你不希望输出富文本,直接转义文本即可

另外,好奇一下论坛帖子的排序顺序是怎么样的,为什么这个帖子有新的回复,我再回到社区首页,却找不到这个帖子呢?

Reply to baurine

太晚的就不让顶上去了

@jasl ,有点明白了,是不是可以这样理解。

转义并不能输出富文本,它输出的是原始文本。

如果你想输出富文本,简单粗暴的方法是 <% raw danger_string %> 来实现的,但这样会有安全影患,所以要用 sanitize 来将富文本中危险的标签,比如 <script> 过滤掉。

got it! 谢谢!

用 sanitize 不能显示富文本编辑器中插入的图片,raw 可以,怎么办?

Reply to yfscret

可以在 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

详情可以看看这篇文章

想到 react 里有个 dangerasly_set_html 方法

You need to Sign in before reply, if you don't have an account, please Sign up first.