Mr. Panda
Tech For Fun

[架构] Serverless 构架基础 (3)

2014 年国外 Serverless 生态迅速发展,诞生了如 Serverless Framework、Vercel 等很多优秀的产品;2017 年阿里云和腾讯云发布了国内的 Serverless 产品:函数计算和云函数。Serverless 的开发框架、WEBIDE也陆续出现。

安全生产: Serverless 安全的主要风险是什么?

Serverless 安全面临的挑战

从软件开发的角度来看,提供 Serverless 服务的云厂商提供了以下的便利:

  • 专注产品功能的实现
  • 无须为服务和操作系统安装安全补丁
  • 完全不用考虑底层服务器操作系统以及软件运行环境
云上安全责任分摊模型

Serverless 安全面临的挑战:

  • 攻击面越来越广:由于 Serverless 中函数的数据来自多种数据源,这就极大增加了攻击面,特别是当数据源消息结构非常复杂时,传统的 Web 防火墙方式就很难对数据进行校验。
  • 攻击方式越来越复杂:除了传统的 DDoS 攻击、数据注入等攻击方式,Serverless 还面临着事件注入、流程劫持等新的攻击方式。
  • 可观测性不足:因为 Serverless 对开发者来说是屏蔽了底层基础设施,且应用是由分布式的函数组成,所以 Serverless 应用的可观测性比传统应用更复杂。
  • 传统安全测试方法不适用:当 Serverless 应用依赖了第三方服务或云服务,虽然在单元测试时这些依赖可以被模拟,但进行安全测试却不能模拟。
传统的安全测试工具对 Serverless 应用也适用
  • DAST:动态应用程序安全性测试。很多 Serverless 的数据源不是 http 触发器,DAST 工具无法对非 HTTP 的 Serverless 应用进行扫描。
  • SAST:静态应用程序的安全性测试。由于 Serverless 使用了很多触发器和云服务,很多静态分析工具并没有考虑到这些情况,很容易出现误报。
  • IAST:交互式应用程序安全测试。这种工具对于非 HTTP 的接口和依赖了云服务的应用并不适用,并且很难去进行流量代理。
  • 传统安全防护方案不兼容
传统的安全防护方案
传统安全防护方案与 Serverless 架构不兼容

Serverless 安全的主要风险

Serverless 安全的主要风险来源:

函数事件注入

数据注入是最常见的安全风险,不过在传统应用中,这些数据注入都是用户输入的数据,而 Serverless 应用的数据并不局限于用户输入。

丰富的数据源不仅增加了潜在的攻击面,也增加了安全防护的复杂性,难 以判断哪些数据是安全数据,哪些数据是危险数据。

身份认证无效

Serverless 架构的应用是由几十甚至上百个函数组成,每个函数实现特定的业务功能,这些函数组合完成整体业务逻辑。一些函数可能会公开其 Web API,需要进行身份认证;另一些则可能只允许内部调用,所以不用进行身份认证。

云存储属于内部调用,却没有进行身份认证

应用配置不安全

在云上运行的应用,尤其是 Serverless 应用,经常会使用到很多应用配置,另外你还会基于配置来实现环境区分、功能开关等逻辑,对于配置中心或存放应用配置的云存储授权不当,这很可能造成应用敏感信息泄露,或没有权限的用户不小心修改配置导致应用无法运行。

用户或角色权限过高

Serverless 应用应该秉持最小权限的原则,也就是仅给函数提供其执行时所必需的权限。很多开发者或团队为了方便,就为函数设置了统一的较大的权限,如果一个应用中函数权限过高,那单个函数的漏洞就可能造成系统级灾难。

函数日志和监控能力不强

虽然云厂商都对函数提供了日志和监控功能,但这些工具都很新,提供的能力也有限,要想利用这些工具来可视化 Serverless 架构的运行情况还非常困难。

函数日志和监控能力的不足,就会导致面临攻击时,你就很难针对 Serverless 攻击进行报警,也很难通过日志去分析、排查并解问题。

第三方依赖不安全

很多时候为了完成业务逻辑,函数就要依赖第三方软件包、开源库,甚至通过 AP 调用第三方远程 Web 服务,如果函数的第三方依赖不安全,也很可能导致函数不安全。

敏感信息泄露

随着应用规模和复杂性的增长,应用需要维护越来越多的敏感信息,比如访问凭证AccesskeyldAccesskeySecret 和 Secrity Token)、数据库密码加密密钥等,常见的错误做法是:把这些敏感信息简单地放在项目配置文件中,随代码一起上传。

