【云原生应用安全】云原生应用安全防护思考(一)

Posted by     "Pu Ming" on Saturday, September 18, 2021

本文转自绿盟科技研究通讯公众号 《【云原生应用安全】云原生应用安全防护思考(一)》

专题信息

专题:【云原生应用安全】

往期回顾:

【云原生应用安全】微服务架构下API业务安全分析概述

【云原生应用安全】云原生应用安全风险思考

一. 概述

应用是云原生体系中最贴近用户和业务价值的部分,笔者在之前《云原生应用安全风险思考》一文中分析了云原生应用面临的风险,相信各位读者已经有所了解,本文为云原生应用安全防护系列的第一篇,主要针对传统应用安全、API安全、云原生应用业务安全这三方面风险提出笔者的一些防护见解及思考。另外,文章篇幅较长,且内容上与前述风险篇相互对应,若结合在一起阅读,思路会更清晰些,希望本文可为各位读者带来更多思考。

二. 传统应用安全防护

《云原生应用安全风险思考》一文中对传统应用风险的介绍,我们得知传统应用为云原生应用奠定了基石,因而笔者认为云原生应用安全防护也可参照传统应用安全防护,接下来笔者将为各位读者介绍传统应用的安全防护方法,笔者认为其主要包含以下四方面。

应用程序代码漏洞缓解

《云原生应用安全风险思考》一文中对传统应用安全的分析,应用程序的已知漏洞几乎是造成所有风险的主要原因,因而针对应用程序的漏洞缓解措施是必要的。

应用程序依赖库漏洞防护

应用程序的漏洞缓解措施只能一定程度上规避开发者不规范编码造成的风险,而应用程序本身除了开发者编写的代码,还可能需要引入第三方依赖库,那么依赖库是否含有已知漏洞将会直接决定该应用程序是否相对安全,因而针对应用程序依赖库漏洞的防护也是必要的。

应用程序访问控制

笔者在之前的风险篇中多次提到“访问权限的错误配置”,“脆弱的函数运行时”等会导致应用存在未授权访问风险,做好应用程序的访问控制非常重要。

应用程序数据安全防护

我们知道,应用程序最终为业务服务,而数据为业务产生了价值,从《云原生应用安全风险思考》一文的分析中我们得知数据泄露风险是目前应用程序面临的巨大风险之一,如何防止数据泄露是我们需要关心的一大问题。

2.1 应用程序代码漏洞缓解

应用程序代码漏洞缓解应当从两方面考虑,一方面是安全编码,另一方面是使用代码审计工具。

2.1.1 安全编码

针对安全编码,开发者需要具备安全编码的能力。例如面对SQL注入漏洞,开发者需要将数据和命令语句及查询语句分离,那么最佳的选择便是使用相对安全的API, 而避免使用解释器,提供参数化界面的接口及迁移至ORM或实体框架。此外,对参数输入的有效过滤,例如白名单机制,也有助于防御注入攻击。再如针对XSS类型的漏洞,主要的防护原则为将不可信的输入源与动态的浏览器内容分离,具体实现的手段也非常多,例如使用从设计上就会将危险输入进行编码或转义以防止XSS攻击的Web框架,例如Ruby on Rails或ReactJS等。由于漏洞类型较多,本文由于篇幅限制,不再赘述,更多的针对代码漏洞的防护方法可以参考OWASP组织在2017年发布的应用十大风险报告[1]。

2.1.2 使用代码审计工具

应用程序代码在未部署至服务器前是静态的,我们可以通过手动编写规则脚本去进行漏洞筛查,但往往效率较低,可行的方法是使用自动化代码审计工具,业界比较主流的有AppScan、Fortify、Burp等。需要注意的是这些工具也不是万能的,可能会产生误报或漏报的现象。

2.2 应用程序依赖库漏洞防护

2.2.1 使用受信任的源

使用受信任的源是最直接的方法,应用开发者可以仅从官方渠道获取第三方组件,同时也可以关注已含有CVE、NVD漏洞的第三方组件,避免试错过程,这些含有漏洞可在官方网站上进行查询,例如Node.js库CVE漏洞列表[2]、Java库CVE漏洞列表[3]、Python库CVE漏洞列表[4]。

2.2.2 使用软件组成分析工具

