下载本文的PDF版本 PDF

大规模赋值武器

Patrick McKenzie,Kalzumeus

一个 Ruby on Rails 应用突显了一些严重但易于避免的安全漏洞。


2010年5月,在用户对 Facebook 隐私政策普遍感到厌恶的新闻周期中,纽约大学的四名学生团队发布了一项捐款 10,000 美元的请求,以构建一个注重隐私的 Facebook 替代品。该软件 Diaspora 将允许用户托管自己的社交网络并拥有自己的数据。该团队承诺开源他们编写的所有代码,通过将代码暴露于公众监督之下,来保证用户数据的隐私和安全。《纽约时报》的头版报道帮助该团队最终筹集了超过 20 万美元。他们预计在 2010 年 10 月向最终用户推出该服务。

9 月 15 日,Diaspora 发布了其 源代码 的“pre-alpha 开发者预览版”。我出于好奇看了一下,并被许多严重的安全错误震惊了。我花了第二天的时间在本地挖掘代码,并尝试与团队联系以私下解决这些问题。这些安全错误非常严重,足以危及该项目的目标。

本文描述了损害 Diaspora 开发者预览版安全性的错误。通过更好的安全实践和更好的默认选择来避免此类错误将使应用程序更加安全。

Diaspora 架构

Diaspora 是基于 Ruby on Rails 3.0 编写的,这是一个流行的现代 Web 框架。大多数 Rails 应用程序作为长时间运行的进程在专门的 Web 服务器(如 Mongrel 或 Thin)中运行。由于 Rails 不是线程安全的,因此通常会在一台机器上并行运行多个进程,并在 Apache 或 nginx 等线程 Web 服务器之后运行。这些服务器直接服务于静态资源请求,并将动态请求代理到 Rails 实例。

在架构上,Diaspora 被设计为联邦式 Web 应用程序,用户帐户(种子)被收集到单独运营的服务(节点/舱)中,方式类似于单独邮件服务器上的电子邮件帐户。最终用户访问其 Diaspora 帐户的主要方式是通过 Web 界面。节点/舱之间使用加密的 XML 消息进行通信。

与大多数 Rails 应用程序不同,Diaspora 没有使用传统的数据库进行持久化。相反,它使用 MongoMapper ORM(对象关系映射)与 MongoDB 接口,其开发者将其描述为“文档型数据库”,它“弥合了键/值存储和传统关系数据库之间的差距”。MongoDB 是现在流行的 NoSQL 数据库的一个例子。

虽然 Diaspora 的架构有些奇特,但开发者预览版发布的问题源于非常平凡的来源。

Ruby on Rails 中的安全性

Web 应用程序安全性是一个非常广泛和深入的主题,在 官方 Rails 安全指南OWASP(开放 Web 应用程序安全项目)Web 应用程序漏洞列表 中对此进行了详细介绍,这些指南和列表本可以帮助捕获本文中讨论的所有问题。虽然 Web 应用程序安全性可能看起来令人望而生畏,但此处讨论的错误是基本的,可以作为那些构建面向公众软件的人员的教训。

对 Diaspora 预发布版本的源代码进行粗略分析后,揭示了大约六个关键错误,几乎影响了系统中的每个类。主要有三种类型,详述如下。所有从 Diaspora 启动时的源代码中提取的代码示例 [注意:我已经 在 GitHub 上 fork 了 Diaspora 公共存储库并创建了一个标签,以便可以检查此代码] 在发现后立即报告给了 Diaspora 团队,并且团队已报告已修复。

身份验证 != 授权:用户不可信

以下代码中的基本模式在 Diaspora 的代码库中重复出现多次:服务器上对安全敏感的操作使用来自 HTTP 请求的参数来标识它们要操作的数据片段,而没有检查已登录用户是否实际被授权查看或操作该数据。

#photos_controller.rb
def destroy
    @album = Album.find_by_id params[:id] # 没有授权检查。
    @album.destroy
    flash[:notice] = "相册 #{@album.name} 已删除。"
    respond_with :location => albums_url