DDoS 和资损

DDoS 几乎成了每个暴露在互联网上的服务面临的主要风险之,比如大量恶意运行函数造成资损

函数执行流程操纵

通过操纵函数执行流程,攻击者可以破坏应用逻辑,并且还可以利用该方式绕过访问控制、提升用户权限、甚至发起 DDoS 攻击。Serverless 应用是由很多离散的函数组成,这些函数按特定顺序编排到一起,形成整体应用。这应用中有多个函数且函数间相互依赖的情况下,抵用顺序就尤为重要,如果函数起来其他云服务,那么状态存储和共享的过程也有可能成为攻击的目标。

比如,上图的应用逻辑是用户通过身份认证和 API 网关上传文件至云存储,然后云存储触发器触发函数 1 校验文件完整性,如果校验通过则发送成功消息到消息队列,经由消息触发器触发函数 2 对用户上传至云存储的文件进行加密。如果云存储没有正确的访问控制,任何用户都可以绕过文件完整性校验而直接上传文件,导致影虎恶意上传大量垃圾文件。如果消息队列没有设置合适的访问控制,任何用户都可以伪造大量恶意消息导致函数恶意执行。

错误处理不当

由于 Serverless 的调试方式还比较有限,所以很多开发者喜欢直接在 FaaS 平台上打印函数运行时的日志,以及冗长的错误信息,如果一些敏感信息没有被清除,可能导致敏感信息泄露在日志中,或日志中记录了详细的错误堆栈,暴露代码的漏洞

不管是传统应用还是 Serverless 架构的应用,都存在安全风险,因此你要先深入了解应用架构中的风险点,这样才能对症下药,解决问题。

如何提升 Serverless 的安全性

减少代码漏洞

  • 不要相信任何用户输入:由于 Serverless 应用的攻击面更多,攻击手段更复杂,所以你永远不要相信任何输入,也不能对输入进行有效性假设。这就要求你验证用户输入或者触发器数据或者执行 sql 时,对参数进行预处理。
  • 使用安全的第三方依赖:维护项目的依赖及版本扫描依赖项找出并去掉存在漏洞的版本、删除不必要的依赖仅从可信赖的资源中使用第三方依赖将不推荐使用的依赖更新到最新版本。可以从一些公开的安全漏洞披露平台中,浏览某个依赖有没有安全漏洞,Node. Js 模块中的已知漏洞、Java 中的已知漏洞、Python 相关技术中的已知漏洞。
  • 正确处理程序异常:对于生产环境中的代码,需要避免打印冗长的错误信息,建议你只为用户提供简单的错误消息,不要显示有关任何内部实现的堆栈或环境变量的详细信息
  • 通过 API 网关确保 API 安全:使用 Serverless 最常见的场景之一就是构建 API。
API 网关确保 API 安全

正确使用访问控制:

访问控制是使用云产品时非常重要的功能,尤其是在规模比较大的团队中,访问控制可以用来限制不同用户能够操作的资源。

  • 为用户和角色配置最小的权限:为了减少访问凭证泄漏带来的风险,建议你使用访问控制来管理每个函数具有的权限,并确保每个函数都有且仅有运行时所需要的最小权限。也可以使用一些开源的工具来保证函数的最小权限。
  • 使用临时访问凭证:当使用代码对云产品进行编程访问时,就会用到访问凭证。在 Serverless 函数中,建议你尽可能使用临时访问凭证,而不是直接在代码中配置固定的访问凭证。
使用临时访问凭证

增强数据安全防护

  • 对云上的数据进行加密:云上的数据主要有两部分:云上存储的数据;云上传输的数据。为了避免中间人攻击,导致传输过程中的数据泄漏,建议你使用 TLS 协议对函数通信、网络请求等传输数据进行加密。
  • 对应用配置进行加密:几乎每个应用都会有很多配置,这些信息会在不同的应用、代码、环境变量中进行传输,只要其中有一个环节有漏洞,就会导致数据泄漏。一定要避免在代码中定义明文配置,要对这些配置进行加密。
  • 如何对配置进行加密:AWS Secrets Manager、Azure 密钥保管库、阿里云密钥管理服务、Google Cloud KMS。
  • 使用 Serverless 相关的身份认证服务:为了避免没有权限的用户访问数据,你也需要对访问请求进行身份认证。不建议配置自己的身份认证方案,建议使用 Serverless 相关的身份认证功能。
  • Serverless 相关运行身份验证功能:AWS API Gateway、AWS Cognito或单点登录、阿里云 API 网关、Google Firebase 身份认证、腾讯云 API 网关、Azure 应用服务身份认证和授权。