如果应用程序较为复杂涉及的组件较多,仅通过手动移除含有漏洞的第三方组件往往效率较低,且容易造成漏洞遗漏,鉴于此,业界通常采取软件组成分析(Software Component Analysis SCA)技术,其原理是通过对现有应用程序中使用的开源依赖项进行统计,并同时分析依赖项间的关系最后得出依赖项的开源许可证及其详细信息,详细信息具体包括依赖项是否存在安全漏洞、漏洞数量、漏洞严重程度等。最终SCA工具会根据这些前提条件判定应用程序是否可以继续运行。目前主流的SCA产品有OWASPDependency Check[5]、SonaType[6]、Snyk[7]、Bundler Audit[8],其中SonaType、Snyk、Bunder Audit均为开源项目。

2.3 应用程序访问控制

实现访问控制,但随着业务量逐渐复杂,用户数量不断增多,准确识别每个用户需要哪些权限、不需要哪些权限是一件具有挑战性的工作,且为每个用户赋予单一权限的方法易造成权限泛滥的风险,因而我们应遵循最小特权原则,即给予每个用户必不可少的特权,从而可以保证所有的用户都能在所赋予的特权之下完成应有的操作,同时也可以限制每个用户所能进行的操作。

使用基于角色的访问控制是实现最小特权原则的经典解决方案,基于角色的访问控制就是将主体(用户、应用)划分为不同的角色,然后为每个角色赋予权限,例如上述提到的业务量大,用户数多的应用程序中,使用基于角色的访问控制就显得很有效,因为我们可以定义每类角色所具备的访问权限,这样即便有成千上万个用户,我们只需按照用户的类型去划分角色,从而可能只需要有限个数的用户角色即可完成访问控制。

2.4 应用程序数据安全防护

笔者认为应用程序的数据安全防护应当覆盖安全编码、密钥管理、安全协议三方面。安全编码涉及敏感信息编码,密钥管理涉及密钥的存储与更换,安全协议涉及函数间数据的安全传输。

2.4.1 安全编码

在应用的开发过程中,开发者常常为方便调试将一些敏感信息写在日志中,随着业务需求地不断增多,开发者容易忘记将调式信息进行删除,从而引发了敏感信息泄露的风险。更为严重的是这种现象在生产环境中也频频出现,例如python的oauthlib依赖库曾被通用缺陷列表(Common Weakness Enumeration CWE)指出含有脆弱性风险[9],原因是其日志文件中写入了敏感信息,以下为此依赖库对应含有风险的代码:

if not request.grant_type== 'password':
    raise errors.UnsupportedGrantTypeError(request=request)
    log.debug('Validating username %s and password %s.', request.username,request.password)

if not self.request_validator.validate_user(request.username,request.password,request.client, request):
    raise errors.InvalidGrantError('Invalid credentials given.',request=request)

以上可以看出开发者将用户名密码记录在了Debug日志中,这是非常危险的写法,因为攻击者可能会利用此缺陷获取用户的登录方式,并进行未授权访问,甚至窃取用户隐私数据,因而针对应用程序的数据安全,安全编码十分重要。

安全编码具体需要怎么做是读者们关心的问题,笔者认为,最重要的是禁止将敏感信息(如:用户名密码、数据库连接方式)存储至源码、日志及易被攻击者发现的地方,同时我们应对存储的所有敏感数据进行加密。

此外,一些开源项目可以帮助开发者避免敏感信息被硬编码至源码中,例如AWS的开源项目git-secrets[10]和Yelp的开源项目detect-secrets[11],各位读者可以参考。

2.4.2 使用密钥管理系统

为了应用程序环境的安全,我们应当使用密钥管理机制,该机制主要用于对密钥进行创建、分配、更换、删除等操作,目前许多企业采用密钥管理系统(Key Management System)的方式,例如国外主要以AWSKMS[12]、Azure Key Vault[13]、Google CKM(Cloud Key Managemet)[14]等为主,国内则以阿里云密钥管理服务[15]、腾讯云密钥管理服务[16]等为主。

2.4.3 使用安全协议

为避免敏感数据在传输过程中泄露,应确保传输中的数据是加密的,例如Web应用中,我们可以通过使用HTTPS协议替代HTTP协议,确保用户传输的数据不被窃取和篡改,从而在一定程度上避免被中间人攻击的风险。

三. API安全