end

例如,如果您登录到 Diaspora 种子,并且知道任何节点/舱上任何照片的 ID,则更改任何可见的 destroy 操作的 URL 以包含任何其他用户的照片 ID,将使您可以删除第二张照片。Rails 使此类漏洞利用非常容易,因为操作的 URL 非常容易猜测,并且对象 ID“泄漏”到各处。不要假设对象 ID 是私有的。

当然,Diaspora 确实尝试检查凭据。它使用处理身份验证的库 Devise 来验证只有在您登录后才能访问 destroy 操作。但是,如上面的代码示例所示,Devise 不处理授权——即检查以确保您实际上被允许执行您尝试执行的操作。

影响

当 Diaspora 发布时,任何 Diaspora 节点/舱上的免费帐户的攻击者基本上可以完全访问该软件的任何功能,以针对其他人的帐户。这是一个非常严重的安全漏洞,但它与系统中的其他漏洞相结合,使攻击者能够实施比仅仅删除照片更微妙和更深远的攻击。

如何避免这种情况

在执行敏感操作之前检查授权。执行此操作的最简单方法(除了使用库来为您处理之外)是采用您对已登录用户的概念,并且仅通过该用户访问用户特定数据。例如,Devise 为所有操作提供对 current_user 对象的访问权限,该对象是当前登录用户的替身。如果某个操作需要访问照片,则应调用 current_user.photos.find(params[:id])。如果恶意用户破坏了 params 哈希(由于它直接来自 HTTP 请求,因此必须被视为“在敌人手中”),该代码将找不到任何照片(因为关联如何作用于 user_id)。这将立即生成 ActiveRecord 异常,在任何潜在的恶意行为开始之前将其阻止。

批量赋值会毁了您的一天

我们已经了解到,如果我们忘记授权,那么恶意用户可以对他人做任意坏事。在以下示例中,由于用户更新方法不安全,攻击者可能会干预他们的个人资料。但这仅仅是我们能做的吗?

#users_controller.rb
def update
    @user = User.find_by_id params[:id] # <-- 没有授权检查。
    prep_image_url(params[:user])

    @user.update_profile params[:user] # <-- 将不受信任的输入传递给 @user,然后...
    respond_with(@user, :location => root_url)
end

#user.rb
def update_profile(params)
    if self.person.update_attributes(params) # <-- 将输入直接插入到数据库中。
        #为清晰起见省略
    end
end

经验不足的开发人员可能会认为,更新方法只能更新 Web 表单上之前的内容。例如,图 1 中显示的表单非常良性,因此人们可能会认为,有人使用此错误所能做的只是篡改用户的个人资料名称和电子邮件地址。

这是非常错误的。

Rails 默认使用称为批量更新的功能,其中 update_attributes 和类似方法接受哈希作为输入,并按顺序调用哈希中符号的所有访问器。对象将更新数据库列(或其 MongoDB 类似物),并将为哈希中定义了该方法的任何 :parameter_name 调用 parameter_name=

影响

让我们看一下以下代码中的 Person 对象,看看这会让攻击者进行哪些恶作剧。请注意,update_profile 不是更新个人资料,而是更新 Person:Diaspora 对与一个人相关联的数据的内部概念,而不是与一个电子邮件地址(User)相关联的登录名。当它实际上是 update_person 时,调用 update_profile 是从审查者那里隐藏此类代码的安全含义的好方法。开发人员应注意正确命名事物。

#Person.rb
    class Person
    #为清晰起见省略
    key :url,     String
    key :diaspora_handle, String, :unique => true
    key :serialized_key, String #用于加密的公钥/私钥对。

    key :owner_id, ObjectId #极其安全敏感,因为...

    one :profile, :class_name => 'Profile'
    many :albums, :class_name => 'Album', :foreign_key => :person_id
    belongs_to :owner, :class_name => 'User' #...更改它会重新分配帐户所有权!

