内容简介:Rails 中 ActiveRecord 的不当使用产生 SQLI 风险
Ruby on Rails 是一个经典的 MVC 框架。其中,ActiveRecord 是 MVC 中的 M(模型),负责处理数据和业务逻辑。每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录,通常表的每个字段在类中都有相应的 Field。ActiveRecord 同时负责把自己持久化,在 ActiveRecord 中封装了对数据库的访问,即 CURD。
使用 ActiveRecord 执行 SQL 语句的大概顺序是:
- 把提供的查询方法转换为等价的 SQL 查询。
- 触发 SQL 查询并从数据库中检索对应的结果。
- 为每个查询结果实例化对应的模型对象。
- 当存在回调时,先调用 after_find 回调再调用 after_initialize 回调。
只要能够理解第一步中的转换规则,那么针对 ActiveRecord 的 SQL 注入和普通的 SQL 注入并没有什么区别。
然而,Rails 为 ActiveRecord 方法提供了内置的 SQL 过滤器,用于转义 '、"、NULL 和换行符,使得大部分 SQL 注入都失去作用。但这并不代表开发者写出来的代码就是绝对安全的。如果没有对用户输入手动进行过滤,并且错误运用了某些方法,那么仍然存在 SQL 注入的风险。具体来讲,在使用某些方法时,需要手动触发这个过滤器。
所谓“错误使用“指的是,向某些方法传入 String 而不是 Array 或 Hash。因为在这种情况下,过滤器不会被触发。
where
以常用的 where 方法作例子,它能够接受的参数类型有 string,array 和 hash。然而,过滤器只有当传入的参数是 array 或 hash 时才是生效。如果开发者直接传入一个 string 类型的参数,那么会带来严重的安全隐患,看下面这个例子:
其中,params[:name]和params[:password] 是表单提交的用户名和密码。转换后的 SQL 语句为:
如果输入的 name为“') OR 1=1--“,那么 SQL 语句就会变成:
“OR 1”后面的内容全部被注释掉了,这是很常见的一句话密码,它和不使用 ORM 的情况下SQL 注入的 PAYLOAD 并无二异。
安全的写法如下:
- 传入 Array:
- 或者传入 Hash:
这两种情况下过滤器会被触发,输入的引号会被转义引起报错:
find_by
同理,find_by 方法也有相同的问题,因为它等价于 where(*args).take:
params[:id] = "admin = '’ OR 1=1 )#" User.find_by params[:id]
SQL 语句:
最安全的方式是传入 Hash:
Select/pluck
select 方法用于指定查询字段,如果传入的是 String,那么输入完全不会被转义:
SQL:
需要注意 select 方法返回的是 Model 类。
pluck 方法和 select 方法作用的地方一致,唯一的不同是 pluck 方法返回一个数组。
等价于:
因为 select 和 pluck 方法作用于 SQL 语句的最开始,因此 SQL 注入的方式非常灵活。
delete_all/destroy_all/update_all
这三个批量操作方法都可以进行 SQL 注入,只要传入的参数是 String。
delete_all 和 destroy_all 的区别是 destroy_all 会触发 ActiveRecord 的回调,而 delete_all 会直接把 SQL 语句传给数据库,所以理论上 destroy_all 比 delete_all 更安全一点。总之,正确的使用方法是传入 Hash。
- delete_all/destroy_all:
- SQL 语句:
- update_all:
SQL 语句:
- average
- calculate
- count
- maximum
- minimum
- sum
- 在 error-based 或 bool 类型的 SQL 注入中,分组和排序子句可以用于猜测字段数:
select * from user order by 1,2,3; (假设 user 表只有两个字段)
如果开启了错误提醒,那么字段名会直接显示在错误中。
其它情况下,可以根据返回的页面是否正确来判断。 - 当然也可以直接爆数据:
params[:sortby] = "(CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 else 1 END)" User.order("#{params[:sortby]} ASC")
From
from方法指定查询的表明,也就是注入点在from之后:
params[:from] = "users WHERE admin = 't' OR 1=1 #" User.from(params[:from]).where(admin: false)
SQL语句:
SELECT "users".* FROM users WHERE admin = 't' OR 1=1 #WHERE "users"."admin" = ?
后面的where被截断了。
Calculate
指的是 ActiveRecord::Calculations#calculate 中的方法,对应于 SQL 语法中的聚集函数
包含有:
注入点是 select 和 from 之间:
SQL 语句:
Group/Order/Having/Reorder
这几个方法对应 SQL 中的 排序 和分组子句,group 方法对应于 group by 子句;having 对应于having 子句;oreder 和 reorder 对应于 order by 子句(reorder 是用于覆盖作用域的默认排序,所以它们的注入方法是完全一样的)。
SQL 语句:
SELECT "users".* FROM "users" ORDER BY (CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 else 1 END) ASC
(猜解 password 的第一位是否为’s’,根据返回的是 True 还是 False ,排序的结果会有所不同)
还有一些方法如 lock,joins 也存在注入点,但因为在实际情况下难以利用或者跟前文的利用方法有重复,故不展开介绍。
小结
总的来说对于 ActiveRecord 的 SQL 注入和普通的 SQL 注入其实差别并不是很大,关键在于掌握 query methods 到 SQL 语句的转换规则以及了解 SQL 过滤器的触发条件。从以上的那些例子可以很明显得看出,当传入的参数是 String 时,许多方法是不会触发 SQL 过滤器的,这时的 SQL 注入就和普通的 SQL 注入基本没有区别。
攻击者应该了解哪些方法可能存在 SQL 注入漏洞。然而实战时的难点主要在于判断 SQL 查询使用的方法,因为链式方法的灵活性,同样的 SQL 查询可以有很多种写法,这对于 SQL 注入带来了很大的困难。
而对于 Rails 开发者来说,应当切记不要直接向查询方法直接传入 String,而应使用 Array 和 Hash,只要能做到这一点,那么被 SQL 注入的可能性将会大幅降低。
--------
微信公众号:TwoSecurity (二向箔安全)
以上所述就是小编给大家介绍的《Rails 中 ActiveRecord 的不当使用产生 SQLI 风险》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Is Parallel Programming Hard, And, If So, What Can You Do About
Paul E. McKenney
The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
html转js在线工具
html转js在线工具