通过《云原生应用安全风险思考》一文中API风险分析,我们知道,虽然云原生应用架构的变化导致了API数量的不断增多,但在造成的API风险上并无太大区别,因而在相应的API防护上我们可以参考传统的API防护方法。此外,我们还可采用API脆弱性检测的方式防止更多由于不安全的配置或API漏洞造成的种种风险。最后,在云原生应用架构下,我们可使用云原生API网关,其与传统的API网关有何不同,能为云原生应用风险带来哪些新的防护是我们关心的问题。因此,本小节笔者将API安全分为传统API防护、API脆弱性检测、云原生API网关三个部分进行介绍。

3.1 传统API防护

针对传统的API风险,我们可以使用传统的API防护方式,例如针对失效的认证,我们可以采取多因素认证[17]的方式或采用账号锁定、验证码机制来防止攻击者对特定用户的暴力破解,再如针对失效的功能授权,我们应当默认拒绝所有访问,并显式授予特定角色访问某一功能,更多典型的API防护方式各位读者可以参考OWASP组织在2019年发布的API十大风险报告[18],该报告针对每种典型风险均提出了较为详细的防护方法,本文限于篇幅,不再赘述。

3.2 API脆弱性检测

API脆弱性主要针对的是服务端可能含有的代码漏洞、错误配置、供应链漏洞等,目前较为可行的方式是使用扫描器对服务端进行周期性的漏洞扫描,国内各大安全厂商均提供扫描器产品,例如绿盟科技的远程安全评估系统(RSAS)[19]和Web应用漏洞扫描系统(WVSS)[20],其中RSAS已支持针对容器镜像的扫描。同时,我们也可以使用其它商业版扫描器,例如AWVS(Acunetix Web Vulnerability Scanner)、AppScan、Burp Suite、Nessus等。

3.3 云原生API网关

云原生API网关,顾名思义指云原生应用环境下的API网关,笔者认为,云原生API网关与传统API网关的区别主要有两方面,一方面是应用架构带来的区别,另一方面是部署模式的区别。

针对应用架构带来的区别,传统API网关更关注于管理API带来的挑战,而云原生API网关由于应用微服务化后,每个服务都可能会由一个小团队独立开发运维,以快速向客户交付相应的功能,因而为了让每个团队能够独立工作,服务应当具备及时发布、更新以及可观测性的特点,鉴于此,云原生API网关更关注于业务层面,例如可通过为终端用户提供静态地址,并动态地将请求路由至相应的服务地址实现服务发布,又如可在终端用户访问服务过程中通过收集关键可观测性指标实现对服务的监控,再如可支持动态地将终端用户的请求路由至服务的不同版本以便进行金丝雀测试。

针对部署模式的区别,传统的API网关通常在虚拟机或Docker容器中进行部署,而云原生API网关则主要在微服务编排平台部署,典型的为Kubernetes。

微服务应用环境中,云原生API网关充当着非常重要的一环,它不仅要负责外部所有的流量接入,同时还要在网关入口处根据不同类型请求提供流量控制、日志收集、性能分析、速率限制、熔断、重试等细粒度控制行为。云原生API网关为云原生应用环境的防护带来了一定优势,首先,由于云原生API网关接管南北向流量,因而将外部访问与微服务进行了一定隔离,从而保障了后台微服务的安全。其次,在早期的微服务治理框架中,例如SpringCloud,由于其将服务治理逻辑嵌入了具体服务代码中,因而导致了应用的复杂性变高,而云原生网关具备一定的服务治理能力,从而可节省后端服务的开发成本,进而有益于应用层面的扩展。最后,云原生API网关也具备解决外界访问带来的一些安全问题,例如TLS加密、数据丢失防护、防止跨域访问、认证授权、访问控制等特性。

云原生API网关以开源项目居多,近些年来,随着技术的不断发展,Kubernetes显然已成为容器编排平台的业界标准,因而云原生API网关也都相应支持在Kubernetes上进行部署,目前主流的云原生API网关有Ambassador、Zuul、Gloo、Kong等。为了让各位读者一览以上提到的云原生API网关在安全功能上的支持,笔者进行了相应统计,以供各位读者参考,具体下表所示:

Ambassador Zuul Gloo Kong
Web应用防火墙 支持 支持 支持 支持
访问控制 支持 支持 支持 支持
基本认证授权 支持 支持 支持 支持
SSL证书管理 支持 支持 不支持 支持
数据丢失防护 不支持 支持 支持 不支持
CORS 支持 支持 支持 支持
JWT 支持 支持 支持 支持
限速服务 支持 支持 支持 支持

