开源项目 RuCaptcha 的验证码可以被攻击重放?

firebroo · 2016年10月14日 · 最后由 HackBraid 回复于 2016年10月17日 · 4820 次阅读

初学 rails, 写了个 blog,然后上社区搜到了这个 @huacnlee 写的 gem 做为验证码,因为我是做安全的,所有习惯性测试了验证码是否可以被复用,首先 chrome 开 f12,然后评论一条,copy 出数据包如下

curl 'http://www.firebroo.com/categorys/20/articles/11/comments' -H 'Cookie: _blog_session=eTBGSkNhYTlUWmYrSDlEN2k2TEV5NEwvRjhmTUdIQTQ3WUdFQ08vaXFhbnhEdGp6RTJHNjVqVmVFL3haVEtnZ2xBaE51ZUR3cTFIcGY3SWIzd2p0WXIxdG1QQWhBemNIUkJYV3B5SGIySmhERmhqNWpoc2lqdEJTSDJTclBaRG51VXpGaWgwenFDcWIrY2JqWWUrZHpKcG1NcExWbVNxNzBYRUV2M01VL0Jibk15TjVqZXBYRktsbWlnM2M5eDNCbWZrOE5XQzhqaUxDUmttWHNETHovSWg5MmZ4V25reDErUVVuaC9MTkVxZ3YrQlk0aFJUcFZwM0dmcFRkajZFUkRDeVllNVhyN1BtQkxQNXYydCtuQURMQ3Bhc0plMXMwYUNRRVp2eUMwWUZxRW9vcFRDUTNhalMxVFAyZVQ3YjdXM3FQTXoxbU1HWGVBajNEc0NDd2VZdHJlUU96Q0N4QlY0YS9VN0RLL0JodEpJMjF1SnpibjBUMURFK2FXL1V6UTI3bkM3ZFFrOVFnR0ZkY0E4cUpiL2s1SHB2WmpvRHRzUWpFK1gxeUh0eXJ4WStQMUZUYVRpdXlWZ2diMjVOZkZyNUxzdGJjOFJrUmdUZnhlM3grV3BsTjN3M3h5SGNlVzRmMEp6bHl3M0E9LS1WQlpNbzgvNjNNb201OGRYTVdPTHhBPT0%3D--d72e796e819742de283b32395b295ee6503be124' -H 'Origin: http://www.firebroo.com' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' -H 'Cache-Control: max-age=0' -H 'Referer: http://www.firebroo.com/categorys/20/articles/11.html' -H 'Connection: keep-alive' --data 'utf8=%E2%9C%93&authenticity_token=SLFcb%2FjeKgBJ2IyZ%2FgG5Itbcbv%2F9dftg8os6r1MZF9Msgk7imr7IcOev8b5Kdz0bOugb8NiQBhhVfH5vX%2F1MuA%3D%3D&comment%5Bcommenter%5D=firebroo&comment%5Bbody%5D=111&_rucaptcha=2d72&commit=%E6%8F%90%E4%BA%A4' --compressed

只要我多次不断执行这条命令,评论就会不断添加。达到绕过验证码实现重放攻击。 然后去 github 看了 verify_rucaptcha?函数,发现验证码设计流程应该是没问题的,无论验证码正确与否,都会被 session.delete 注销。

def verify_rucaptcha?(resource = nil)
  rucaptcha_at = session[:_rucaptcha_at].to_i
  captcha = (params[:_rucaptcha] || '').downcase.strip

  # Captcha chars in Session expire in 2 minutes
  valid = false
  if (Time.now.to_i - rucaptcha_at) <= RuCaptcha.config.expires_in
    valid = captcha.present? && captcha == session.delete(:_rucaptcha)
  end

  if resource && resource.respond_to?(:errors)
    resource.errors.add(:base, t('rucaptcha.invalid')) unless valid
  end
end

我打印过出重放请求时候的 session 的_rucaptcha, 发现和首次评论的验证码值一样,所以 session.delete 没有删除成功还是有啥缓存?

这应该是没有 delete 掉