end

#User.rb
one :person, :class_name => 'Person', :foreign_key => :owner_id

这意味着通过更改 Person 的 owner_id,可以将 Person 从一个帐户 (User) 重新分配到另一个帐户,从而不仅可以拒绝任意受害者使用该服务,还可以接管他们的帐户。这使攻击者可以冒充他们、随意访问他们的数据等。这是因为 MongoDB 中的“one”方法选择它可以在数据库中找到的第一个匹配条目,这意味着如果两个 Person 具有相同的 owner_id,则拥有的 User 将非确定性地控制其中一个。这使攻击者可以将您的 Person#owner_id 分配为他的 #owner_id,这使攻击者有 50-50 的机会获得对您帐户的控制权。

情况变得更糟:由于攻击者还可以将其自身数据的 owner_id 重新分配为无意义的字符串,这将使其个人数据与其帐户脱钩,这将确保其帐户与受害者的个人数据相关联。

情况变得更加糟糕。 请注意 serialized_key 列。如果您更深入地查看 User 类,那就是其序列化的公钥/私钥加密密钥对。Diaspora 节点/舱在相互通信时使用加密,以便 Facebook 的窥探者无法读取用户的状态更新。这是 Diaspora 的核心卖点。不幸的是,攻击者可以使用未经检查的授权和批量更新的组合静默地覆盖用户的密钥对,将其替换为用户生成的密钥对。由于攻击者现在知道用户的私钥,无论 Diaspora 的密码学实现得多么好,攻击者都可以随意读取用户的消息。这损害了 Diaspora 对用户的核心价值主张:他们的数据将保持安全并在他们的控制之下。

这就是在现实生活中杀死大多数加密系统的原因。您不必击败加密来击败系统;您只需要击败它周围链条中最薄弱的环节。几乎可以肯定这不是加密算法——它可能是开发人员在错误地认为强大的密码学意味着强大的安全性时添加到更大系统中的一些不足之处。密码学不是安全性的酱油。

此攻击非常容易执行。可以使用一个不比安装了 Firebug 的 Firefox 更复杂的工具来完成:向表单添加一个额外的参数,切换提交 URL,并立即获得对您希望的任何帐户的控制权。对于开源软件项目以及其他可以假定攻击者可以访问源代码的情况,此漏洞尤其明显:负责授权和访问用户对象的控制器是攻击者的明确优先事项,因为预计从颠覆它中获得的收益。一个技术娴熟的攻击者可以在几分钟内找到此漏洞并创建一个脚本来将其武器化。

如何避免这种情况

可以通过检查授权来避免此攻击的特定变体,但这本身并不能阻止所有相关攻击。攻击者可以创建任意数量的帐户,更改每个帐户上的 owner_id 以与受害者的合法用户 ID 冲突,并且这样做会成功地将受害者的数据与其登录名脱钩。这相当于拒绝服务攻击,因为受害者失去了 Diaspora 服务的效用。

在身份验证修复后,应将对敏感数据的写入访问权限限制在最大程度上可行的范围内。合适的第一步是禁用批量赋值,这应始终在面向公众的 Rails 应用程序中关闭。Rails 团队大概默认保持批量赋值开启,因为它节省了许多代码行,并使 15 分钟的博客演示更好,但它几乎在所有应用程序中都是一个安全漏洞。

幸运的是,这很容易解决:Rails 有一种称为 attr_accessible 的机制,该机制仅使列出的模型属性可用于批量赋值。仅允许安全属性进行批量赋值(例如,您希望最终用户被允许更新的数据,例如他们的姓名而不是他们的密钥)可以防止此类攻击。此外,attr_accessible 在其应用程序代码中显式地记录了程序员对安全性的假设:作为白名单,它是模型类中已知的弱点,并且任何安全审查流程都将对其进行彻底检查。