通过上述表格可以看出Zuul全项支持,但因Zuul与Spring Cloud的深度集成,故只能针对使用Java环境的微服务进行防护。其余云原生API网关均有一项不支持,主要为Ambassador针对数据丢失防护不支持,Gloo针对SSL证书管理不支持,Kong也是对数据丢失防护不支持,需要注意的是,这三个API网关相比于Zuul有较为明显的区别,Ambassador与Gloo均为Kubernetes原生网关,且从官方网站上[21][22]都能看到他们兼容微服务治理框架Istio的方案,因而如果各位读者使用Istio治理微服务,可以选择Ambassador和Gloo。Kong属于这四者中开源社区最为活跃及成熟的,从官方的解决方案中[23]可以看到,其以支持Kubernetes的部署方案,凭借Kong在API安全上的积累,相信很快可以在云原生API网关上占据一席之地,成为大多数人的选择。

四. 云原生应用业务安全

针对《云原生应用安全风险思考》一文中提到的云原生应用业务层面安全问题,基于基线的异常检测是一类比较有效的方法:首先建立正常业务行为与参数的基线,进而找出偏移基线的异常业务操作,其中,基线的建立需要结合业务系统的特性和专家知识共同来完成。

在电商系统中,业务参数基线主要基于专家知识来建立。例如商品价格不仅与商品本身相关,也与时间和各类优惠活动等相关。这类基线需要运维人员持续的维护。对于业务逻辑基线的建立,由于业务系统在正式上线运行以后,其操作逻辑一般不会有较大的变化,同时异常操作所占的比例较少。因此可以采集业务系统历史的操作数据,结合统计分析与机器学习的方法建立业务逻辑的基线。相比于人工方法,这种方法可以提高基线建立的效率,有效减轻运维人员的工作量。

为此,可利用分布式追踪工具对云原生应用中产生的数据进行采集,笔者对当前主流的分布式追踪工具Zipkin,Jaeger,Skywalking,Pinpoint进行了调研,这些分布式追踪工具大体可分为三类,基于SDK的,基于探针的,基于Sidecar的。

基于SDK的分布式追踪工具。以Jaeger为例,Jaeger提供了大量可供追踪使用的API,通过侵入微服务业务的软件系统,在系统源代码中添加追踪模块实现分布式追踪。此类工具可以最大限度地抓取业务系统中的有效数据,提供了足够的可参考指标;但其通用性较差,需要针对每个服务进行重新实现,部署成本较高,工作量较大。

基于探针的分布式追踪工具。以SkyWalking Java探针为例,在使用SkyWalkingJava探针时,需将探针文件打包到容器镜像中,并在镜像启动程序中添加-javaagent agent.jar命令实现探针的启动,以完成SkyWalking在微服务业务上的部署。SkyWalking的Java探针实现原理为字节码注入,将需要注入的类文件转换成byte数组,通过设置好的拦截器注入到正在运行的程序中。这种探针通过控制JVM中类加载器的行为,侵入运行时环境实现分布式追踪。此类工具无需修改业务系统的源代码,相对SDK有更好的通用性,但其可获取的有效数据相对SDK类工具较少。

基于Sidecar实现。Sidecar作为服务代理,为其所管理的容器实现服务发现,流量管理,负载均衡和路由等功能。在流量管理过程中,Sidecar可以抓取进出容器的网络请求与响应数据,这些数据可以记录该服务所完成的一次单个操作,可与追踪中的跨度信息对应,因此可将sidecar视为一种基于数据收集的分布式追踪工具。Sidecar无需修改业务系统代码,也不会引入额外的系统的开销。但由于sidecar所抓取的跨度不包含追踪链路上下文,要将sidecar所抓取的跨度数据串联成追踪链路是很困难的。

通过使用以上分布式追踪工具进行数据采集后,针对《云原生应用安全风险思考》一文中提出的三种业务异常场景(业务参数异常、业务逻辑异常、业务频率异常),笔者设计并实现了业务异常检测引擎,如下图所示。其中,采集模块主要用于采集业务系统的运行数据,训练模块主要针对业务系统历史数据进行训练以提取行为特征数据,检测模块主要对正在运行的业务系统进行异常检测。

image
图1 业务异常检测引擎设计图

检测引擎中每部分的具体功能为:

