本文要点:
应用程序层加密是“将更多基础架构和 IT 职责移交给开发人员或 DevOps 角色”这一趋势的一部分。
端到端加密是一种越来越流行的应用层加密类型。
这种加密方式使组织可以使用密钥管理和策略来实施访问控制。
根据平台的不同,将代码安全地交付给最终用户的方式也大不相同。基于浏览器的 JavaScript 是最具挑战性的。
使用这些方法可以极大地提高隐私保护水平和安全性,因此尽管前方挑战重重,但这条路还是值得去走的。
只要处理的是敏感用户数据,谁都会担心数据泄露的风险。我们知道加密技术可以降低数据泄露的负面影响,但大多数加密方法都属于 TLS 和 VPN 等基础架构级元素,而不是位于应用程序层上。应用层加密和端到端加密可以成为我们工具包中的强大工具,但是作为开发人员,我们如何在不引入错误或降低数据效率的前提下,安全地向应用添加加密措施呢?
在本文中,我们讨论了应用层加密的优缺点。我们将介绍浏览器中应用层加密的攻击面、它与原生客户端的显著不同之处,以及 WebCrypto 在这里有哪些帮助。
威胁图景
数据泄漏给企业带来的声誉、财务和人员影响可能会非常严重。一些帮助保护最终用户隐私的新法规是重要的进步举措,但它们也伴随着潜在的巨额罚款。
研究表明,加密是减少数据泄露的影响和降低成本的最有效的技术安全措施之一。当攻击者获得的是加密的数据集时,他们要么必须攻击另一个系统来获取密钥,要么只能用上元数据和辅助信息,却碰不到那些"值钱的数据"。
加密通常集中在“基础架构层”元素上,例如 TLS、VPN、数据库加密标志和全盘加密等。这些是我们工具箱中的重要工具,但它们依赖的是那些关于基础架构的假设,而不是应用代码本身。
实际上,如果我们观察最近的数据泄露问题就会发现,那些大公司肯定使用了 TLS 和静态数据库加密,但到头来也无法阻止数据泄漏。例如,Capital One最近被黑,敏感财务信息被盗。谷歌相册意外地允许错误的用户访问其他用户的照片和视频。这些错误本可以通过应用层或端到端加密来预防,或至少缓解的。
作为开发人员,基础架构不是我们的强项,有时候也不在我们的职责范围之内,因此相比加密来说我们更重视功能开发。但是对于那些真正关心深度防御技术的人们来说,向应用本身添加加密措施是很有意义的。应用层加密可以让我们的系统免受基础架构级故障、TLS 的已知缺陷和某些服务端漏洞的影响。
左移加密:什么是应用层加密?
将更多安全性、运维和测试工作转移到开发过程中的实践(称为左移,shift-left)正在改善软件的敏捷性、可靠性和效率。这也意味着我们需要在应用开发中实现最佳安全实践,而不是在出现问题时亡羊补牢。但是,绝大多数开发人员都不是安全或加密专家;与此同时,安全团队对 IT 和开发中安全状况的控制权也比以往要少得多。
应用层加密(左移加密)是这种趋势的一部分。这意味着让开发人员可以更好地掌控要加密哪些内容,以及谁可以获取密钥这些问题。在某些情况下,可能只有用户才有密钥。在其他情况下,应用层加密可以成为数据管理上附加的访问控制层,从而提供深度防御能力。
顾名思义,应用层加密是直接添加到应用代码库中的,由应用逻辑控制对关键材料的访问。因此,你可以认为数据本身在整个生命周期中都是加密的,而不只依赖网络或磁盘加密。
最为人熟知的应用层加密场景,是 Signal 和 WhatsApp 提供的那种端到端加密通信应用,因此我们来仔细研究一下这些应用是如何工作的。简化一下,基本上是这样的方式:
在这个简单的示例中,我们已经看到了应用层加密的一些能力:
用户考虑的是他们要同谁交谈,而不是访问控制或加密这些事情;但实际上,正是用户在制定策略决策。
访问控制逻辑被加密实施(并由拥有密钥的人员定义),这种逻辑并不只是(攻击者可能成功入侵的)服务器上的一些规则那么简单。
服务器无法访问纯文本数据,因此针对服务器的攻击(例如 SQL 注入、内部威胁和根级别的破坏)无法获取数据。
请注意,这是端到端加密的一个示例,但并非所有应用层加密都是端到端的。此外,像这样的应用仍然需要 TLS 和其他基础架构层加密来强制执行身份验证等操作、预防重放攻击并解决其他许多问题。
为什么 TLS 不够用
当我们考虑 TLS 时,我们想的是在源头加密数据,并在服务器上解密。但是这种过度简化的图景隐藏了 TLS 的现实局限。
实际情况中,TLS 加密传输数据时会忽略静态数据,这会影响传输两端的安全性。它还完全忽略了 HTTPS 终止后的数据状况,这些数据可能位于你想不到的网络边缘地带;例如在你的负载均衡器上。
那么,应用其他位置的加密状况又是怎样的呢?如果你的加密措施高于平均水平,那么你已经在应用中编写了经过测试的强大代码来加密静态数据,并在网络中使用了 HTTPS 和 IPSec,还启用了透明数据库加密。
我们可以通过这种方法基本做到“所有位置都加密”,但是随着数据在系统中不断移动,它在每一步都会被解密和重新加密。接触纯文本数据的每个点都是潜在的漏洞,这会带来很大的攻击面。你要问自己一个问题:“为什么这些中间服务都需要纯文本数据?”其实它们可能用不着。
基础架构层加密还存在一些安全隐患,因为基础架构中一些想不到的部分可能会获取数据。例如,即便你的数据库加密过了,但其备份却没有。或者你的健康监控系统可能会以纯文本格式记录敏感数据,甚至可能将其发送给第三方(太可怕了)。之所以会出现这些安全漏洞,是因为不同的员工或部门负责的安全领域太细碎了:
在移动端,你的开发团队或供应商必须编写一些安全代码(或至少要正确实现 HTTPS)。或者你的移动设备管理(MDM)系统封装了数据,或者你可能要依靠用户来选中“加密通话”选项,然后由 OS 供应商处理这里的敏感操作。
在网络中,IT 或 DevOps 部门负责提供证书并确保 HTTPS 配置正确,这做起来往往很困难。
在服务器上,你要依靠 IT 和 DevOps 部门来保障对系统内部访问的安全性,并且要依靠云提供商和数据库供应商来实现“透明”数据库加密。
这些解决方案中都使用了不同的密码、库和密钥大小。你要依赖很多人去做很多正确的事情。这就是问题所在。
交付可信赖的代码
加密与通信息息相关;数据由一方写入并加密,然后由另一方接收并解密。发送者和接收者都必须有一个知道如何加密和解密的应用,并且要能信任这个应用,确认它能正确操作。但这说起来容易做起来难。
如果加密代码是恶意的怎么办?攻击者可以做什么?最简单的攻击手段是让应用完全按预期运行,同时将未加密的消息发送给恶意方。当然也可以有更精密的途径:加入隐藏的漏洞以削弱密级、破坏公钥,等等。但它们的本质是一样的:都是帮助坏人获取秘密信息的一段代码。
因此我们来谈谈代码交付。考虑两个使用手机应用通信的用户,他们之间的信任链大致是这样的:优秀的程序员编写良好的加密代码,将其编译为应用,对应用进行数字签名,然后通过 TLS 将其上传至应用商店。用户通过 TLS 下载应用,操作系统检查数字签名是否“受信任”,然后用户运行应用以进行加密通信。注意,该协议本身就是应用层加密的数据交换。像 Debian Linux 这样的系统也有类似的协议,用于安装和升级服务器及桌面应用。
受信任的应用下载这一步可能会出现很多问题:用户可能下载了应用的恶意版本;操作系统供应商可能会破坏应用数字签名的检查步骤;攻击者可能诱使用户安装应用的较旧且易受攻击的版本(或不升级到新版)。任何一种攻击手段都会影响端到端加密通信的可信度。但在大多数情况下,这套流程还是没什么问题的。
应用级加密通常以在移动设备、便携式计算机或服务器上运行的原生代码来实现,并且可以使用上文所述的这种协议来交付受信代码。但是,现代应用往往会有一个基于浏览器的主要组件,即便是关键敏感信息也会在其中处理。
那么 Web 端该怎么办?
Web 端的代码交付模型和应用端有很大区别。当用户要进行安全会话时,他们会访问一个网页。浏览器按需下载一些基于 TLS 的 JavaScript 代码。除了警告用户 TLS 连接不正确之外,标准的代码交付协议就到此为止了。它完全依赖 TLS。交付的 JavaScript 代码需要执行应用层加密,并且要确保没有任何恶意代码会将未加密的文本发送给坏人。
为什么这里会有问题?举例来说,假设我们的安全声明是让数据在一个浏览器中加密,在另一浏览器中解密,并且介于两者之间的网络服务器看不到数据(除非关闭警告标志等安全措施)。要破坏这种声明,服务器只需在应用启动时交付恶意 JavaScript 代码即可。因此,只要攻击者可以控制交付代码的服务器,或利用 DNS 和 TLS 的某个漏洞,就可以在不破坏任何加密步骤的前提下实施攻击。恶意代码可以只发送到特定目标,从而躲避安全研究人员的检测。
实际上,随着应用更新和持续集成的速度越来越快,类似的攻击也可以针对移动和桌面应用实施。许多现代应用使用动态代码技术将至少部分代码实时交付给应用;许多桌面应用会随意更新自己的代码。这使攻击者有很多时间点可以劫持代码更新,但同样也让安全团队能够快速修补应用。也就是说,基于浏览器的攻击已经不是什么稀奇的事物了。
安全和加密社区中有人谈到这个问题时会说,你不应该实施基于浏览器的加密,或者如果你这样做了,就不能声明它是端到端安全的;至少这种做法会造成一种虚假的安全感。我们不同意这种说法。这里确实存在漏洞,但是作为开发人员,无论如何我们都应该这样做,因为很简单的事实是,人们会在 Web 上执行很多关键的安全事务。
为什么浏览器中的应用层加密仍具有良好的安全性
尽管存在代码交付问题,但在浏览器中执行应用层加密仍可以显著提高任何系统的整体安全性。这是因为安全性并不是只有零分和满分。在现代服务器基础架构中,很少有单个浏览器只与执行每个任务的单个 Web 服务器对话的状况;现代系统的设计要复杂得多。
例如,假设你的 Web 应用使用了 HTTPS,并实施了基于浏览器的端到端加密,但是它有一个 SQL 注入漏洞。这个漏洞的本质是,攻击者引导应用诱使数据库转储敏感数据(讽刺地是这些步骤都会走 HTTPS)。但在我们的示例中,数据是端到端加密的,因此数据库只包含加密的消息。如果没有应用层加密,那么坏人将获得更敏感的信息:纯文本消息。请注意,单靠这个漏洞,攻击者是无法更改代码以注入恶意 JavaScript 的;基于浏览器的加密代码仍然有效。
另一方面,如果攻击者可以利用 API 服务器上的一个远程执行代码漏洞,并且可以动态修改 JavaScript 或向其中注入恶意代码,那么他们只需简单地添加代码就能破坏端到端加密,并将纯文本数据发送给自己。
这只是两个例子,一个可以破坏应用层加密,另一个不能;但是端到端加密可以阻止其他无数攻击途径:也许你的一位讨厌的员工想要获取关于名人的私人信息,但无权访问相关代码。也许你将 Postgres 数据库备份到了 S3 存储桶上,却无意中将其留在了 Web 上公开出来。也许攻击者可以破坏 TLS,但他们只能被动行事。他们可以窃听,但不能注入代码。
如我们所见,即使存在代码交付层面的挑战,浏览器中的应用层加密也可以提供深度防御。在下一节中,我们将讨论应对这些挑战的方法。
改进面向浏览器的可信代码交付
有多种方法可以提高浏览器中应用层加密的安全性。第一道防线是使用良好的,受信任的代码。现代应用的开发速度比以前快很多,因为我们可以复用许多在网上找来的代码;但如果这些运行在用户浏览器中的代码是恶意的或易受攻击的,就会严重破坏加密防线。
保护提供代码的服务器也是很重要的。在这种服务器上分配访问控制权限时,请遵循最小特权原则。请使用多方控制进行管理和代码部署工作。这将大大降低内部攻击的风险。
还有一些不常用的代码交付设置会指示浏览器采取额外的预防措施。这些设置并非默认选项,因为它们在某种程度上降低了开发和集成过程的灵活性;但无论你的应用是否做了加密,它们提供的安全性都是值得你多付出一些劳动的:
HTTP 严格传输安全性(HSTS):指示浏览器始终通过 HTTPS 加载页面。这样可以防止降级攻击。例如,即便攻击者可以将你的 DNS 重定向到恶意服务上,他们也无法将连接降级为未加密的 HTTP。
严格的内容安全策略(CSP):将加载代码的安全来源列入白名单。在复杂的现代 JavaScript 世界中,这会阻止应用从你不知道的远程资源动态加载代码。
子资源完整性(SRI):只加载你信任的脚本。这会使用加密哈希来标记受信任的脚本。如果攻击者修改了加密应用的 JavaScript 就会更改哈希值,于是这些代码就不会加载。
此外,还有一个相对较新的浏览器 API,可帮助有效且安全地传递密码原语:这就是 WebCrypto API。它提供了底层密文、哈希和其他加密组件。这很有用,因为这样你就不必在 JavaScript 中包含这些密文了。浏览器可以直接实现它们,并能利用本地原生执行甚至硬件加速的优势。它不能阻止某些攻击方式(例如简单将未加密的数据副本发送给坏人),但 WebCrypto 确实能让基于浏览器的加密方法更标准化,更容易使用。
其他陷阱
安全代码交付并不是实施应用层加密时面临的唯一挑战。最大的问题是,大多数加密库相对较难安全使用,并且难以在不同的编程语言和平台中一致地实现。当你在浏览器中加密某些内容并在应用上解密它们时,你可能需要使用来自不同语言(Android、iOS 和 JavaScript)的三种实现,它们都使用完全相同的密码和模式。
这些模式的安全操作不是很容易理解。例如,广受欢迎的 AES 加密是安全的,但是将其与不安全的模式(例如 ECB,Java 中的默认模式)配对就不够安全了。将 AES 与 GCM 配对被认为是最佳实践,但即便是 GCM 也有自己的缺陷。如果你使用相同的密钥加密了太多数据,或者对初始化向量/随机数进行了错误操作,就可能泄漏密钥信息,这是其他一些模式所没有的缺陷。
一个错误可能会使加密数据无法恢复,甚至更糟的是被坏人恢复。
另一个挑战是,如果你将加密数据放入数据库中,那么它就不再可搜索了。你必须提前计划要数据库或应用执行的查询和下拉选项的类型。例如,如果你加密了一个用户的家庭住址,则不能简单地对所有带有字符串“俄勒冈州”的行执行 SELECT*操作。如果你的应用中有一个步骤是下拉选择州名,则可以改为加密用户的整个地址,同时加一个带有州名的未加密元数据字段,以执行此类查询。你还可以使用应用层逻辑来解密记录并执行其余的搜索任务,但这里数据库就没什么用了。
很多人经常担心应用层加密的性能,但这并不是一个大问题。加密速度是很快的,而且现代硬件都有对应的加速单元。毕竟,我们的整个社交网络,那么多照片和视频都是用 HTTPS 传输的,但其性能影响可以忽略不计。应用层也是一回事,加密过程不会成为性能瓶颈。
可以肯定的是,针对应用层加密的攻击还是存在的。很多国家的政府已将使用加密服务或安装加密应用定为违法或法律上不可行的操作。用户使用弱密码或重复使用密码会完全破坏加密防线。用户忘记密码也是一个挑战;这种情况下该怎么办?用户是否能通过密码重置电子邮件来恢复数据?这种备份措施也是对端到端加密防线的削弱。
当然,一旦数据被解密,攻击者便可以攻击终端设备本身。2019 年的 WhatsApp 就遇到了这样的情况,让一些人怀疑端到端加密是否值得或者是否那么重要。但是,攻击者必须利用 WhatsApp 的零日漏洞,针对特定的用户才能实施攻击,这一事实足以证明端到端加密是有意义的。
如何成功
在应用中实现加密措施时,你需要考虑自己有哪些特定的安全目标,必须遵循哪些规范,以及需要谁来提供关键材料。你的加密措施是和你的应用紧密绑定的。受过训练的加密专家可以帮助你了解自己所用方法的优缺点,但你不能靠媒体上的文章来判断具体的对错。不过你可以选择一些方法来更接近“坚固的加密防线”,并且这些方法用起来一般是很安全的。
首先简要介绍三种主要的加密系统:对称,非对称和哈希。对称(共享密钥)加密快速而高效,这些算法通常是数据加密的基准。一般来说你需要的就是 AES。对称加密会面临密钥管理方面的挑战。你需要一种让双方获取共享密钥的方法,所以这里你就需要非对称加密了。各种对称多块模式的机密性和完整性属性有很大区别,某些模式在特定的数据类型或特定的系统约束(例如缺少随机数生成器)下有更好的效果:诸如 ECB、GCM、CBC 和 SIV 等。
非对称(公用/专用密钥)加密比对称加密更慢且更复杂,这些算法通常用于交换对称密钥。RSA 是这里的“经典”选择。ECC 相比之下更加现代化和高效,并且得到了广泛的支持。粗略地说,公钥用于加密数据和验证签名,私钥用于解密数据和生成签名。
哈希、加密签名和消息身份验证代码(MAC)提供了完整性。哈希会生成一个短字符串,该字符串可证明数据是未更改的,或者在使用消息身份验证码的情况下可证明持有密钥的一方“签名”了数据。许多人认为加密意味着完整性,但事实并非如此。例如,AES 默认情况下并不提供完整性。这里 SHA2、Poly1305 和 GCM 之类的算法会有所帮助。
管理密钥本身是一个非常大的主题,但其中也有一些重要事项:
生成:密钥的随机性、密钥的大小、对称与非对称等。
存储:从用户生成的密码派生密钥,还是存储密码以供今后查找。如果要存储密码,你是否拥有像 keychain 或硬件安全模块(HSM)那样的安全存储区域?现在,许多操作系统和平台都支持安全密钥管理。
通信:如何在客户端和服务器或两个用户之间同步密钥。对于对称密钥来说这非常困难,但对于公共密钥来说这里也存在挑战。公共密钥不一定要保密,但是你必须能确保它们确实来自你期望的来源。为此,你需要提前准备一些值得信赖的东西,这是一个鸡与蛋的问题。
除了密钥材料之外,还有其他一些与加密消息相关联的随机性或唯一性元素。初始化矢量、盐和随机数都属于这一类别。它们也需要传递给解密方,因此都需要存储或传输。一般来说,将未加密的密文与密文一起传输是安全的,但请注意不要让攻击者修改内容。
你还需要填充(pad)、编码、序列化和签名消息。信不信,就连填充不当也可能破坏加密消息的机密性。要对 JSON 对象或 HTTP 标头之类的结构化数据进行签名,你需要让双方采用相同的方式对数据进行序列化和反序列化,否则签名将不匹配。
如果你已完成了所有这些操作,那么你将获得一条加密并签名的消息。你可能会将这条消息发送给另一方,后者将检查签名并解密消息。这意味着你需要与对方沟通所有选项:密钥 ID、大小、密码、模式、IV、哈希算法等等。这种交流本身在许多密码系统中都是一个薄弱环节。例如,攻击者可以欺骗某些对称系统,使其表现得像非对称系统一样,并将其共享密钥直接发送给攻击者——这下完了。
我们这里有一些建议,特别是当你需要或希望遵守 NIST/FIPS-140 加密规范(有时政府或银行业务会要求遵守这种规范)时可以参考它们:
对称加密:AES-GCM 是一种不错的操作模式,因为它提供了多块机密性(与 ECC 不同)和身份验证/完整性(与 CBC 不同)。它得到了广泛支持,因此你需要的话一般就能依赖它。但是,你必须非常谨慎对待 GCM 随机数,因为随机数重用(或者如果攻击者可以选择随机数)可能会泄漏关键材料,这就有麻烦了。
身份验证:这一措施能证明是拥有私钥的一方加密了数据。这是很重要的。我们的建议与上一条相同。
密钥交换:P-384 曲线上的 Diffie-Hellman 椭圆曲线(ECDH)是一个不错的选择。
哈希:SHA256 是当下的标准选项。
不要使用旧的或有缺陷的方法:虽然这里没有详尽的名单,但最常见的“旧的或有缺陷的”方法包括:DES、MD4、MD5、SHA1、RC4、AES-ECB(RSA 是旧的,但只有它可用的时候也能用,只是最好使用 ECC。)
libSodium:如果你不需要遵从 NIST/FIPS 规范,则绝对应该考虑 libSodium。它的声誉很好,并且它的库通常比实现类 FIPS 密码的库更容易使用。
结论
加密是保护数据的一种非常有效的方法,但是当今部署的大多数加密措施都是 IT 基础架构的一部分,而非应用的一部分。作为开发人员,我们可以将应用层加密纳入工具箱来提高用户的隐私保护水平和安全性,这是我们独有的机会。前路肯定存在挑战;加密的数据可能更难管理,并且大多数加密库对于未经培训的开发人员来说都很难用,但是比起我们用户得到的收益,这些付出都是值得的!
专业术语
下文不是这些术语的正式定义,只是简单的注释,帮助你了解这些术语和技术怎样用在应用层加密上。
高级加密标准(AES):最常见的对称加密标准之一。
非对称加密:也称为公钥加密。与对称加密相比,在密钥管理方面更慢,但更灵活。其算法包括 ECC 和 RSA。通常用于协商对称加密密钥。
分组密码模式(BCM):由于诸如 AES 之类的对称密码只能处理固定数量的位(例如 128),因此必须使用安全的方法来处理多个块。此类模式使用不当是一个常见漏洞。常用的块模式包括 GCM、ECB(尽管它是不安全的)、CBC、SIV 等。
域名系统(DNS):用于(一般而言)根据服务器名称标识服务器的协议。DNS 是安全基础架构的重要组成部分,因为对 DNS 的控制可以使攻击者将服务器模拟为最终用户。
FIPS140/NIST 密码标准:经美国联邦政府审查的面向多种用途的密码集合。一些行业要求使用经过审查的密码和实现。美国国家标准技术研究院(NIST)对此进行了标准化。
硬件安全模型(HSM):用于安全管理加密密钥和操作的工具包。硬件层提供了额外的保护,例如反特权攻击。
初始化向量、盐和随机数:用作加密算法中组成部分的随机数。根据算法和模式的不同,每种算法都可以具有不同的安全性和用途。
完整性:一种安全性属性,它意味着某条数据无法被更改,或者一旦被更改就可以被检测到,或证明消息是由授权方生成的。MAC 和 SHA2、GCM 和 Poly1305 等算法可以帮助提供此属性。
对称加密:类似 AES 的算法,双方使用相同的密钥进行加密和解密。比非对称加密更快,但是需要在安全通道上交换密钥,因此更难管理密钥。
传输层安全性(TLS):“传输中”通信加密的广泛标准。有时称为 HTTPS,但也适用于其他协议,例如 VPN。
虚拟专用网(VPN):基于网络的通信的加密。相比 HTTPS,它还可以保护更多层面的用户行为,并且经常使用和 HTTPS 类似的技术。
作者介绍:
Isaac Potoczny-Jones 是 Tozny LLC(一家致力于身份管理和加密的隐私和安全公司)的创始人兼首席执行官。Isaac 在网络安全方面的工作涉及开源、公共部门和商业公司。他的项目包括针对人类研究项目的端到端隐私加密、安全的跨域协作、身份管理、匿名授权、无移动密码的身份验证、硬件设备的防伪和保护隐私的身份验证等。他曾与 DARPA、海军、空军研究实验室、国土安全部、美国国家标准与技术研究院以及国防部和情报界的其他部门等机构合作。Isaac 是加密和编程语言领域的活跃开源开发人员。学历:计算机科学学士学位;网络安全硕士学位。
原文链接:
How to Use Encryption for Defense in Depth in Native and Browser Apps
评论