云原生安全包含两层含义:
面向云原生环境的安全的目标是防护云原生环境中基础设施、编排系统和微服务等系统的安全。
这类安全机制不一定具备云原生的特性,比如不是容器化、可编排的,而是以传统模式部署的,甚至是硬件设备,但其作用是保护日益普及的云原生环境,例如,
当然,云原生内部的安全机制以云原生形态居多,例如,
这些安全容器都是云原生的部署模式,具有云原生的特性。
具有云原生特征的安全是指具有上一小节所述的云原生特性的各类安全机制。此类安全机制具有弹性、敏捷、轻量级、可编排等特性。
云原生是一种理念上的创新,它通过容器化、资源编排和微服务重构传统的开发运营体系,加快业务上线和变更的速度。云原生系统的种种优良特性同样会给安全厂商带来很大启发,从而重构它们的安全产品、平台,改变其交付、更新模式。
这种具有云原生特征的安全机制与当前流行的安全资源池有相似的特性,当然借助业界流行的云原生技术和平台,能提供比安全资源池性能更好、处置更灵活的安全能力。
需要说明的是,对于云原生安全的两层含义,我们讨论得更多的是前者,即在云原生环境中识别各个系统和组件的脆弱性和安全风险,进而提出和设计面向云原生环境的安全,而相应的安全机制必须应用于云原生环境。当然,随着讨论的逐渐深入,读者会发现,云原生环境中的安全防护会天然地要求一些主机侧的安全机制具有云原生特性。例如容器环境的短生命周期、业务变更极其迅速,导致访问控制、入侵检测等安全机制偏向于特权容器等形态。此外,还要求可以根据编排系统的业务调度策略进行安全策略的动态调整。要满足这两个要求,最后的安全机制必然与云原生系统融合,体现出明显的云原生特性。
因而,虽然我们将云原生安全分成了两种安全机制,但这两种机制会互相融合。在理想情况下,云原生安全会是在云原生环境下,对原有的安全机制进行重构或设计新的安全功能,使得最终的安全机制能与云原生系统无缝融合,最终体现出云原生的安全能力。
未来的云安全等价于纯安全,因为未来云计算将会变得无处不在,所有的安全机制都会覆盖云计算场景。我们谈云安全,其实就是谈一个通用场景下的安全问题。
既然未来云安全等价于纯安全,而云计算的下半场是云原生,那么不妨做个推论:未来的云原生安全等价于原生安全。那么,什么是原生安全呢?笔者认为原生安全有两个特点:
原生安全会有三个发展阶段,如下图所示。
根据云原生环境的构成,面向云原生环境的安全体系可包含三个层面的安全机制。
全生命周期的云原生安全框架
容器环境,或者叫容器云,其本质是云计算的一种实现方式,我们可以将其称为PaaS或者CaaS。容器技术是云原生体系的底层,因而容器安全也是云原生安全的基石。近两年,随着容器技术越来越多地被大家所青睐,容器安全也逐渐得到了广泛的关注和重视,容器环境的安全是云原生安全的重中之重。
总体而言,容器层面的安全可以分为以下几部分。
相应地,容器云的整体安全建设思路可遵循云计算安全架构,见下图。
云计算安全架构
除了物理安全,容器云环境的安全可以粗略分为两个主要方面:
这样,对于容器云的安全方案,可以分别从两个方面进行设计。
容器云安全建设思路
Kubernetes已经成为事实上的云原生编排系统,那么Kubernetes的安全就成为非常重要的编排安全部分。
编排系统支撑着诸多微服务框架和云原生应用,如无服务、服务网格等,这些新型的微服务体系也同样存在各种安全风险。例如,
云原生安全与传统以虚拟化安全为主的云计算安全有巨大的差别。
统计从一个容器创建到其销毁的时间间隔(TTL),发现容器的生命周期分布呈三种类型,如下图所示。
容器的TTL分布
经过进一步统计发现,46%的容器生命周期短于1小时,11%的容器生命周期短于1分钟。容器安全和虚拟化安全的最大差别看似是隔离技术强度,但其实应是生命周期,甚至没有之一,因为这会影响到攻防双方的战术偏好。
近年来终端侧兴起了终端检测与响应(Endpoint Detection and Response,EDR)系统,该系统通过捕获终端上的进程行为、状态等日志并对其进行分析和规则匹配,发现终端上的恶意攻击。但在容器环境中,一方面,容器逃逸等攻击手法往往利用操作系统层面隔离的漏洞,与通常终端上的恶意软件攻击手法不同,现有的规则检测不能直接适用;另一方面,容器运行的进程行为与桌面终端有很大的差别,在这种场景中更适合用行为特征对工作负载进行识别,类似于用户和实体行为分析(User and Entity Behavior Analytics,UEBA),不过容器上的日志只能体现短时间内的进程、业务模式,无法通过机器学习等方式对正常模式进行画像,这对于高度依赖客户侧做训练的很多UEBA算法是致命的。
因而,如何防护短生命周期的容器是一个非常重要的问题,解决过程中需要转变很多之前固有的防护思维。
在应对短生命周期的容器环境时,防守者会调整异常检测、行为分析的机制。但这种技术路线有两个问题:
安全的本质在于对抗以及攻防投入产出比的平衡。从攻击方的视角看,由于容器的短生命周期,攻击容器的代价较高,而收益较小;但对第三方软件库、项目依赖的镜像“投毒”的持久化代价较小,而其收益远高于攻击容器。
那么从防守者的视角看,如何在降低安全运营成本的同时,提升安全防护效果呢?这两年,业界有一个词比较流行:Shift Left(安全左移)。将软件的生命周期从左到右展开,即开发、测试、集成、部署、运行阶段,安全左移的含义就是将安全防护从传统运行时运营(Ops)转向开发侧(Devs)。
早期防微杜渐的成本永远小于一溃千里后再修复的代价,而且往往发现问题越早,修复效果越好,这种经验在很多场合下都是适用的。以云原生的场景为例,白盒代码的审计难度远远小于对黑盒服务的渗透测试和安全评估,而且由于能掌握代码的跳转逻辑、参数信息,其准确率也相当高;检查镜像的文件系统脆弱性的难度远远小于运行时的恶意攻击检测,而且由于掌握精确的版本和漏洞信息,能够准确知道攻击者的尝试是否成功,效果远好于网络侧的入侵检测。
因此,安全团队要想降低云原生场景下的安全运营成本,提升运营效率,那么首先就应考虑防护思路的转换,贯彻“安全左移”的策略,从重视运行时安全转向先从开发侧解决最基本和最容易的问题。
云原生的兴起离不开开发运营一体化(DevOps)的推动。开发者开发代码后,可以快速编译代码、构建镜像并将其部署到测试、生产环境中,使得整个开发和运营的流程打通,并且保证软件依赖库和运行时环境一致,避免各类环境不一致导致的配置、调试开销。在整个过程中,容器技术天然具有的隔离性、运行时环境一致性、镜像仓库等特性,直接推动了DevOps的落地。
DevOps不只改变开发团队、测试团队和IT运营团队,还有安全团队。传统上,安全团队通常聚焦在运营侧,可能是IT运营团队中的一部分,而开发安全则主要由开发团队负责,两者从组织架构和工作职责上来看是天然分离的。
但如前所述,在云原生场景下,安全左移要求安全团队越来越关注开发侧的安全,但同时又要保证以往运行时的各项安全功能可应用于云原生业务系统中,换句话说,安全能力应该覆盖开发和运营闭环的每个环节,这样的开发运营一体化安全称为DevSecOps,如下图所示。
DevSecOps闭环
编排(Orchestration)是指将各类资源根据业务需要进行动态控制和管理。在云原生场景中,安全架构需要借助容器编排系统的能力来动态部署或销毁安全资源,并按需调度流量牵引或旁路到前述的安全资源,然后将安全策略下发到安全资源,形成全局统一、一致的安全能力。
除了资源管理之外,安全架构也可借助容器编排系统进行动态升级。例如,某个安全应用发布新版,则可以将其推送到镜像仓库中。编排系统可以将部署在所有节点中的安全容器进行版本升级,甚至可以通过灰度升级策略将一部分安全容器升级,其余安全容器保持不变,从而确认本次更新是否会对业务产生影响。
以安全容器的形式防护容器的安全,看似是一种自然且优美的防护机制。
在过去虚拟化安全的实践中,安全团队发现在宿主机层面防护虚拟机内部的安全是非常困难的事情。由于虚拟化是硬件层面的隔离,因而宿主机中的安全机制(如安全代理、虚拟化的安全设备等)都看不到,也阻止不了虚拟机内部的恶意进程或行为。有一种权宜之计是在虚拟机中安装安全代理。但如果是公有云服务商这么做,租户会认为存在被监听、操控的风险;而如果是私有云运营者这么做,也会有稳定性、负载等方面的顾虑。
而在容器环境中,容器技术本质上是操作系统虚拟化,因而宿主机中的安全代理是可以观察到宿主机或容器中的所有进程、文件系统等信息的,也可以通过系统调用对容器中的进程、网络连接进行控制,因而在宿主机层面完全可以实现对宿主机、容器等资产的安全防护。
在云原生环境中,如果安全代理本身就是以容器的形式出现的,那么上述第1点中安全编排所需的安全软件部署、更新、扩容等功能都可以通过容器技术实现。当然,通常这个安全容器需要一些能力(Capability)来获得某些权限,当它需要所有的权限时,它其实就是一个特权容器。在很多安全厂商的容器安全方案中,宿主机侧通常会部署安全特权容器,以实现对其所在宿主机和容器的全方位防护,如下图所示。
特权容器防护示意图
特权容器通常关心的是系统调用、网络流量,而在云原生场景下,业务团队和安全团队会更关心容器所承载业务的安全,如微服务的安全、无服务的安全。这些安全通常存在于网络和传输层之上的应用层,而在应用层上进行防护,通常可以使用Sidecar模式的安全容器。
Sidecar容器本质上就是一种提供反向代理的容器,如下图所示,该容器会劫持业务容器的流量,经过解析后获得应用层请求和响应,然后根据安全策略进行检测或防护。
除此之外,Sidecar安全容器与安全特权容器的区别是Sidecar安全容器尽可能贴近服务,可以与编排系统深度融合,随着微服务和无服务容器的增加而相应增加,反之亦然。可见,Sidecar安全容器的资源管理和策略管理是云原生的。
Sidecar防护示意图
随着越来越多的组织向微服务/DevOps转型,容器生态系统每年都会发生很大的变化,因而云原生安全现状也会出现快速演进的现象。
下图来自sysdig 2023云原生安全和使用报告。
在镜像中每天都会发现新的漏洞,但是在规模化地维护多个workloads时,修复每一个漏洞是不切实际的。成功地现代漏洞管理模式,需要安全团队根据漏洞对组织实际风险做评估,综合考虑。
常用于优先考虑漏洞修复工作的方法包括:
解决正在运行且存在被已知漏洞利用的软件包应该是首要任务。研究发现,客户积极主动地修复易受攻击且在运行时加载的漏洞。当结合上述漏洞的多个标准(修复可用性、易受攻击性、运行时加载的软件包)时,在分析的25000个镜像中找到的漏洞仅占2%。
研究人员计算了运行时加载包的漏洞百分比,根据包的类型来衡量哪种语言、库或文件类型带来的漏洞风险最大。在运行包中的32万多个漏洞中,java包占61%。
在已识别的1777个恶意镜像中,上图图表中显示了包含的恶意内容的类型。
容器是敏捷管理的理想伴侣,它加速了代码开发和发布,通常作为容器化的微服务。
上图的镜像寿命数据反映了代码发布时间间隔的变化,CI/CD流水线正在帮助开发团队,比以往任何时候都更快速度地交付软件。
容器的寿命本来就很短,过去几年中,近一半的容器寿命都不到5分钟。在2023年,这个数字增长到了70%以上,容器寿命少于5分钟。这是一个巨大的飞跃,强调了持续威胁检测和调查记录的必要性,因为容器可能只存在几秒钟。
为什么容器的寿命如此短呢?因为许多容器只需要在执行函数时存在足够长的时间即可,在完成任务后终止。几秒钟可能很短,但对于一些进程来说,这就足够了。
容器的短暂性仍然是这项技术的独特优势之一,因为容器镜像是可以根据需要进行更改的。然后,这也为监视、安全和合规性带来了新的挑战,因为许多工具无法报告不再存在的实体。
容器是敏捷管理的理想伴侣,它加速了代码开发和发布,通常作为容器化的微服务。CI/CD流水线正在帮助开发团队,比以往任何时候都更快速度地交付软件。
数据显示,大约有一半的容器镜像在一周或更短的时间内被替换,也可以成为迭代。对于今天的大多数企业来说,快速上市的速度很重要,这对于保持竞争力来说至关重要。代码被更频繁地部署,这就产生了新的容器镜像。
研究每个客户在其基础设施中运行的容器数量,61%的客户运行超过250个容器。在高级用户中,只有6%的客户管理超过5000个容器。
DevOps和云计算团队报告称,一旦证明了收益,采用的速度就会加快,因为更多的业务部门会考虑加入新平台。
今年的情况表明,总体上容器数量有所增加。这种转变可能表明,更多的workloads正在转向容器,远离传统的架构,或者基础设施越来越高效,能够处理越来越多的容器。
综上来说:
企业正在迅速采用容器化微服务、CI/CD和按需的云服来加快创新。然而,变化如此之快也带来了风险,因为云扩展和云原生应用程序的复杂性暴露了DevSecOps流程的成熟度不足的问题。此外,来自于配置错误和漏洞的供应链风险已经成为一个重要的关注领域。
研究表明,尽管人们对所需工具和零信任方法的益处有所认识,但云安全流程仍然落后于云计算应用的快速发展速度。从所调查的实际客户数据中可以看出,有几个安全实践领域需要改进以减少风险:
参考资料:
https://marketing.alauda.cn/service/extfile/page/032cee11d9c84ca2ac7a850da71234e1?cl_sr=inbound&cl_source1=%E5%85%AC%E4%BC%97%E5%8F%B7-%E7%81%B5%E9%9B%80%E4%BA%91#/previewer
下图较为全面地展现了Docker用户在使用容器的过程中涉及的组件或操作,主要包含Docker客户端、Docker容器所在宿主机和镜像仓库三部分。
它涉及的使用场景主要包括以下两类:
结合桑图,我们从容器镜像、活动容器、容器网络、容器管理程序接口、宿主机操作系统和软件漏洞六个方面来分析容器基础设施可能存在的风险。
所有容器都来自容器镜像。因此,我们首先研究容器镜像的风险。
与虚拟机镜像不同的是,容器镜像是一个不包含系统内核的联合文件系统(Unionfs),即为进程的正常运行提供基本、一致的文件环境。另外,容器是动态的,镜像是静态的。考虑到这一特点,我们从镜像的内容和镜像的流通、使用等几方面开展分析。
随着容器技术的成熟和流行,大部分流行的开源软件都提供了Dockerfile和容器镜像。在实际的容器化应用开发过程中,人们很少从零开始构建自己的业务镜像,而是将Docker Hub上的镜像作为基础镜像,在此基础上增加自己的代码或程序,然后打包成最终的业务镜像并上线运行。例如,为了提供Web服务,开发人员可能会在Django镜像的基础上,加上自己编写的Python代码,然后打包成Web后端镜像。
毫无疑问,这种积累和复用减少了造轮子的次数,大大提高了开发效率和软件质量,推动了现代软件工程的发展。如今,一个较为普遍的情况是,用户自己的代码依赖若干开源组件,这些开源组件本身又有着复杂的依赖树,甚至最终打包好的业务镜像中还包含完全用不到的开源组件。这导致许多开发者可能根本不知道自己的镜像中到底包含多少以及哪些组件。包含的组件越多,可能存在的漏洞就越多,大量引入第三方组件的同时也大量引入了风险。2020年,有研究报告显示,在使用最为广泛的镜像仓库Docker Hub中,约有51%的镜像至少包含一个危险(critical)级别的安全漏洞。这意味着,使用这些镜像或基于这些镜像制作目的镜像都将使最终业务面临安全风险——无论业务自身代码程序的安全性如何。
除了有漏洞的可信开源镜像外,以Docker Hub为代表的公共镜像仓库中还可能存在一些恶意镜像。如果使用了这些镜像或把这些镜像作为基础镜像,其行为相当于引狼入室,风险不言自明。
容器的先进性之一在于它提供了“一次开发,随处部署”的可能性,大大降低了开发者和运维人员的负担。但凡事有利就有弊。为了开发、调试方便,开发者可能会将敏感信息——如数据库密码、证书和私钥等内容直接写到代码中,或者以配置文件形式存放。构建镜像时,这些敏感内容被一并打包进镜像,甚至上传到公开的镜像仓库,从而造成敏感数据泄露。
除了静态容器镜像的风险,当镜像以容器的形式运行起来后,这些活动容器又存在哪些风险呢?
与传统IT环境类似,容器环境下的业务代码本身也可能存在Bug甚至安全漏洞。容器技术并不能解决这些问题。无论是SQL注入、XSS和文件上传漏洞,还是反序列化或缓冲区溢出漏洞,它们都有可能出现在容器化应用中。
容器默认情况下连接到由docker0网桥提供的子网中。如果在启动时配置了端口映射,容器就能够对外提供服务。在这种情况下,前述各种安全漏洞就有可能被外部攻击者利用,从而导致容器被入侵。
与其他虚拟化技术一样,容器并非空中楼阁。既然运行在宿主机上,容器必然要使用宿主机提供的各种资源——计算资源、存储资源等。如果容器使用了过多资源,就会对宿主机及宿主机上的其他容器造成影响,甚至形成资源耗尽型攻击。
然而,在默认情况下,Docker并不会对容器的资源使用进行限制。也就是说,默认配置启动的容器理论上能够无限使用宿主机的CPU、内存、硬盘等资源。
“配置与挂载”指的是容器在启动时带有的配置选项和挂载选项。我们知道,作为一种虚拟化技术,容器的核心是两大隔离机制:
除此以外,Capabilities、Seccomp和AppArmor等机制通过限制容器内进程的权限和系统调用访问能力,进一步提高了容器的安全性。
为什么配置和挂载也可能导致风险呢?因为通过简单的配置和挂载,容器的隔离性将被轻易打破。例如:
因此,用户在对容器进行配置时,一定要慎之又慎。
默认情况下每个容器处于自己独立的网络命名空间中,与宿主机之间存在隔离。然而,每个容器都处于由docker0网桥构建的同一局域网内,彼此之间互相连通。理论上,容器之间可能发生网络攻击,尤其是中间人攻击等局域网内常见的攻击方式。
事实也确实如此。容器内的root用户虽然被Docker禁用了许多权限(Capabilities机制),但它目前依然具有CAP_NET_RAW权限,具备构造并发送ICMP、ARP等报文的能力。因此,ARP欺骗、DNS劫持等中间人攻击是可能发生在容器网络的。
Socket是Docker守护进程接收请求及返回响应的应用接口。Docker守护进程主要监听两种形式的Socket:
安装完成并启动后,Docker守护进程默认只监听UNIX socket。
为什么UNIX socket也可能存在风险呢?它的问题主要与Docker守护进程的高权限有关:Docker守护进程默认以宿主机root权限运行。只要能够与该UNIX socket进行交互,就可以借助Docker守护进程以root权限在宿主机上执行任意命令。相关的风险利用场景主要有两个:
在版本较新的Docker中,Docker守护进程默认不会监听TCP socket。用户可以通过配置文件来设置Docker守护进程开启对TCP socket的监听,默认监听端口一般是2375。
然而,默认情况下对Docker守护进程TCP socket的访问是无加密且无认证的。因此,任何网络可达的访问者都可以通过该TCP socket来对Docker守护进程下发命令。例如,以下命令能够列出IP为192.168.1.101的主机上的所有活动容器:
docker -H tcp://192.168.1.101:2375 ps
显而易见,攻击者也能够通过这样的TCP socket对目标主机上的Docker守护进程下发命令,从而实现对目标主机的控制。控制方式与通过UNIX socket的控制类似,只是需要通过-H tcp://参数来设置目标地址和端口。
与虚拟机不同,作为一种轻量级虚拟化技术,容器通常与宿主机共享内核。这意味着,如果宿主机内核本身存在安全漏洞,理论上,这些漏洞是能够在容器内进行利用的。通过利用这些漏洞,攻击者可能实现权限提升,甚至从容器中逃逸,获得宿主机的控制权。
例如,在存在CVE-2016-5195(“脏牛”)漏洞的容器环境中,攻击者可以借助该漏洞向进程vDSO区域写入恶意代码,从而实现容器逃逸。
令人欣慰的是,Capabilities及Seccomp机制在一定程度上缓解了共享内核带来的问题。另外,以Kata Containers和gVisor为代表的安全容器则能够较为彻底地解决共享内核带来的安全问题。前者为每一个容器创建一个独立的轻量虚拟机,后者在用户空间模拟内核以处理系统调用,虽然实现思路不同,但都致力于让容器摆脱对宿主机内核的直接依赖。但是,安全容器不等于绝对安全。依然存在容器逃逸的安全风险。
任何软件都存在漏洞,Docker自然不会例外。在已经曝光的漏洞中,CVE-2019-14271、CVE-2019-5736等漏洞能够导致容器逃逸,属于高危漏洞,其中CVE-2019-14271的CVSS 3.x风险评分更是高达9.8分(满分为10)
过去,开发者关心的安全要素主要是代码安全性,如写的代码是否足够健壮、代码是否正确处理了异常且不会引起拒绝服务、代码是否能够有效阻止各种注入漏洞、是否有溢出等。诚然,代码安全性非常重要。但是,随着以容器为代表的云原生技术的兴起,开发环境与生产环境的差异被“容器化”逐渐消解,容器技术顺理成章地参与到开发者的编码、调试、打包过程中。然而,这同样意味着,容器自身的安全问题可能会给上述开发阶段的各个过程带来风险。
SDL(Security Development Lifecycle,安全开发生命周期)实践告诉我们,做安全越早越好,从开发阶段就开始对安全性进行合理的评估和控制能够有效提升整个工程质量。反之亦然,如果在开发阶段就引入安全问题,那么它往往是最隐蔽的,在运行时再检测这些问题将会颇为棘手。
随着容器技术的普及,容器镜像也成为软件供应链中非常重要的一部分。人们像使用pip等工具从仓库获取各种编程语言软件库一样,可从Docker Hub或第三方仓库拉取镜像,在其基础上进行开发,从而实现所需功能,最后打包发布。
然而,业务依赖的基础镜像可能存在问题——无论是开发者无心导致的安全漏洞还是攻击者故意埋下的恶意代码,这种“内生风险”的潜在危害比黑客从外部发起攻击严重得多,且更不易被发现。
在之前的小节,我们已经曝光了容器相关的开发侧和软件供应链可能出现的攻击形式。按照CI/CD的思路,从开发到集成,接下来就是生产运行了。运行时环境是攻防交锋最为精彩的擂台。
运行时容器可能发生的攻击形式数不胜数,然而归根结底,所有攻击影响的还是业务系统的机密性、完整性和可用性(CIA三要素)。从这个角度出发,我们可以对攻击做以下分类:
基于上述分类,我们将介绍两种非常典型的攻击方式:容器及安全容器逃逸、从容器发起的资源耗尽型攻击。
与其他虚拟化技术类似,逃逸是最为严重的安全风险,直接危害了底层宿主机和整个云计算系统的安全。
“容器逃逸”是指以下一种过程和结果:首先,攻击者通过劫持容器化业务逻辑或直接控制(CaaS等合法获得容器控制权的场景)等方式,已经获得了容器内某种权限下的命令执行能力;攻击者利用这种命令执行能力,借助一些手段进而获得该容器所在的直接宿主机上某种权限下的命令执行能力。
注意以下几点:
将这些注意点延伸开来,能够获得很有意思的见解。例如,结合第4点我们可以想到,在权限持久化攻防博弈的进程中,人们逐渐积累了众多Linux场景下建立后门的方法。其中一大经典模式是向特定文件中写入绑定shell或反弹shell语句,五花八门,不胜枚举。
那么如果容器挂载了宿主机的某些文件或目录,将挂载列表与前述用于建立后门而写入shell的文件、目录列表取交集,就可以得到容器逃逸的可能途径呢,见下图。进一步说,用于防御和检测后门的思路和技术,经过改进和移植也能覆盖掉某种类型的容器逃逸问题。
undone
由于云原生应用具备高度弹性化、可扩展、可移植等特点,现今大多数企业已纷纷将其应用从传统的单体架构转向微服务架构,云计算模式也相应地从IaaS转向CaaS和FaaS(Function as aService,函数即服务)。
应用架构和云计算模式的变革会导致进一步的风险,这些风险较之传统应用风险存在延续的地方 ,同时也衍生出的新的风险。
综上,云原生应用带来的风险是不容小觑的,接下来我们将针对以上观点进行详细说明。
由于云原生应用也是应用,因而云原生应用风险可以参考传统应用风险。传统应用风险以Web应用风险为主,主要包含注入、敏感数据泄露、跨站脚本、使用含有已知漏洞的组件、不足的日志记录和监控等风险。
此外,在云原生环境中,应用的API交互模式逐渐由“人机交互”转变为“机机交互”。虽然API大量出现是云原生环境的一大特点,但本质上来说,API风险并无新的变化,因而其风险可以参考现有的API风险,主要包含安全性错误配置和注入、资产管理不当、资源缺失和速率限制等风险。
有关传统应用风险和API风险的更多细节可以分别参考OWASP组织在2017年和2019年发布的《OWASP应用十大风险报告》和《OWASP API十大风险报告》。
云原生应用面临的新风险主要“新”在哪里?在笔者看来,“新”主要体现在新应用架构。我们知道,新应用架构遵循微服务化的设计模式,通过应用的微服务化,我们能够构建容错性好、易于管理的松耦合系统。与此同时,新应用架构的出现也会引入新的风险。为了较为完整地对风险进行分析,在本节我们将以信息系统安全等级三要素,即机密性(Confidentiality)、完整性(Integrity)和可用性(Availability)作为导向,为各位读者介绍应用架构变化带来的新风险。
本节接下来的内容将以信息泄露、未授权访问、拒绝服务为例,分别介绍上述三类风险。
在云原生环境中,虽然造成应用数据泄露风险的原因有很多,但都离不开以下几个因素。
我们知道,应用中存储的数据多是基于API进行访问的,若应用中某API含有未授权访问漏洞,如Redis未授权访问漏洞,攻击者便可利用此漏洞绕过Redis认证机制访问内部数据,导致敏感信息泄露。
在传统单体应用架构下,由于API访问范围为用户到应用,攻击者只能看到外部进入应用的流量,无法看到应用内部的流量,所以恶意使用API漏洞进行数据窃取造成的损失通常是有限的。
反观微服务应用架构,当单体应用被拆分为若干个服务后,这些服务会根据业务情况进行相互访问,API访问范围变为服务到服务(Service to Service)。若某服务存在API漏洞,导致攻击者有利可图,那么攻击者将会看到应用内部的流量,这无疑为攻击者提供了更多的攻击渠道,因而就数据泄露的风险程度而言,相比传统单体应用架构,微服务架构带来的风险更大。此外,随着服务数量达到一定规模,API数量将不断递增,从而扩大了攻击面,增大了数据泄露的风险。
在应用的开发过程中,开发者常疏于密钥管理,导致数据泄露的风险。例如开发者将密钥信息、数据库连接密码等敏感信息硬编码在应用程序中,从而增大了应用程序日志泄露、应用程序访问密钥泄露等风险。
在传统单体应用架构中,开发者常将配置连同应用一起打包,当需要修改配置时,只需登录至服务端进行相应修改,再重启应用便可实现。从密钥管理风险的角度上讲,这种单个集中式配置文件的存储方式的风险是相对可控的。
在微服务应用架构中,应用的配置数量与服务数量是成正比的,服务越多,配置就越多。例如,微服务应用中会存在各种服务、数据库访问、环境变量的配置,且各个配置支持动态调整。同时,微服务应用架构对服务的配置管理也提出了更高的要求,如代码与配置可分离、配置支持分布式、配置实时可更新、配置可统一治理等。因此,微服务下的配置管理更加复杂,对运维人员的要求更高,密钥管理的难度也在不断提升,最终会造成更大的数据泄露风险。
如我们所知,如果应用采用HTTP进行数据传输,那么HTTP页面的所有信息都将以纯文本形式传输,默认是不提供任何加密措施的,因而在数据传输过程中易被攻击者监听、截获和篡改。典型的攻击流程为:攻击者首先通过Fiddler、Wireshark等抓包工具进行流量监听,之后截获传输的敏感信息,如数据库密码、登录密码等,最后根据自身意图对敏感数据进行篡改并发送至服务端,进而导致数据泄露的风险。
在传统单体应用架构中,由于网络拓扑相对简单,且应用通信多基于HTTP/HTTPS,因而造成的数据泄露风险多是因为采用了HTTP。在微服务应用架构中,网络拓扑相对复杂,具有分布式的特点,应用间的通信不仅采用HTTP/HTTPS,还采用gRPC等协议,而gRPC协议默认不加密,将会导致攻击面增多,带来更多的数据泄露风险。
在云原生环境中,应用未授权访问的风险多是由应用自身漏洞或访问权限错误配置导致的。
应用漏洞是造成未授权访问的一大因素。如我们所知,未授权访问漏洞非常多,Redis、MongoDB、Jenkins、Docker、ZooKeeper、Hadoop等常见的应用都曾曝光过相关漏洞。例如
从漏洞成因来看,认证及授权机制薄弱是其主要原因。在单体应用架构下,应用作为一个整体对用户进行认证授权,且应用的访问来源相对单一,基本为浏览器,因而风险是相对可控的。在微服务应用架构下,其包含的所有服务均须对各自的访问进行授权,从而明确当前用户的访问控制权限。此外,服务的访问来源除了用户外还包含内部的其他服务,因而在微服务架构下,应用的认证授权机制更为复杂,为云原生应用带来了更多的攻击面。
如果运维人员对用户的访问权限进行了错误配置,就会增大被攻击者利用的风险。例如,运维人员对Web应用访问权限进行相应配置,针对普通用户,运维人员应只赋予其只读操作,若运维人员进行了错误的配置,如为普通用户配置了写操作,那么攻击者便会利用此缺陷绕过认证访问机制,对应用发起未授权访问攻击。
在传统应用架构中,由于设计相对单一,应用的访问权限也相对单一,几乎只涉及用户对应用的访问权限这一层面,因此对应的访问权限配置也相对简单。诚然,也因访问权限配置简单,用户身份凭据等所有敏感信息常存储在应用的服务端,一旦攻击者利用配置的缺陷对应用发起未授权访问入侵,就有可能拿到所有保存在后端的数据,从而造成巨大风险。
在微服务应用架构下,由于访问权限还须涉及服务对服务这一层面,因而权限映射关系变得更加复杂,相应的权限配置难度也在同步增加。例如一个复杂应用被拆分为100个服务,运维人员需要精密地对每个服务赋予其应有的权限,如果因疏忽为某个服务配置了错误的权限,攻击者就有可能利用此缺陷对服务展开攻击。若该服务中包含漏洞,就可能会导致单一漏洞扩展至整个应用。所以如何对云原生应用的访问权限进行高效率管理成为一个较难的问题,这也是导致其风险的关键因素。
拒绝服务是应用程序面临的常见风险。造成拒绝服务的主要原因包含两方面。
应用漏洞可以导致应用被拒绝服务攻击,那么具体是如何形成的呢?以ReDoS漏洞为例。ReDoS为正则表达式拒绝服务,攻击者对该漏洞的利用通常是这样的场景:应用程序为用户提供了正则表达式的输入类型但又没有对具体的输入进行有效验证,那么攻击者便可通过构造解析效率极低的正则表达式作为输入,在短时间内引发100%的CPU占用率,最终导致资源耗尽甚至应用程序崩溃。
此处我们以CC攻击举例,其攻击原理通常是攻击者通过控制僵尸网络、肉鸡或代理服务器不断地向目标主机发送大量合法请求,从而使正常用户的请求处理变得异常缓慢。
在传统Web场景中,攻击者利用代理服务器向受害者发起大量HTTP GET请求,该请求主要通过动态页面向数据库发送访问操作,通过大量的连接,数据库负载极高,并超过其正常处理能力,从而无法响应正常请求,最终导致服务器宕机。
在微服务应用架构下,由于API数量会随着服务数量的递增而递增,因而可能会导致单一请求生成数以万计的复杂中间层和后端服务调用,进而更容易引起被拒绝服务攻击的风险。例如若微服务应用的API设计未考虑太多因单个API调用引起的耗时问题,那么当外部访问量突增时,将会导致访问需求与资源能力不匹配的问题,使服务端无法对请求做出及时的响应,造成页面卡死,进而引起系统崩溃。
请参阅这篇文章。
在之前的章节中,我们提到了46%的容器生命周期小于1小时,11%的容器生命周期小于1分钟,并且分析了这种短生命周期对云原生环境中的对抗带来的影响。
总体而言,容器环境中的短生命周期是云原生业务和编排机制造成的,这种特性给攻防双方都带来了变化。
云原生时代,攻击者会更关注软件开发的早期阶段,主要是一些更为持久的资产,如代码、第三方库、镜像、仓库、编排系统、控制面、宿主机等。
那么对于安全团队而言,早期投入和可用的安全技术都不会很多,做最简单的事情意义最大,此时可以考虑将安全控制向开发侧转移,也就是从运营安全转向开发安全。因为在DevOps的闭环图中,开发在左侧,运营在右侧,所以又称为安全左移(Shift Left)。
安全左移需要考虑开发安全、软件供应链安全、镜像仓库、配置核查这四个部分。
软件工程是系统性工程,其中软件代码的安全漏洞是影响软件最终运行安全性的重要因素。
然而现实并没有想象中乐观,2020全年公开CVE漏洞数为14443,其中危急漏洞占比14.07%,高危漏洞占比42.59%,两者占比达到56.66%,攻击者利用此类漏洞可以远程执行任意命令或者代码,可见代码安全的整体形势确实不容乐观。自有代码产生脆弱性的主要原因是代码开发者缺乏安全经验和安全意识,在编写代码时没有进行必要的安全检查。
为了应对代码产生的漏洞,应该对代码进行安全审计。审计实施人员对系统重要业务场景进行风险分析并审计源代码,通常可使用静态应用安全测试(Static Application SecurityTesting,SAST)的方式进行审计。
软件供应链存在风险,第三方库的安全性不容乐观,特别是开源软件,通常一个项目会使用大量的开源软件。据Gartner统计,2016年至少95%的IT机构会在关键IT产品中使用开源软件(这个数字在2010年为75%),这些软件可能并不被机构所感知;如今新应用程序70%~90%的代码来自外部或第三方组件,平均每个应用程序有105个开源组件。其中最让人不安的是企业对其是否使用了开源软件、使用了什么开源软件并不了解。
为了应对此类风险,可使用软件成分分析(Software Composition Analysis,SCA)技术以发现项目中用到的第三方软件库(特别是开源软件),分析相关代码版本库,将其与漏洞库比较,如有匹配则告知存在漏洞。
容器镜像是早期云原生安全的重要阵地,攻防双方都会聚焦在此。
针对容器镜像,可采用分析工具与人工审计相结合的方式,根据提供的应用开发过程文档,审阅系统实现的技术方案,对架构的安全性进行审计和评估,分析系统防护薄弱点及可能存在的安全风险。
镜像构建的方式通常有两种:基于容器直接构建或基于Dockerfile构建。建议所有的镜像文件由Dockerfile创建,因为基于Dockerfile构建的镜像是完全透明的,所有的操作指令都是可控和可追溯的。
镜像构建存在的风险项通常包括:
针对以上问题,可以从下面几方面来加固镜像构建安全。
公共仓库安全
Docker Hub是目前最大的容器镜像仓库。有研究报告中提到Docker Hub中超过30%的镜像包含高危漏洞,因此在享受Docker Hub带来的便利时,也应确保下载镜像的安全性。
私有仓库安全
容器都是由本地存储的镜像快速启动运行的,那么镜像的安全性直接关乎到容器安全。除了前述提及容器仓库中镜像安全性的问题外,本地构建的镜像也会引入第三方库,造成安全风险。所以,对下载的镜像和本地构建的镜像进行安全检测就显得尤其重要。这里的漏洞检测主要还是针对已知的CVE漏洞进行扫描分析。
目前比较流行的镜像扫描引擎有Docker Security Scanning(未开源)、Clair(开源)和Anchore(开源)等。
镜像检测的核心目前仍然是已知系统CVE检测。扫描器获取到镜像后,将它分离成相应的层和软件包,然后将这些包与多个CVE数据库包的名称和版本进行对比,从而判定是否存在漏洞。通常开源的镜像漏洞扫描工具会获取各发行版官方途径安装的软件,如Debian/Ubuntu通过apt/apt-get/aptitude等命令安装软件包,而Red Hat/CentOS则是yum/rpm安装的软件包,而至于软件开发者自己部署的非官方软件,这些扫描工具一般是不覆盖的,因而读者需要自己编写相关的扫描插件,或者购买商业版的镜像漏洞扫描器。
还有一些通过扫描镜像中的环境变量、操作命令及端口开放信息来识别恶意镜像的方案,但对于这些方案使用者仍然需要自己基于结果来判断,没有统一的标准。
容器镜像在下载和上传时须保证完整性和秘密性,以下建议有助于抵御如中间人攻击等威胁。
攻防永远是成本和收益之间的平衡,如果防守方能做好对长生命周期资产的持续风险和脆弱性的评估和缓解,那么攻击者的成本显然会升高。他们就会尝试攻击业务容器,即便其生命周期短,但只要使用自动化的攻击工具,还是可以实现短时间的持久化的,然后利用这一段时间窗口进行横向移动,最后找到可以持久化的资源也是可能的。
因而防守者还是需要回到运营侧安全,但在容器环境中,无论是杀毒软件(AV),还是EDR或UEBA,都或多或少存在一些固有的缺陷,或不能用,或要做改造。例如,
下图比较了某宿主机上容器中进程的相似度,可以发现大量容器中的进程存在相似性,这就能对容器群的进程进行画像,偏离基线的进程都可以认为是异常行为。
下图展示了一些异常行为,如某些进程所在的文件路径与正常的不同,就很可能是攻击者尝试逃避进程名白名单机制,而CPU偏高可能存在如挖矿等资源耗尽的攻击。
需要注意的是,这些异常检测都是基于机器学习得到的,也就是统计意义上的异常,并不能“实锤”恶意行为。如果要获得高置信度的告警,则需要通过专家经验规则来匹配恶意行为。
在云原生环境中,开发者关注的是如何编写业务功能,而攻击者关注最多的也是是否能滥用业务功能,最典型的就是“薅羊毛”等黑产。
业务安全是离客户最近且价值最大的安全功能,然而云原生场景非常复杂,很难有统一的业务安全模型;此外,在微服务和服务网格的场景下,服务之间大部分通过API调用实现,这与当前Web应用存在大量人机交互完全不同,因而API安全在云原生场景下也存在很大的差异。
从技术上看,要实现API和业务安全,第一步是获得API调用的可观测能力(visibility),当前有很多开源项目具有这样的能力,但其在开发、构建过程中的侵入性各有不同,如下表所示。一般而言,侵入性越强,其最后获得的API请求信息越多,但在既有的开发流程和项目中部署就越难。
其中Jaeger通过在代码中插桩的方式,能够获得所有调用顺序和参数,因而理论上就能建立非常精准的API调用参数和序列的基线,而Sidecar反向代理的方式无法获得调用序列,只能通过分析启发式地获得近似基线,其上限可以逼近Jaeger所得到的基线。
获得API调用的参数范围、调用序列后,就可以通过学习获得正常的基线,如果攻击者调用的参数、序列出现异常,安全团队就可能获知并找到相关的恶意行为。