分布式追踪工具。相比Skywalking、Sidecar,Jaeger可获取的数据字段最多,能够检测的异常场景最丰富,然而,Jaeger需要在业务系统的源代码中进行插桩,对开发团队而言有较强的侵入性。相反,Sidecar模式没有代码和镜像的侵入性,但通过反向代理截取流量的模式也决定了它不能获得丰富的上下文,如云原生应用的API调用关系树(TraceID)是无法获得的。如何利用侵入性更低的采集工具收集到的数据来实现覆盖更多场景的异常检测仍需要很多后续工作。

数据筛选与整合模块。此模块主要功能为过滤掉数据集中的脏数据,以及提取出可以表示业务系统行为的数据。在云原生应用中,可以表示业务系统行为的数据为API调用关系树、服务名、操作名、HTTPPOST参数等。

数据训练模块。将预处理后的历史数据利用机器学习或统计学的方法,训练出业务系统中的正常行为,并生成与业务系统正常行为匹配的特征数据。这里进行训练的先验知识为,我们认为业务系统中大量存在的行为是正常行为,而数量很少的行为是异常行为。在训练过程中,需要根据专家知识对训练结果的检验不断调整训练模型的参数。

检测引擎。将业务系统当前数据与特征数据库中数据进行检索匹配,并利用序列相似性计算等方法找出特征数据库中与当前行为最为匹配的特征数据。检测引擎需要将特征数据与当前数据的相似性与基线进行比较,若比较结果显示当前行为与正常行为的差异在基线限制范围内,则为正常行为,若超出基线限制范围,则判定为异常行为。对于基线,首先需要根据专家知识设置合理的初始基线,并根据不同场景,或利用无监督模型自行调整基线,或由运维人员手动维护基线。

五. 总结

云原生应用面临的新风险主要“新”在哪里,笔者看来“新”主要体现在新应用架构的出现,我们知道,新应用架构遵循微服务化的设计模式,通过应用的微服务化,我们能够构建容错性好、易于管理的松耦合系统,与此同时,新应用架构的出现也会引入新的风险,为了较为完整地对风险进行分析,本文我们将以信息系统安全等级三要素,即机密性(Confidentiality)、完整性(Integrity)、可用性(Availability)作为导向为各位读者介绍应用架构变化带来的新风险。

本文为各位读者介绍了云原生应用在传统应用安全、API安全、云原生应用业务安全三个维度的相应防护方法,结合之前风险篇的相应介绍,首先我们可以看出传统应用防护技术适用于云原生应用,因而深刻理解传统应用防护内容非常重要。其次,云原生应用架构的变化为API带来了更多特点,也带来了新的防护方法,如云原生API网关的合理使用可以有效改善用户环境下的API安全状况。最后云原生应用业务方面的异常会给相应的业务系统带来巨大的损失。而由于API业务安全与业务场景的强耦合性,需要在系统设计之初就考虑各种业务场景下的API安全问题。一方面加强API的认证授权机制;另一方面要加入必要的数据采集功能,为后续业务异常场景的分析提供支撑。

六.参考文献

[1] https://owasp.org/www-project-top-ten/

[2] https://www.npmjs.com/advisories

[3] https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=java

[4] https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=python

[5] https://owasp.org/www-project-dependency-check/

[6] https://www.sonatype.com/

[7] https://snyk.io/

[8] https://github.com/rubysec/bundler-audit

[9] https://cwe.mitre.org/data/definitions/534.html

[10] https://github.com/awslabs/git-secrets

[11] https://github.com/Yelp/detect-secrets

[12] https://aws.amazon.com/cn/kms/

[13] https://azure.microsoft.com/en-us/services/key-vault/

[14] https://cloud.google.com/security-key-management

[15] https://www.aliyun.com/product/kms

[16] https://cloud.tencent.com/product/kms

[17] https://owasp.org/www-project-api-security/

[18] https://zh.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E8%A6%81%E7%B4%A0%E9%A9%97%E8%AD%89

[19] https://www.nsfocus.com.cn/html/2019/209_1009/66.html

[20] https://www.nsfocus.com.cn/html/2019/206_0911/8.html

[21] https://www.getambassador.io/user-guide/with-istio/

[22] https://www.solo.io/blog/istio-1-5-api-gateway-with-gloo/

[23] https://konghq.com/solutions/kubernetes-ingress/

「真诚赞赏,手留余香」

Ming Blog

真诚赞赏,手留余香