更新日志:OWASP Top 10 项目
已发表: 2022-03-11在过去十年中,Web 应用程序的复杂性呈爆炸式增长。 它们已经从用于联系表格和民意调查的简单容器发展成为成熟的应用程序。 我们可以将它们与大型桌面应用程序进行比较,无论是在大小上还是在性能上。 随着复杂性的急剧上升和功能丰富的应用程序数量的增加,投入大量时间和精力使所有应用程序组件尽可能安全已成为必要。 互联网用户的大量增加使得解决保护数据和应用程序用户的问题变得更加重要。 有大量的威胁试图潜入并给所有相关人员带来严重的头痛。
2001年,一个新的组织进入了舞台。 它的目标是解决影响网站和应用程序的安全问题。 它被恰当地命名为开放 Web 应用程序安全项目 (OWASP)。 如今,它发布资源,组织会议,并提出有关 Web 和应用程序安全的标准。 Web 应用程序安全的事实标准是 OWASP 十大项目。 它列出了十个最普遍的安全威胁。 决定什么进入的影响因素是大量的数据和社区反馈。 2017 年底,该项目进行了更新。 对许多现代 Web 应用程序至关重要的几个新问题获得了它们的位置,而有些则从列表中逃脱了。
本文对原始列表进行了补充,并说明了列表的最新更改。 它描述了威胁,试图提供清晰的例子以便于理解,并提出应对安全威胁的方法。
从 OWASP 前 10 名列表中删除的问题
在 2017 年更新之前,2013 年的列表是最新的。 考虑到现在构建和使用 Web 应用程序的方式发生了变化,只有进行彻底的修改才有意义。 微服务正在分一杯羹,新的酷炫的框架正在取代普通的代码战斗装备。 这些事实意味着之前列出的一些威胁已被删除,而一些新威胁则取而代之。
我们将确保在本文中刷新我们对长期被遗忘的问题的记忆,并介绍新的坏狼。 了解历史是避免重蹈覆辙的唯一可靠方法。
跨站请求伪造
跨站点请求伪造(CSRF)是该项目最近迭代中的“失败者”之一。 它消失了,因为许多现代 Web 框架都包含 CSRF 防御机制。 因此,将您的应用程序暴露在威胁之下的可能性会迅速降低。
不管 CSRF 退出列表,刷新我们的记忆还是不错的。 让我们确保它不会比以往更强大。
从本质上讲,CSRF 是一种感觉就像烟雾弹的漏洞利用。 攻击者诱骗毫无戒心的用户在 Web 应用程序中执行不需要的请求或操作。 简而言之,攻击者强迫其受害者向第三方应用程序发送请求,而受害者不知道该请求已被发送。 该请求可能是一个用于检索资源的 HTTP GET 请求,或者更糟糕的是,一个 HTTP POST 请求会更改受害者控制下的资源。 在攻击过程中,受害者认为一切都很好,通常甚至没有注意到后台发生了什么。 空气净化后,损坏或丢失了什么,没有人知道发生了什么。
目标应用程序中成功的先前用户身份验证使陷阱有效。 用户在攻击之前的某个时间点登录了应用程序。 应用程序向受害者发送了一个 cookie 来记住他们。 一旦 Web 浏览器发送恶意请求,cookie 就会与任何潜在的有效负载一起自动发送,并且应用程序不会反对将请求提供给它已经知道的用户。
最著名的例子之一是欺骗用户将钱从他们的账户转移到攻击者控制的账户。 用户登录电子银行系统以检查其帐户余额。 之后,他们访问一个在线论坛,查看新手机的评论。 一名攻击者用炸药钓鱼,发布了一条评论,其中包括一张带有看似损坏的图像超链接的图像。 攻击者没有使用真正的超链接,而是使用电子银行系统内部使用的超链接将资金从账户 A 转移到账户 B: https://payments.dummybank.com?receiver=attacker&amount=100
://payments.dummybank.com?receiver=attacker&amount=100。 银行系统将经过身份验证的用户设为发送方,将“接收方”参数中的值设为资金的接收方。 当然,攻击者将他们的离岸账户指定为接收方。
由于浏览器在呈现页面时会自动加载图像,因此请求发生在后台。 如果银行的支付系统使用 HTTP GET 请求实现汇款,那么没有什么可以阻止灾难的发生。 请注意,该示例很简单,并且很可能传输不是通过 HTTP GET 处理的。 然而,攻击者可以稍微困难一点,设法改变论坛的 HTML 消息发布表单中的属性“action”。 然后浏览器将请求发送到银行的支付系统,而不是论坛的后端。
偷钱只是众多例子之一。 更改用户的电子邮件地址或进行意外购买也属于此类。 正如经常发生的那样,社会工程和一些技术知识是防止软件工程错误的有效手段。
在设计系统时,请牢记以下几点:
- 不要使用 HTTP GET 请求来封装修改资源的操作。 您应该只使用这些请求来检索信息。 请记住,经验法则是使 GET 请求具有幂等性。
- Do ,当使用 HTTP POST 请求在内部传输数据时,倾向于以 JSON、XML 或其他格式发送数据,而不是将参数编码为查询字符串。 使用重要的数据格式可以降低有人创建虚假 HTML 表单的危险,该表单会将数据发送到您的服务。
- 请确保在您的 HTML 表单中创建并包含一个唯一且不可预测的标记。 这些令牌对于每个请求也应该是唯一的。 检查此类令牌的存在和正确性将降低发生威胁的风险。 要找出令牌并在他们的虚假请求中使用它,攻击者需要访问您的系统并直接从那里获取令牌。 由于令牌是一次性的,因此它们不能在恶意代码中重复使用它们。
当与跨站点脚本 (XSS) 结合使用时,此漏洞会产生更严重的影响。 如果攻击者可以将恶意代码注入到喜欢的网站或应用程序中,那么攻击的范围就会变得更加严重和危险。 更关键的是,如果可能发生 XSS 攻击,攻击者可以绕过一些针对 CSRF 的保护机制。
请记住,CSRF 并没有消失,只是不像以前那么普遍了。
未经验证的重定向和转发
许多应用程序在完成一个操作后,会将用户重定向或转发到同一应用程序的另一部分,甚至是其他应用程序。 例如,成功登录应用程序会触发重定向到主页或用户最初访问的页面。 很多时候,目的地是表单操作或链接地址的一部分。 如果处理重定向或转发的组件不能确保目标地址确实是应用程序生成的地址,就会出现潜在威胁。 这是一个称为“未经验证的重定向和转发”的安全漏洞。
未经验证的重定向和转发被认为是危险的两个主要原因是网络钓鱼和凭据劫持。 攻击者可以设法更改重定向/转发目标位置,并将用户发送到与原始应用程序几乎无法区分的恶意应用程序。 毫无戒心的用户会向恶意第三方泄露他们的凭据和机密信息。 在他们意识到发生了什么之前,已经为时已晚。
例如,Web 应用程序通常会通过支持重定向到上次访问的页面来实现登录。 为了能够轻松地做到这一点,HTML 表单的 action 属性可能类似于http://myapp.example.com/signin?url=http://myapp.example.com/puppies 。 您是小狗的超级崇拜者,因此安装浏览器扩展程序将网站图标替换为您最喜欢的小狗的缩略图是有意义的。 不幸的是,这是一个狗咬狗的世界。 浏览器扩展的作者决定兑现你无条件的爱,并引入一个额外的“功能”。 每次您访问您最喜欢的小狗粉丝网站时,它都会将表单操作属性中的“url”参数替换为指向他们自己网站的链接。 由于该网站看起来完全一样,当您提供信用卡详细信息来购买小狗扑克牌时,您实际上是在资助恶意攻击者,而不是您的爱好。
解决该漏洞涉及通过确保它是预期位置来检查目标位置。 如果框架或库执行完整的重定向或转发逻辑,则检查实现并在必要时更新代码是有益的。 否则,您需要进行手动检查以防止攻击。
您可以进行多种类型的检查。 为了获得最佳保护,请结合使用几种方法,而不是只使用其中一种。
- 验证传出 URL,确保它指向您控制的域。
- 不要使用显式地址,而是在前端对其进行编码,然后在后端对其进行解码和验证。
- 准备可信 URL 的白名单。 仅允许转发和重定向到列入白名单的位置。 更喜欢这种方法来维护黑名单。 黑名单通常只有在发生不好的事情时才会填充新项目。 白名单更具限制性。
- 采用 LinkedIn 和其他一些应用程序使用的方法:向您的用户展示一个页面,要求他们确认重定向/转发,明确他们将离开您的应用程序。
合并问题
清单上的大多数问题都可以被标记为实施中的缺陷,要么是由于缺乏知识,要么是对潜在威胁的深入调查不够深入。 这两个原因都可以归结为缺乏经验,未来考虑它们的解决方案很容易——只要确保学得更多、更透彻。 一个特别棘手的问题依赖于做出太多假设的危险(但非常人性化)特征以及开发和维护复杂计算机系统的困难。 属于此类别的漏洞是“访问控制损坏”。
损坏的访问控制
漏洞是由于应用程序某些部分的授权和访问控制不足或完全缺乏而导致的。 在 OWASP 十大项目的先前迭代中,存在两个问题:不安全的直接对象引用和缺少函数级访问控制。 由于它们的相似性,它们现在合并为一个。
直接对象引用
URL 中经常使用直接对象引用来标识正在操作的资源。 例如,当用户登录时,他们可以通过单击包含其个人资料标识符的链接来访问他们的个人资料。 如果相同的标识符存储在数据库中并用于检索个人资料信息,并且应用程序假设人们只能通过登录页面访问个人资料页面,则更改 URL 中的个人资料标识符会公开另一个用户的个人资料信息。
设置删除配置文件表单http://myapp.example.com/users/15/delete的 URL 的应用程序清楚地表明该应用程序中至少有 14 个其他用户。 在这种情况下,弄清楚如何获得其他用户的删除表单并不是火箭科学。
此问题的解决方案是对每个资源执行授权检查,而不假设只能采用某些路径来访问应用程序的某些部分。 此外,删除直接引用并使用间接引用是向前迈出的又一步,因为它使恶意用户很难弄清楚引用是如何创建的。
在开发过程中,作为预防措施,写下一个简单的状态机图。 让状态代表应用程序中的不同页面,并转换用户可以采取的操作。 这使得列出所有需要特别注意的转换和页面变得更加容易。
缺少功能级访问控制
缺少函数级访问控制与不安全的直接对象引用非常相似。 在这种情况下,用户修改 URL 或其他一些参数以尝试访问应用程序的受保护部分。 如果没有适当的访问控制检查访问级别,则用户可以获得对应用程序资源的特权访问或至少对其存在的一些了解。
借用直接对象引用的示例,如果应用程序假设访问删除表单的用户是超级用户只是因为超级用户可以看到删除表单的链接,而无需进行任何进一步的授权,那么就会出现巨大的安全漏洞。 指望某些 UI 元素的可用性不是正确的访问控制。
通过始终确保在应用程序的所有层中执行检查来解决该问题。 前端界面可能不是恶意用户访问您的域层的唯一途径。 此外,不要依赖用户传递的有关其访问级别的信息。 执行适当的会话控制并始终仔细检查接收到的数据。 仅仅因为请求正文说用户是管理员,并不意味着他们是管理员。
损坏的访问控制现在结合了与访问控制不足相关的所有问题,无论是在应用程序级别还是系统级别,例如文件系统的错误配置。
OWASP Top 10 列表中的新问题
新前端框架的出现和新软件开发实践的采用将安全问题转移到全新的主题。 新技术还设法解决了我们之前手动处理的一些常见问题。 因此,修改清单并根据现代趋势对其进行调整变得有益。
XML 外部实体
XML 标准提供了一个鲜为人知的概念,称为外部实体,它是文档数据类型定义 (DTD) 的一部分。 它允许文档作者指定指向外部实体的链接,然后可以在主文档中引用并包含这些链接。 一个非常简单的例子是:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY> <!ENTITY bar "baz"> ]> <foo> &bar; </foo>
在解析过程中,引用&bar;
被定义实体的内容替换,从而产生<foo>baz</foo>
。
如果应用程序接受外部输入并将其直接包含在 XML 文档定义中,而无需任何检查,那么大范围的数据泄漏和攻击将成为可能。
神奇的是,实体不必是简单的字符串——它可以是对文件系统上文件的引用。 XML 解析器很乐意获取指定文件的内容并将其包含在生成的响应中,这可能会泄露敏感的系统信息。 如 OWASP 所示,通过将实体定义为
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
此漏洞的一个特别麻烦的“功能”是可以轻松执行拒绝服务攻击。 一种简单的方法是列出/dev/random
等无限文件的内容。 另一种是创建一系列实体,每个实体多次引用前一个实体。 这会将最终引用变成一个可能非常宽且深的树的根,其解析可能会耗尽系统内存。 这种攻击甚至被称为十亿笑。 如 Wikipedia 所示,定义了一系列虚拟实体,为攻击者提供了在最终文档中包含十亿个 lol 的机会。

