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的信息,了解更多详情。