提升应用可观测性

由于 Serverless 应用可观测性不足,遇到安全攻击可能难以第一时间发现,发现安全问题也难以快速定位并恢复,所以导致 Serverless 面临着比传统应用更大的安全风险。

记录函数日志并设置报警

Serverless 屏蔽了底层机器,需要将日志输出到统一的日志存储服务,这样才能更方便查看问题。强烈建议针对云账号的账单设置报警,尽可能避免预期外的费用。

使用配置审计监控资源的配置变化

由于 Serverless 函数是在云上运行的,函数本身就有很多配置,函数依赖的云服务也有很多配置,一旦某个配置发生变化,很可能对应用运行造成影响,建议使用云厂商提供的配置审计功能来检测云资源的配置变化。

基于配置审计,持续监控的规则:

  • 检测函数是否通过控制台创建:对于复杂的 Serverless 应用,通常具有完善的 CI/CD 流程,而不是简单的从控制台创建函数。
  • 检测使用同一个角色的多个函数:通常会保持一个函数一个角色的原则,才能更小的实现函数权限最小化。
  • 检测配置了多个不同触发器的函数:通常会保持一个函数一个触发器的原则,这样才能保证函数单一职责。如果函数有多个触发器就会增加函数的攻击点。
  • 检测使用通配符(*)权限的函数:通配符意味着更多的权限,这违背了函数最小权限的原则。
使用操作审计记录云上所有的操作事件

如果某个用户误删或恶意删除了某个资源,你要及时收到报警并处理,尽可能避免影响变大,这时你就可以使用云厂商的操作审计服务了。

总结

  • 在云上运行的应用,云厂商负责计算、网络、存储等底层资源的安全性,应用所有者负责应用本身的安全性。
  • Serverless 安全性面临的主要挑战是:越来越多的攻击面、越来越复杂的攻击方式,可观测性不足以及传统安全测试方法和防护方案不适用于 Serverless 架构。
  • 对于 Serverless 架构的安全风险需要深入理解,才能更好地规避。
Serverless 安全生产思维导图

成本优化:Serverless 真的省钱吗?

对技术 Leader 来说,在进行技术选型时,除了考虑技术架构、稳定性等,更需要考虑成本,下面将从分析、优化、控制等方面讨论 Serverless 应用的成本。

Serverless 应用的成本分析

大多数 FaaS 都是按照函数执行次数和函数执行消耗的内存来收费的。

  • 执行次数:1.3元/百万次
  • 执行时间(按量付费) :0.0110592元/1GB-秒

认为 Serverless 成本比传统 Serverful 架构更高的两个误区:

  • Serverless 的收费包含了应用的系统管理功能:Serverless 函数则可以完全利用分配的 CPU 和内存。
  • Serverless 的函数并不是持续运行的:函数每次只会运行几十亳秒到几百毫秒,不运行时不收费。所以实际产生的费用可能要便宜得多。
使用 Serverless 服务能极大的降低成本

Serverless 这种用多少花多少的收费模式带来的缺点就是很难预具体会产生多少费用,云厂商通常也提供了预付费模式来解决这个问题。

Serverless 应用的成本优化

Serverless 应用是按照代码执行次数、执行时间进行收费:性能=时间=成本。性能越好,函数运行时间越少,成本也就越低。

为函数设置超时时间

  • 避免函数因为异常而无限制地运行下去,导致成本的上升。
  • 一般为提供 API 服务的函数设置 10s 左右的超时时间。
  • 可以降低函数 DDoS 攻击的风险,避免恶意攻击造成函数持续运行带来资损。

为函数分配合适的内存

  • 函数收费是按照 GB 每秒来收费的,所以内存越高,费用越高,但通常内存越高,函数运行速度又更快。
  • 需要在成本和性能之间找到平衡点。

减少函数的冷启动耗时

函数的冷启动会直接增加函数的执行时间,并且这部分时间是你的业务逻辑之外的耗时,所以少函数的冷启动时间,可以帮你节省一大笔费用。

减少外部慢 API 调用

  • 通常调用外部 API 会涉及网络请求,如果外部 API 比较慢,函数执行过程就会阻塞,导致函数执行耗时增加,进而增加成本。
  • 如果是必要的外部 API 调用,可以尽量减少 API 调用时间,如依赖第三方身份认证 Oauth 服务,可以就近选择 Oauth 服务。

为函数实例设置并发