<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>
可以通过使用不太复杂的数据格式来防止 XML 外部实体攻击。 JSON 是一个很好的替代品,前提是由于可能会受到攻击而采取了一些预防措施。 必须更新 XML 库,同时禁用外部实体处理和 DTD。 与往常一样,在使用来自不受信任来源的数据或将其包含在文档中之前,请对其进行验证和清理。
不安全的反序列化
在编写代码时,开发人员有权使用他们编写的代码来控制他们正在开发的系统。 控制只写很少甚至不写代码的第三方系统的行为会有多棒? 由于人并不完美,图书馆也有缺陷,这绝对是可能的。
应用程序状态和配置通常被序列化和存储。 如果序列化数据与当前用户紧密耦合,有时浏览器会充当存储引擎。 一个试图聪明并节省处理时间的应用程序可以使用 cookie 来标记用户已登录。由于 cookie 只能在登录成功后创建,因此将用户名存储在 cookie 中是有意义的。 然后根据 cookie 的存在和内容对用户进行身份验证和授权。 如果人们没有恶意,就不会出错。 老实说,他们也不应该好奇。
如果好奇的用户在他们的机器上发现了一个 cookie,他们可能会看到如下内容:
{"username": "joe.doe", "expires": "2018-06-01 10:28:16"}
一个完全有效的 Python 字典,序列化为 JSON,没什么特别的。 好奇的用户可能会更改到期日期以防止应用程序强制注销。 更加好奇的用户可能会尝试将用户名修改为"jane.doe"
。 如果这个用户名存在,它将为现在可以访问私人数据的毫无戒心的用户打开一个全新的世界。
将数据序列化为 JSON 并保持一切透明的简单示例远非发生在您身上的最糟糕的事情。 如果攻击者成功修改了一些序列化数据,他们可能会以强制系统执行任意代码的方式对其进行修改。
假设您正在构建一个 REST API,它允许人们用 Python 编写自己的机器学习模型并将它们上传到您的服务。 该服务将评估上传的模型并使用您的数据集对其进行训练。 这使人们可以使用您的计算资源和大量可用的数据集来快速轻松地构建模型。
该服务不会以纯文本格式存储代码。 用户腌制他们的代码,使用他们的私钥对其进行加密,然后将其发送到 API 进行训练。 当服务需要运行模型时,它会解密代码、解压缩并运行它。 棘手的部分是 pickle 协议是不安全的。 可以以允许在反序列化期间执行任意恶意代码的方式构造代码。
Python 的 pickle 协议允许类定义方法__reduce__
,该方法返回有关如何反序列化自定义对象的信息。 支持的返回值之一是两个参数的元组:一个可调用的和一个要传递给可调用的参数的元组。 考虑到您的 ML 模型训练系统旨在提供代码结构的最大灵活性,可以编写以下代码:
class Algo(object): def run(self): pass def __reduce__(self): import itertools return (list, (itertools.count(1), ))
一旦对象需要被反序列化(unpickled),一个函数list
就会被调用,只有一个参数。 函数list
是 Python 中的列表构造函数,函数itertools.count
生成一个无限的值迭代器,从传递的参数开始。 将无限迭代器转换为有限列表可能会对系统的性能和稳定性造成灾难性后果。
解决此类漏洞的唯一真正方法是选择不反序列化来自外部来源的数据。 如果无法做到这一点,建议使用校验和或数字签名来防止对可能被恶意用户修改的数据进行反序列化。 此外,尝试设置与主系统分离的沙盒环境,以限制可能出现的问题的影响。
当使用外部库对数据进行反序列化时,例如来自 XML 或 JSON,请尝试选择那些允许您在执行实际反序列化过程之前进行对象类型检查的库。 这可能会捕获意外的对象类型,其唯一目的是损害您的系统。
与您的应用程序执行的所有其他操作一样,强制执行广泛的日志记录和监控。 反序列化经常发生或比正常失败更多是坏事正在发生的信号。 尽早发现问题。
记录和监控不足
您花了多少时间来确保记录应用程序中发生的所有警告和错误? 您是只存储代码中发生的错误,还是记录验证错误? 当您的域的业务规则未得到满足时会发生什么? 未能在您的应用程序中保留所有错误和可疑活动会导致安全性和数据泄露。
想象以下场景。 与大多数应用程序一样,您的应用程序包含一个登录页面。 该表单有两个字段,一个用于输入电子邮件,另一个用于输入密码。 如果用户尝试登录并且他们提供了错误的密码,他们可以重试。 不幸的是,错误尝试的次数不受限制,因此在尝试 N 次失败后,登录页面不会被锁定。 攻击者可以利用这个机会,在给定一封正确的电子邮件的情况下,继续从彩虹表输入密码,直到一个组合最终成功。 如果您的应用程序足够安全,并且您在将密码输入数据库之前对密码进行了哈希处理,那么这种特殊的攻击将不起作用。 但是,您有识别入侵的机制吗?
仅仅因为这一次尝试未能打开您的登录页面,并不意味着其他人不会。 登录页面也可能不是您拥有的唯一潜在后门。 如果不是出于其他原因,有人可能会尝试对您使用损坏的访问控制。 即使是精心设计的应用程序也应该知道有人试图攻击它们,即使这可能是不可能的。 不过,它总是这样。
为了尽最大努力保护自己免受此类攻击,请执行以下步骤:
- 记录应用程序中发生的所有故障和警告,无论是代码中引发的异常还是访问控制、验证和数据操作错误。 存储的所有信息都必须被复制并保留足够长的时间,以便可以进行回顾性检查和分析。
- 确定格式和持久层很重要。 拥有任意文本格式的大文件很容易实现; 稍后处理它不是。 选择一种便于存储和读取数据的存储选项,以及一种便于快速(反)序列化的格式。 将 JSON 存储在数据库中可以实现快速访问,从而简化使用。 通过定期备份来保持数据库较小。
- 如果您正在处理重要且有价值的数据,请跟踪可用于审计最终状态的操作。 实施防止数据篡改的机制。
- 让后台系统分析日志并在出现问题时提醒您。 检查——就像测试用户是否反复尝试访问应用程序的受保护部分一样简单——帮助。 但是,不要用虚拟检查使系统过载。 监控系统必须作为单独的服务运行,并且不得影响主系统的性能。
解决问题时,请特别注意不要将错误日志泄露给外部用户。 不这样做会使您容易受到敏感信息的泄露。 日志记录和监控应该帮助您解决问题,而不是攻击者更有效地完成工作。
下一步
了解 Web 应用程序中的潜在威胁和漏洞非常重要。 更重要的是开始在您的应用程序中识别它们并应用补丁来删除它们。
注意应用程序安全是软件开发项目所有步骤的重要组成部分。 软件架构师、开发人员和测试人员都必须将软件测试程序整合到他们的工作流程中。 在软件开发过程的适当步骤中使用安全检查表和自动化测试以降低安全风险是有益的。
无论您是分析现有应用程序还是开发新的应用程序,您都应该研究 OWASP 应用程序安全验证标准项目 (ASVS)。 该项目的目标是开发应用程序安全验证的标准。 该标准列举了开发安全 Web 应用程序的测试和要求。 这些测试被分配了从一到三的级别,其中一级表示危险最小,三表示潜在威胁最高。 该分类允许应用程序管理员决定哪些威胁更可能和更重要。 不必在每个应用程序中包含每个测试。
新的和现有的 Web 应用程序项目,尤其是那些遵循敏捷原则的项目,都受益于为保护其应用程序而进行的结构化计划。 如果您决定使用 OWASP 安全知识框架,则 OWASP ASVS 测试的规划会更容易。 它是一个用于管理面向安全测试的冲刺的应用程序,附带一组关于如何解决常见安全问题的示例,以及基于 OWASP ASVS 的易于遵循的清单。
如果您刚刚开始探索 Web 应用程序的安全性并需要一个安全的沙盒游乐场,请使用来自 OWASP 的 Web 应用程序实现——WebGoat。 这是一个故意不安全的 Web 应用程序实现。 该应用程序将指导您完成课程,每节课程都集中在一个安全威胁上。
在应用程序开发期间,请确保您:
- 识别威胁并确定优先级。 定义哪些威胁可能实际发生并对您的应用程序构成风险。 确定威胁的优先级并确定哪些威胁值得进行最多的开发和测试工作。 如果您正在为静态博客提供服务,那么投入大量精力来解决日志记录和监控不足的问题并没有多大意义。
- 评估应用程序的架构和设计。 在应用程序开发的后期阶段,一些漏洞很难解决。 例如,如果您打算执行第三方代码,并且没有使用沙盒环境的计划,那么将很难防御不安全的反序列化和注入攻击。
- 更新软件开发流程。 针对 Web 应用程序威胁的测试必须尽可能地成为一个自动化过程。 通过尝试发现安全漏洞的自动化测试来增强您的 CI/CD 工作流程是有益的。 您甚至可以利用现有的单元测试系统来开发安全测试并定期运行它们。
- 学习和改进。 问题和漏洞列表不是静态的,绝对不限于十个或十五个威胁。 新的功能和想法为新型攻击打开了大门。 了解 Web 应用程序安全领域的当前趋势以保持最新状态非常重要。 应用所学; 否则,你就是在浪费时间。
结论
尽管如其名称所示,OWASP 十大项目仅列出了十个安全漏洞,但仍有数以千计的陷阱和后门威胁您的应用程序,最重要的是威胁您的用户及其数据。 确保保持警惕并不断更新您的知识,因为技术的变化和改进既有好处也有坏处。
哦,还有,别忘了,世界不是非黑即白的。 安全漏洞不会单独出现。 它们经常交织在一起。 接触到一个通常意味着一堆其他人在拐角处,等着抬起他们丑陋的头,有时,即使这不是你的错,作为系统安全开发人员,你仍然需要修补漏洞来遏制网络犯罪。 例如,请参阅被黑的信用卡号码仍然可以在 Google 中使用。