5. 离线重定向
许多应用包含一个action控制器,它根据上下文将用户带到一个不同地址。最常见的例子是Session控制器,引导最近验证的用户到他们想到的目的地址,或者引导到一个默认的目的地址:
01 | class SignupsController < ApplicationController |
04 | if params[ :destination ].present? |
05 | redirect_to params[ :destination ] |
07 | redirect_to dashboard_path |
这引起了风险,攻击者可以创建一个URL,导致在信任用户登陆以后被引导到恶意网站:
1 | https://example.com/sessions/ new ?destination=http://evil.com/ |
未验证的重定向可以被用作钓鱼,或者摧毁用户对你的信任,因为看起来是你带他们到一个恶意网站的。即使一个警惕的用户,在他们第一次页面加载以后,也可能
不会检查URL地址栏来确认没有被钓鱼。这个问题非常严重,以致它被加入了最新一版的OWASP 十大应用安全威胁。
最佳实践:通过一个hash
1 | redirect_to params.merge(only_path: true ) |
当通过
字符串时,你能解析它为一个路径。
1 | redirect_to URI .parse(params[ :destination ]).path<span style= "font-size: 10pt; line-height: 1.5; font-family: 'sans serif',tahoma,verdana,helvetica;" ></span> |
修复方法:默认情况下,Rails应该只允许转向同一域名或在白名单上的域名。在少数情况下,外部转向的地方是有计划的。开发者应该要求通过anexternal:true选项到redirect_to 按顺序去决定加入更多危险的行为。
6. 利用link_to的跨站脚本 (XSS)
许多开发者没有意识到link_to助手的HREF属性可以被用来注入JavaScript脚本。这里是一个不安全代码的例子:
1 | <%= link_to "Homepage" , user.homepage_url %> |
假设用户可以通过更新他们的注册信息修改homepage_url的值,这就引起了XSS风险。像这样:
1 | user.homepage_url = "javascript:alert('hello')" |
将会生成这个HTML:
1 | < a href = "javascript:alert('hello')" >Homepage</ a > |
点击此链接将会执行攻击者提供的脚本。Rails的XSS保护不能阻止它。在社区转移到低调的JavaScript技术以前,这曾经是必要而且常见的,但现在成为一个残留的弱点。
最佳实践: 避免在HREF中使用不信任的输入。当你必须允许用户控制HREF时,先将输入通过URI.parse转换,并且清醒的检查协议与主机地址。
修复: Rails 应该默认在link_to助手中只允许目录,HTTP, HTTPS 和mailto:href 值。开发者应该通过传入link_to助手选项,选择进入不安全的行为,或者只是简单的link_to不支持这些,而开发者可以手工修改链接。
7. SQL注入
Rails做了个相当好的工作来阻止常见的SQL注入(SQLi)攻击,所以开发者会认为Rails是免疫SQLi的,并不是这样的。设想一个开发者需要基于参数拉取订单表的小计或者总计。他们可能这样写:
1 | Order.pluck(params[ :column ]) |
这样做是不安全的。明显地,用户能够纂改应用程序来获取订单表上他们想要的的任何列数据。然而,一些不明显的东西是攻击者也能够拉取其他表值。例如:
1 | params[ :column ] = "password FROM users--" |
2 | Order.pluck(params[ :column ]) |
会变成:
1 | SELECT password FROM users |
类似的,column_name属性在#calculate上实际是能接受任意的SQL的:
1 | params[ :column ] = "age) FROM users WHERE name = 'Bob'; --" |
2 | Order.calculate( :sum , params[ :column ]) |
会变成:
1 | SELECT SUM (age) FROM users WHERE name = 'Bob' ; |
控制#calculate方法的column_name属性允许攻击者从任意列或者任意表中拉取指定的值。
Rails-SQLi.org 详细讲述哪些ActiveRecord方法和选项允许使用SQL,而且提供例子说明它们是会遭到什么样的攻击。
最佳实践:了解你使用的APIs,同时要知道它们在什么情况下会允许超乎你预期的危险的操作,还有接受输入的白名单。
修复:很难找到解决所有问题的方案,因为不同的环境有不同的解决方案。通常,ActiveRecord APIs只允许经常使用的SQL片段,方法column_name的参数一般只接受列名。如果开发者想自己控制的更多,需要使用其他APIs。
点击Justin Collins的Twitter关于编写rails-sqli.org的信息,了解更多详情。