不仅能提升函数性能,还能节省函数成本,这样计费的时间就是从第一个请求开始到最后一个请求结束。

单实例单并发和单实例多并发的比较

选择合适的计费方式

  • 目前绝大部分 FaaS 平台都支持按量付费和预付费,可以根据应用特点选择合适的付费方式。
  • 如果应用流量一直很高且比较平稳、对延迟也比较敏感,可以选择预留模式,这将极大减少冷启动。
  • 日常测试或离线处理数据时,函数可能是临时大量执行,可以选择按量付费,可以保持较高的资源利用率。
预留资源计费

Serverless 应用的成本控制

在 Serverless 架构中,我们可以通过提升应用性能来降低成本。但是基于 Serverless 架构,账单变得动态起来,费用难以预测。为什么 Serverless 账单难以预测呢?

  • 应用是弹性的,很难预测函数到底要执行多少次。
  • 应用通常不仅仅包含 FaaS 的费用,还有其他组合使用的云产品的费用,而这些费用可能比 FaaS 的成本还高。

在前面的成本分析中,之所以没有把 FaaS 之外的成本包含在内,是因为基于 Serverful 的架构,也可能需要使用到这些产品。企业可以自行开发这些产品和服务。

Serverelss 应用的成本:FaaS + 事件源的成本和相关服务成本。

  • 成本预测: 成本预测主要就是根据以往的数据预测未来的成本。
  • 成本监控:就像我们使用监控来观测程序是否正常运行一样,Serverless 应用的成本监控也非常重要,可以今早的发现成本的异常情况。可以基于 Serverless 开发一个简单的成本监控程序。也可以使用一些第三方的成本分析和成本监控平台。

总结

  • 基于 Serverless 架构,不用再为闲置的服务器付费,只为实际使用的资源付费就可以了。同时还可以通过提高 Serverelss 应用的性能,进一步优化成本。
  • Serverless 应用的成本包括 FaaS 中函数执行的成本以及函数所依赖的触发器、数据源和 BaaS 服务的成本。
  • Serverless 中函数按照执行次数和执行时间进行收费,因此能大幅降低成本。
  • 可以通过提升 Serverless 函数的性能来优化成本。

系统迁移:传统应用如何迁移到 Serverless?

Serverless 是一项新的技术,和传统开发方式区別很大,迁移成本也很大。传统应用要想迁移到 Serverless 架构上,就要进行相关的改造。

传统应用应该如何迁移到 Serverless?

缓存

应用进程是持续运行在服务器上的

同一个服务器上的内存可以共享,所以传统应用通常可以在内存中缓存数据,以便提升计算性能;基于 Serverless 架构的应用,内存缓存通常没有意义,函数的生命周期有限,传统应用迁移到 Serverless 架构面临的第一个改造点就是内存缓存问题

在 Serverless 架构的应用中一般用缓存数据库做缓存。在传统应用中(尤其分布式应用)大部分时我们也会使用缓存数据库,因为服务器和服务器之间,也无法共享内存。所以内存缓存也仅作用于当前服务器处理的所有请求。请求读取数据的顺序一般为:内存缓存→缓存数据库(如 Redis)→远程服务。

身份认证

身份认证是传统应用迁移到 Serverless 的第二个改造点。基于 cookie-session 的认证方式通常是把身份信息保存在服务端的 session 中,现在大部分 cookie-session 的身份认证,会将 session 存储在缓存数据库,这样就降低了迁移成本。

磁盘读写

除了对内存读写,一些传统应用可能还会对磁盘有很多读写操作,部暑传统应用的磁盘是直接挂载到服务器上的,所以就算应用重启了,服务器和磁盘也依旧存在。

Serverless 函数运行在 FaaS 平台上,函数运行时只会有一个临时目录的读写权限一旦运行环境被释放,该临时目录也会被释放,所以磁盘数据无法持续存储

那么对于有读写磁盘需求的应用应该如何迁移到 Serverless 架构呢?

  • 可以为 Serverless 函数挂载一个持久存储,只要不释放数据可以永久保存。
  • 不同函数可以共用同一个持久化存储,这样不同函数就可以读写同一份数据。
  • 计算和存储分离,这样更利于应用扩缩容
传统应用读写磁盘和 Serverless 应用持久化存储

统一接入层

传统应用和 Serverless 应用的统一接入

使用 API 网关做统一接入是架构上的改造,应用代码也需要改造,将传统 Web 服务 Serverless 化,是传统应用迁移到 Serverless 架构的又一个改造点。

Web 服务如何 Serverless 化