你这个是 session 重放攻击了吧,
rucaptcha 是验证码信息存在 session 里的,验证码默认有效期是两份钟。
你用一个正确的 session 在有效期内重发,server 每次都会从 session 里解析出验证码和有效期 两个都是正确的,验证就通过了...

是的 Rails 的 session 是通过 cookie 实现的,不是 Rucaptalcha 的问题

session,session_id 放在 cookie 里面,你每次都带相同的 cookie 怎么删得掉啊。

#3 楼 @jasl #4 楼 @gyorou session 通过 cookies 实现的啊。。那这种重放刷的问题靠验证码已经解决不了了。。 #2 楼 @759803573 我以为 rails 的 sessions 也会存服务器。

#5 楼 @firebroo Session 通过 Cookie 来储存 Rails 的默认方案,这块叫做 SessionStore,如果你需要后端来储存 Session,也是很容易修改的

#6 楼 @jasl 刚查到了,rails 设置默认的 session 存储方式很不安全啊。。

这不是 Rails Session 的问题,是 rucaptcha 的问题,设计的时候忘了这个茬。

#7 楼 @firebroo Rails 的方式安全,表单的重放攻击可以通过 CSRF Token 来防止,即你在控制器加入 protect_from_forgery (在 ApplicationController 默认包含的,所以其子类都会受到保护),这时候,你在生成的 HTML 的 <head></head> 里可找到形如 <meta name="csrf-token" content="8MCrc1N+u+FfLYTsrpqsLW41/I0s3rWf6zk6Cib2tt5T0pu1ErBMAeWIkd5AlFJ3Q69b/k8vX14dxZ7RBFVDfA=="> 的标签,在遵守 Rails 的标准开发方式的时候,当表单提交时,其中的 content 会作为请求头的 X-CSRF-Token 字段传递给后端(由jquery_ujs 实现),这块通过一个 Rack 中间件来处理(没记错的话) 但是在采用非 Rails 标准方式实现的时候,或者暴露 API 的应用,这个机制会失效

#9 楼 @jasl 好吧,我看了一点点 rails guide 就开始写着玩了。。 #8 楼 @huacnlee 恩,后来各种搜,看到了指南有说明默认方式会存在攻击重放问题,我现在把 session 放数据库解决了问题。

#11 楼 @firebroo 应该还是不用的,你主贴的 curl 里请求也并没包含 CSRF 相关的头,这时候(遵守 Rails 的默认写法前提下),这个请求应该会抛出异常 建议你还是先检查下 CSRF 相关的保护有没做好(在控制器这边),按标准实践来实现功能

Gem 问题已修复,改用 Cache 存储验证码到服务端。

https://github.com/huacnlee/rucaptcha/commit/e129851fd982bdf5e8cc2c6bbf0d3b11a27bc248

  • rucaptcha 1.0.1

#12 楼 @jasl 我的表单有 authenticity_token 字段,app/controllers/application_controller.rb 里面也有 protect_from_forgery with: :exception,应该没问题吧。CSRF 攻击和这种重放攻击有区别,token 验证的设计方式一般是在 cookies 里面放置一个加密的,然后表单放置一个明文的,提交的时候解密 cookies 里面的 token 然后和表单提交的来对比,我用上面的包重放,两个永远相等。所以 csrf token 无法防御这种重放的,token 的真实应用场景是别人无法拿到你 cookies 里面的 token(当然可以配合 xss 拿到 cookie 另说),如果我加密了 token 存在 cookies 里面,基本拿到 cookies 也没法知道明文的 token。然后就起到了防御 CSRF 攻击的作用。

#9 楼 @jasl CSRF Token 只能避免跨站攻击,没法避免重放攻击,拷贝相同的 Cookie、表单、相同的环境执行是有效果的

#15 楼 @firebroo 再帮忙验证一下

#17 楼 @huacnlee 恩,我刚测试了,1.0.1 已经确认修复了。

#13 楼 @huacnlee 今天为了这个 cookies 琢磨了一下午,没想到你这么快就更新了。👍

#19 楼 @jasl 没事,很感谢你给我的回复~.~

@firebroo 不错不错,学习了~

需要 登录 后方可回复, 如果你还没有账号请 注册新账号