这是非常理想的,因此开发人员最好强制使用 attr_accessible。这很容易做到:只需在初始化器中调用 ActiveRecord::Base.attr_accessible(nil),所有 Rails 模型将自动禁用批量赋值,直到它们通过 attr_accessible 显式启用它。请注意,这可能会破坏常见 Rails gem 和插件的功能,因为它们有时依赖于默认值。这是安全是社区问题的一种方式。

如果您的数据存储允许,另一种缓解方法是显式禁止写入尽可能多的数据。几乎可以肯定,owner_id 没有合法的理由可以重新分配。ActiveRecord 允许您使用 attr_readonly 来执行此操作。MongoMapper 目前不支持此功能,这是在生产系统中使用前沿技术的危险之一。

NoSQL 并不意味着没有 SQL 注入

新型 NoSQL 数据库在被利用方面的经验比我们已知和喜爱的旧关系数据库少几十年,这意味着针对充分理解的攻击的对策仍然不成熟。例如,针对 SQL 数据库的规范攻击是 SQL 注入:使用应用程序的用户暴露界面来制作任意 SQL 代码并在数据库上执行它。

def self.search(query)
    Person.all('$where' => "function() { return this.diaspora_handle.match(/^#{query}/i) ||
    this.profile.first_name.match(/^#{query}/i) ||
    this.profile.last_name.match(/^#{query}/i); }") #允许代码注入到 MongoDB。
end

影响

之前的代码片段允许代码注入到 MongoDB 中,有效地允许攻击者完全读取数据库,包括序列化的加密密钥。请注意,由于字符串插值的魔力,攻击者可以使包含 JavaScript 的字符串评估为攻击者几乎想要的任何内容。例如,攻击者可以注入一个精心构造的 JavaScript 字符串,以导致第一个正则表达式在没有任何结果的情况下终止,然后执行任意代码,然后注释掉 JavaScript 的其余部分。

我们可以从这个 find 调用中获取关于任何特定人的一个数据位——这个人是否在结果集中。但是,由于我们可以随意构造结果集,我们可以使其成为非常重要的一位。JavaScript 可以获取一个字符串并将其转换为数字。此代码留给读者作为练习。使用该 JavaScript,攻击者可以对数据库运行重复的 find 查询,以对序列化的加密密钥对进行二分搜索

“如果 Patrick 的序列化密钥大于 2^512,则返回 Patrick。好的,他不在结果集中?好的,如果他的密钥大于 2^256,则返回 Patrick。他在结果集中?如果他的密钥大于 2^256 + 2^255,则返回他。...”

1,024 位的密钥长度可能会给开发人员留下非常安全的印象。但是,如果我们被允许对密钥进行二分搜索,则只需大约 1,000 个请求即可发现密钥。通过 HTTP 客户端执行搜索的脚本可以在一两分钟内轻松运行 1,000 次访问。以这种方式泄露用户的密钥对会泄露用户曾经发送或将要发送的所有消息,并且除了服务器上容易被忽视的瞬时活动峰值外,不会留下任何入侵痕迹。更有耐心的攻击者甚至可以避免留下这一点。

这可能不是代码注入造成的唯一漏洞。攻击者很可能可以通过此接口执行状态更改 JavaScript,或将 Person 文档与其他文档连接起来,以从数据库中读取任何想要的内容,例如用户密码哈希。评估这些攻击是否可行需要深入了解 MongoDB 和 Ruby 包装器的内部工作原理。典型的应用程序开发人员没有足够的技能来评估在这些级别上运行的堆栈部分:这本质上与询问他们,如果针对针对异构架构编译的数据库执行他们的 SQL 查询是否会导致缓冲区溢出相同。明智的开发人员不应尝试回答这个问题,而应将任何注入攻击视为允许完全系统泄露。

如何避免这种情况

不要在发送到数据库的查询中插入字符串。使用 MongoDB 等效的预编译语句。如果您的数据库解决方案没有预编译语句,那么它还不够成熟,无法在面向公众的产品中使用。

发布最终用户软件时要小心

人们可能会合理地问,开发者预览版中的安全缺陷是紧急事件还是仅仅是产品开发历史中的一个注脚。由于其创建的情况,Diaspora 从未有过既公开可用但又不可利用的奢侈。作为一个备受期待的项目,Diaspora 保证(并且确实)在代码可用后的几个小时内就有公开可访问的服务器。

设置服务器的人员应该足够了解以评估运行它们的安全后果。Diaspora 预览版的情况并非如此:存在公开可访问的 Diaspora 服务器,任何用户都可以在这些服务器上轻松泄露另一个用户的帐户。此外,即使假设服务器运营商了解他们在做什么,他们的用户以及受邀加入“新的安全 Facebook”的用户的朋友也无法评估他们在 Diaspora 上的安全性。他们相信,既然它在他们的浏览器上并得到朋友的认可,那么它一定是安全可靠的。(这基本上是他们加入 Facebook 之前的过程,之后才评估该操作的隐私后果。)

最安全的计算机系统是位于一个锁着的房间里,周围有武装警卫,并且已关闭电源的系统。不幸的是,这在现实世界中不是一个可行的建议:如果软件要改善用户的生活,就需要开发和使用软件。Diaspora 能否在公开预览发布的同时,又不让最终用户暴露于其安全缺陷之下?可以。一个明智的折衷方案是发布代码时省略注册页面,迫使开发人员仅通过 Rake 任务或 Rails 控制台添加新用户。这将保留开发人员 100% 的项目工作能力和新闻媒体获取屏幕截图的能力——而不会允许技术上不成熟的人员注册 Diaspora 服务器。

Diaspora 社区已采取一些措施来减少过早部署软件的危害,但这些措施不足。该团队策划了一个 公共 Diaspora 种子列表,其中包括软件不安全的醒目免责声明,但这种消极姿态无法解决社交软件传播方式的现实:朋友向朋友推荐它,并且面对加入新站点的社会压力,警告将被忽视或忽略。

Rails 能否防止这些问题?

许多语言或框架的支持者认为“他们的”框架比其他替代方案更安全,并且某些其他框架本质上是不安全的。不安全的代码可以用任何语言编写:实际上,考虑到问题“这是否安全?”在算法上是不可判定的(它很容易简化为停机问题),人们可能会认为,创建一个始终安全的任何有用的计算机语言几乎是不可能的。

也就是说,默认设置和社区很重要。Rails 体现了约定优于配置的精神,这是 37signals 团队(Rails 的原始作者)所描述的“有主见的软件”的一个例子。Rails 约定普遍针对程序员的生产力和幸福感进行了优化。这有时会以安全性为代价进行权衡,例如默认开启批量赋值的示例。

在某些意见上存在折衷方案,这些方案可以使 Rails 更安全,而又不会显着阻碍开发体验。例如,Rails 可以默认在开发环境中启用批量赋值,但在生产环境中禁用(生产环境通常是恶意用户可以访问的环境)。这有先例:例如,Rails 仅针对生产模式下的本地请求打印堆栈跟踪(其中可能包含敏感信息),并且如果错误是由非本地请求引起的,则会给出信息量较少(且更安全)的消息。

但是,任何数量的改进框架都无法将程序员从犯错中拯救出来,例如忘记在破坏性操作之前检查授权。这就是社区发挥作用的地方:开源社区、实践开发人员和教育工作者需要强调安全是一个过程。没有技术银弹可以使应用程序安全:它是通过详细的分析,从而采取行动解决漏洞而变得更安全的。

这在计算机科学教育中经常被忽视,因为安全被视为事后诸葛亮或在以后解决的实现细节。大学通常像行业一样评分:在几乎所有输入上成功运行的程序几乎可以获得所有可能的积分。这种心态应用于安全性,会产生灾难性后果:攻击者几乎拥有无限的时间与应用程序交互,有时甚至可以使用其源代码,并查看它如何对特定输入做出反应。在不可数无穷的程序状态和可能的输入空间中,攻击者可能只需要识别一个输入,程序失败该输入会损害系统的安全性。

如果 Diaspora 中的其他一切都完美实现,那也无关紧要;如果搜索功能仍然允许代码注入,仅此一项就会导致该项目的核心目标完全失败。

在打补丁后,Diaspora 安全吗?

安全性是旨在产生安全性的过程的结果。虽然 Diaspora 项目一直在迭代软件,并且在本文发布时已提供给部分最终用户,但不可能说架构和代码绝对安全。这在 Diaspora 中绝非独有:几乎所有面向公众的软件都存在漏洞,尽管投入了大量资源来保护流行的商业和开源产品。

但这并不是绝望的理由:通过改进的代码、改进的实践和安全审查修复或避免的每个错误都为软件用户提供了增量安全,并提高了其效用。我们可以做得更好,我们应该开始这样做。

喜欢它,讨厌它?请告诉我们

[email protected]

Patrick McKenzie 是 Kalzumeus 的创始人,Kalzumeus 是一家位于日本大垣市的小型软件公司。他的主要产品——Bingo Card CreatorAppointment Reminder——都是用 Ruby 编写的。他于 2004 年毕业于华盛顿大学,获得计算机科学 BS/CS 学位和东亚研究 BA 学位。

© 2011 1542-7730/11/0300 $10.00

acmqueue

最初发表于 Queue 第 9 卷,第 3 期
数字图书馆 中评论本文





更多相关文章

Jinnan Guo、Peter Pietzuch、Andrew Paverd、Kapil Vaswani - 使用机密联邦学习的可信 AI
安全性、隐私性、问责制、透明度和公平性原则是现代 AI 法规的基石。经典 FL 的设计非常强调安全性和隐私性,但以透明度和问责制为代价。CFL 通过将 FL 与 TEE 和承诺相结合,弥合了这一差距。此外,CFL 还带来了其他理想的安全属性,例如基于代码的访问控制、模型机密性和推理期间模型的保护。机密计算的最新进展(如机密容器和机密 GPU)意味着可以无缝扩展现有 FL 框架以支持 CFL,且开销较低。


Raluca Ada Popa - 机密计算还是密码学计算?
通过 MPC/同态加密与硬件飞地的安全计算在部署、安全性和性能方面存在权衡。关于性能,您想到的工作负载非常重要。对于简单的求和、低阶多项式或简单的机器学习任务等简单工作负载,这两种方法都可以在实践中使用,但对于复杂的计算(如复杂的 SQL 分析或训练大型机器学习模型),目前只有硬件飞地方法对于许多实际部署场景来说足够实用。


Matthew A. Johnson、Stavros Volos、Ken Gordon、Sean T. Allen、Christoph M. Wintersteiger、Sylvan Clebsch、John Starks、Manuel Costa - 机密容器组
此处提供的实验表明,Parma(Azure 容器实例上驱动机密容器的架构)增加的额外性能开销低于底层 TEE 增加的额外性能开销的 1%。重要的是,Parma 确保基于证明报告的容器组的所有可达状态的安全不变性。这允许外部第三方与容器安全地通信,从而实现各种需要机密访问安全数据的容器化工作流程。公司获得了在云中运行其最机密工作流程的优势,而无需在其安全要求上妥协。


Charles Garcia-Tobin、Mark Knight - 通过 Arm CCA 提升安全性
机密计算具有通过将监管系统移出 TCB 来提高通用计算平台安全性的巨大潜力,从而减小 TCB 的大小、攻击面和安全架构师必须考虑的攻击向量。机密计算需要在平台硬件和软件方面进行创新,但这些创新有可能增强对计算的信任,尤其是在第三方拥有或控制的设备上。机密计算的早期消费者将需要自行决定他们选择信任的平台。





© 保留所有权利。

© . All rights reserved.