将 Web 服务 Serverless 化的核心工作就是开发一个适配层,通过适配层将函数的事件对象转化为标准的 Web 请求。

  • 创建一个自定义 HTTP Server。
  • 将事件对象转换为 HTTP 请求参数,并转发到传统 Web 服务。
  • 将 HTTP 响应转换为函数返回值。
适配层将函数时间对象转成标准的 Web 请求

传统 Web 框架 Serverless 化比较麻烦的地方就在于,你需要完全理解 Web 框架和 Serverless 函数事件的每个参数,有些云厂商也提供了相关的工具来帮助完成适配,如阿里云函数计算提供的 @webserverless/fc-express 和腾讯云云函数提供的 tencent-serverless-http。

前文已经学习了如何实现一个自定义运行时,其中自定义运行时的原理本质上也是在函数中实现一个 HTTP 服务,传统 Web 服务 Serverless 化的原理与自定义运行时的原理是非常类似的。

Serverless 应用的成本优化

两种方案:在函数中直接创建 HTTP 服务;通过 Docker 镜像启动一个 HTTP 服务。

  • 通过适配层,由适配层将事件对象转换为标准 Web 请求。
  • 通过自定义运行时,在函数中创建一个 HTTP 服务,该 HTTP 服务将函数事件处理后转发给传统 Web 服务。
  • 通过自定义运行时,将传统 Web 服务构建为自定义镜像。

总结

  • 传统应用迁移到 Serverless 想要考虑内存缓存身份认证持久化存储Web 服务 Serverless 化等改造点。
  • 如果一个应用本身就是分布式部的,且在架构上是计算和存储分离的,则比较容易迁移到 Serverless。
  • Web 服务 Serverless 化主要原理是实现一个自定义 HTTP服务,通过该 HTTP 服务处理事件对象和 Web 请求的差异。
  • 通过实现适配层自定义运行时等方案,实现 Web 服务 Serverless 化。

身份认证:使用 Serverless 实现登录注册功能

要实现应用中的身份认证,首先要详细了解身份认证的技术方案,以及该方案怎么在 Serverless 架构中使用。

身份认证的技术方案

Cookie-session

早期互联网主要以 Web 为主,客户端是浏览器所以 Cookie-Session 方式是早期最常用的身份认证方式。

Cookie-Session 身份认证流程

存在两个主要问题:

  • 服务端的 SessionID 是直接存储在内存中的,在分布式系统中无法共享登录状态。
  • cookie 是浏览器的功能,手机 App 等客户端并不支持 cookie,所以该方案不适用于非浏览器的应用。

解决方案:

  • 用一个共享存储来保存 Session 信息,最常见的就是 Redis,因为 Redis 是一个内存数据库,读写速度很快。
基于共享存储的 Cookie- Session 身份认证流程
  • JWT,是(JSON Web Token)的简称,Token 是个比较长字符串,格式为 Header.Payload.Signature ,token 的加密是不可逆的,且可以设置过期时间,因此不会泄露用户的个人信息。JWT 这种身份认证方案,适用于各端,也非常适合 Serverless 应用。
JWT 身份认证流程

总结

  • Serverless 应用的身份认证本质上是要将有状态的认证方案改为无状态的。
  • 使用共享存储来保存登录状态。
  • 使用无状态的身份认证方案,比如 JWT。
  • 使用三方的身份认证服务。
  • Cookie-Session 的身份认证方式,是在服务端存储 Session 信息,客户端(浏览器)通过 cookie 存储 SessionID。
  • JWT 的身份认证方式,是在服务端根据用户信息生成 token,客户端保存 token。
  • Cookie-Session 的认证方案通常是有状态的,对于分布式、无状态的应用,需要将 Session 保存在共享存储中。
  • JWT 的认证方式通常是无状态的,所以比较适合 Serverless 应用。

参考链接

  1. nodejh/serverless-class

Jonsam

一个理科IT宅男,喜欢旅游、分享和美食,做点想做的事情,遇见想见的人。

🍒 美食 | 🌐 FE | 🕌 旅行 | 💻 加班 | ♍ 处女座

jonsam ng

jonsam ng

文章作者

海阔凭鱼跃,天高任鸟飞。

[架构] Serverless 构架基础 (3)
2014 年国外 Serverless 生态迅速发展,诞生了如 Serverless Framework、Vercel 等很多优秀的产品;2017 年阿里云和腾讯云发布了国内的 Serverless 产品:函数计算和云函数。Serverless …
扫描二维码继续阅读
2021-12-27