RASP基础 #
简介 #
RASP,全名 Runtime Application Self-Protection,即运行时应用自我保护。RASP 是把安全防护放到了应用内部,在代码执行层面实时监控和阻断攻击。
我们对于一般的 Web 应用都会用 WAF 来进行保护。WAF 采用的是黑名单模型,它维护大量攻击特征库,流量只要匹配到就会拦截,但是这有可能会被攻击者经过一些特殊手法实现绕过。
RASP 采用的是行为分析模型,它不关心用户的输入是什么样的,而是去 hook 住像数据库查询、命令执行、文件读写之类的敏感操作,观察实际要执行的动作有没有被用户的输入篡改。
通过 RASP 技术,用户不再单纯依赖像防火墙等外围防护技术。RASP 附加到应用中,可以部署多个探针以实现全面监控,并且实时保护应用程序免受攻击,同时保持较低的误报率和性能开销。
RASP 覆盖整个应用堆栈,包括应用程序代码、所使用的库或框架,甚至是一些商业软件。生成的安全警报可以转发到数据平台进行分析、上传到工单系统或者继承到 XDR 平台。像 Spring4Shell、Log4Shell 和许多其他0day漏洞都可以通过 RASP 来防止。
有一些 RASP 解决方案在 HTTP 请求由业务代码处理前,经过一系列 Filter,还有一些解决方案前置一个 Interceptor,一旦检测到而已请求立即阻止。但是绕过这些基于流量的检测是很容易的,漏洞利用语言或者框架的解析特性混淆或编码字符串就能绕过。这两种解决方案没有利用任何上下文,效果甚至可能步入传统的 WAF,而且又继承了 WAF 的所有缺点。同时,由于 RASP 可以看作为 WAF 融合进了应用程序,而一些 WAF 本身就存在漏洞,所以可能会造成更大的安全影响。
RASP核心机制 #
这里就以 Java 为例,我目前设计的 RASP 项目也是针对的 Java 的。
Java RASP 能够嵌入到应用中,主要依靠 Java Agent + 字节码插桩:
- **注入 Agent:**通过
-javaagent:rasp.jar的方式在 JVM 启动时加载,或者用agentmain模式动态附加到运行中的进程。 - **Hook 敏感类:**用字节码框架在
FileInputStream、Statement.execute、Runtime.exec等关键方法的开头或者结尾插入检测逻辑。 - 请求级上下文:当 Tomcat 收到请求时,RASP 标记当前线程,后续所有敏感操作都能关联到这个请求的用户输入,判断是否被污染。
- **插件化检测:**对于 OpenRASP 来说检测逻辑用的是 JavaScript 编写的,这样可以通过 Rhino 或 V8 引擎执行,方便快速更新规则而不需要重启应用。
RASP实现概念 #
要实现 Java RASP,就需要在在字节码中实现插桩(Instrumentation),而执行 Instrumentation 的 agent 是使用像ByteBuddy、ASM、Javassist等字节码操作库实现的。当我们启动 Java 程序时,主应用程序不会立即执行,Java 将创建一个 JVM,然后加载并启动-javaagent命令行参数所指定的 Java agent。而 agent 会注册一个负责 Hook 的 ClassTransformer,该 Transformer 用于安全防护。当应用程序正在运行并且正在加载一个新类时,类加载器将在加载此类时在 Instrucmentation API 上通知 ClassTransformer。如果符合 Hook 规则,类就会被修改,称作 patched,最后加载到 JVM 中。每当代码与这个类或者 API 交互时,RASP 都会察觉,并判断此类代码是否应该继续执行。
RASP、WAF、EDR 的区别 #
RASP、WAF 和 EDR 是三种安全技术,可以单独使用,也可以组合使用。WAF 一般部署在 Web 应用的前面,直接作用于流量,能阻拦大部分明显恶意流量。但是 WAF 也有明显的缺点:
- 因为 WAF 严重依赖正则表达式和模式匹配,导致容易被绕过;
- 需要大量的人工调整才能应对 0day;
- 缺乏上下文,会导致较高的误报率。
相比之下,RASP 集成到应用程序内部,能够拥有完整的上下文,能在攻击到达主机之前阻止并检测攻击。与 WAF 相比,RASP 做到了与应用融为一体。同时,RASP 也对 0day 具有一定的防护能力,而且几乎不需要人工调整,这导致的误报也相对较少。
EDR 全称 Endpoint Detection and Response,即断点检测与响应。这是一个部署在终端设备的安全软件,核心功能是持续监控设备上的活动,发现可疑行为不仅进行报警,也会自动做出响应。EDR 不只看文件本身,但是它更加关注行为。比如一个 word 文档突然启动 powershell 去连接一个陌生 IP,这种异常的“行为链”就会被 EDR 捕捉到。
EDR 是在进程级别运行的,负责主机的安全。它的探针挂在操作系统层面,主要监控进程的行为:
- 哪个进程启动了
- 它读了什么文件、写了什么文件
- 它发起了哪些网络连接
- 它调用了哪些系统 API
- 父子进程关系(谁启动了谁)
这种设计就导致 EDR 能看到的信息基本都局限在进程这一层面:
- 进程意图:这个进程在干什么。比如试图读取
/etc/passed、试图连接某一个陌生 IP。 - 进程链:进程之间的父子关系。比如
Word->powershell->cmd->下载恶意文件,这条链能反映攻击者的操作路径。
但是 EDR 也有很多东西看不到:
- 网络流量里到底传了什么内容
- 用户身份和权限的全局关系
- 邮件系统里哪封邮件带了恶意附件
- 应用内部的逻辑漏洞
所以 EDR 的信息是局部的、进程维度的。但是在攻击的调查期间,明确入侵点至关重要,EDR 无法给出攻击入口。
所以 RASP 在扩展检测与响应(XDR)中发挥重要作用,提供应用程序层的监测。
WAF、RASP 和 EDR 并不构成直接竞争关系。在 SQL 注入的场景,攻击者利用编码技巧绕过了 WAF,RASP 未能及时 patch 应用程序时,EDR 能在 SQL 服务器被攻破并试图启动 PowerShell 脚本时成功阻止攻击。所以 WAF、RASP、EDR 应该是协同工作,互相补充。
与此同时,Java 平台自身就有一个沙箱解决方案,即 Java Security Manager (JSM)。JSM 通过限制执行调用代码的权限并拒绝访问有价值的资源,如文件系统或网络,来应用最小特权原则。假设攻击者设法加载了一个恶意类,并想要调用 ProcessBuilder.start 启动一个进程。Security Manager 会将权限检查委托给 Access Controller,然后 Access Controller 遍历调用栈,确保调用栈上每个调用者都有正确的权限。只要有一个调用者权限不足,就会抛出异常,拒绝访问。
相比之下,RASP在设计之初就考虑到了供应链安全,其拥有完整的上下文信息。通过密切观察应用程序的行为和数据流动情况,使得RASP能够更深层次地检查和控制应用程序,超越了JSM所能达到的深度。
RASP阻断时机 #
在实现 RASP 解决方案之前,先了解一下 Java 应用可能遭受的攻击类型以及防护方法。攻击者要入侵应用,首先要找到入口,可能是一个暴露了 Web 服务的容器,如 Netty 或者 Tomcat HTTP。当攻击者发起请求时,应用可能遭受反序列化漏洞、SQL 注入等,或包含有可被利用的 0day 漏洞的第三方库和框架,导致攻击者能在机器上执行任意代码。
一旦攻击者设法在 JVM 上执行代码,就可以决定是否启动一个进程,EDR 会检测到进程启动。但是攻击者可以选择窃取敏感信息,部署恶意库并将其加载到 JVM 中,不会新启动进程从而绕过 EDR 的检测。除此之外,攻击者还可以暂时留在 JVM 中,不留下任何文件的情况下部署一个无文件的 Webshell,比如一些通过模拟注册 Filter、Interceptor、Controller 的 Spring shell,也可以破坏植入应用程序中的所有安全机制,随意越权使用应用的全部功能。
RASP 会在应用内部部署大量探针,监控范围不仅涵盖 Java API 类库,还涵盖正在使用的第三方库和 Web 框架,比如 SpringBoot。一旦数据进入应用程序,RASP 通过代码插桩将其标记为受污染数据。如果 RASP 在 Runtime 处进行插桩,之后 RASP 会追踪数据从请求到 Java Runtime 库的流动过程。例如攻击者试图从 Spring 库启动一个进程,或者受污染的数据改变了 SQL 查询的内容,RASP 会对这些经典的攻击模式进行阻止。
RASP 通常针对 OWASP Top 10 中大部分注入攻击使用黑名单机制。黑名单不只局限于字符串层面,还涉及 package 层和 Gadget 层,比如 JNDI lookup、文件部署、反序列化代码、表达式语言解释器等。
虽然 RASP 可以在应用程序不同阶段阻断攻击链,但为了避免绕过,需要尽快阻断攻击。例如,RASP 监视到程序试图从 Spring 启动进程,那么应该立即停止,而不是等到启动进程后中断进程。因为 RASP 也是基于 Java 代码环境的,攻击者可以利用一些 Gadget 逃离 JVM 环境,甚至攻击 RASP。
RASP的一些问题 #
在真实场景中 RASP 的应用环境比在实验环境中复杂的多,如果想要一个 RASP 真正运行在业务上就需要从甲乙两方的角度考虑问题。
语言环境的通配适用性 #
企业内部的 Web 应用纷繁复杂,有用 Java 写的,有用 Go 写的,还有 PHP、Python 写的,这就需要对不同的语言构建的应用程序都实现相应的防护。
对于甲方来说,购买一个安全防护产品肯定是要能起到通用防护作用的,肯定不会只针对 Java 购进一套 Java RASP 的。
对于乙方来说,每一种语言都有不同的特性,都要用不同的方式构建 RASP,对于开发和安全研究人员来说工作量非常大,就算是 OpenRASP 这样的团队也只是做出了充分支持 Java 和 PHP 的产品。
语言的适配性也是影响 RASP 推广的一个原因。对比传统的 WAF、流量监测等产品,它们不受语言的研制,只关心流量中是否具有威胁的流量,这就减少了变量,从而加强了广泛使用的可能,无论在什么环境中都能快速部署并发挥作用。对于一般的企业来说,购买 WAF 可能比 RASP 更好。
部署的通配适用性 #
由于开发人员所擅长的技能不同或者不同项目组的技能树设定不同,企业内部往往会存在使用各种各样框架实现的代码,而在代码部署上,如果没有一开始就制定严格的规范的话,部署环境也会存在各种各样的问题。比如 Java,企业内部可能存在 Struts2、Spring 等框架,同时这些应用可能部署在不同的中间件上,像 Tomcat、Weblogic、JBoss 等,不同的框架,不同的中间件部署方式都有或多或少的不同,想要实现通配很难。
规则的通用性 #
在 OpenRASP 中是统一使用 js 做规则,然后用 js 引擎解析规则。
自身稳定性 #
因为 RASP 是将检测逻辑插入到 hook 点中的,只要到达相应的 hook 点检测逻辑是一定会被执行的,如果这个时候 RASP 实现的检测逻辑本身出现了问题,严重的话是可能导致整个业务崩溃的,或者直接被打穿。同样,如果在 RASP 所执行的逻辑中出现严重错误,将会直接将错误抛出到业务逻辑中,轻则当前业务中断,重则整个服务中断,这对于甲方来说是非常严重的事故了,甚至比服务器被攻击都要严重。
这也是为什么很多甲方并不喜欢 RASP 这种方式的原因,因为说到底 RASP 还是将代码插入到业务执行流中的,如果不出问题还好,一旦出问题就会影响业务。相对来说,WAF 顶多就是误封,但并不会影响业务,稳定性是有保障的。
自身安全性 #
如果一个 RASP 本身存在一定的漏洞,这是相当可怕的。即使原来的应用是没有明显的安全危险的,但是在 RASP 处理过程中存在漏洞,而正好攻击者传入一个利用这种漏洞的 payload,这将直接在 RASP 处理流中完成触发。
举一个例子,比如 RASP 中使用了受漏洞影响的 Fastjson 库来处理 json 字符串,那么当攻击者在发送 Fastjson 反序列化攻击 payload 的时候就会造成目标系统被 RCE。
像 OpenRASP 在某个版本使用的就是 Fastjson 来处理 json 字符串,而当时的版本就是存在漏洞的版本。所以在后来的 OpenRASP 版本中使用了比较安全的 Gson 来处理 json 字符串。
RASP的处理思路就决定了其与业务是联系非常紧密的,可以说就是业务的“一部分”,所以如果RASP自己的代码不规范不安全,最终将导致直接给业务写了一个漏洞。
规则的稳定性 #
RASP 的规则是需要经过专业的安全研究人员反复打磨并且根据业务来定制化的,需要尽量将所有可能性都考虑进去,同时尽量减少误报。但是由于规则贡献者的水平参差不齐,很容易导致规则遗漏,从而根本无法拦截相关的攻击,或者产生大量的攻击误报。这对于甲方来说肯定是一笔包赔的买卖——花了大量时间部署,花费大量服务器资源来启用 RASP,但是最终的安全效果却还是不尽如人意。
如果想要尽可能完善规则,只能更加贴近业务场景,针对不同情况做不同规则判别。所以说规则和业务场景是分不开的,对于乙方来说不深入开发、不深入客户是很难做好安全产品的,如果只是停留在实验阶段,是永远无法向工程化和产品化转换的。
部署的复杂性 #
理想中最佳的 Java RASP 实践方式是使用agentmain模式进行无侵入部署,但是受限于 JVM 进程保护机制没有办法对目标类添加新的方法,所以就会造成多次 attach 造成的重复字节码插入的问题。目前主流的 Java RASP 推荐部署方式都是利用premain模式进行部署,这就造成了必须停止相关业务,加上相应启动参数,再开启服务这样一个复杂的过程。
对于甲方来说,重启一次业务完成部署RASP的代价是比较高的,所以都是不愿意采取这样的方案的。而且在甲方企业内部存在那么多的服务,一台台部署显然也是不现实的。目前所提出的自动化部署方案也受限于实际业务场景的复杂性,并不稳定。
总结 #
目前来说 RASP 的解决方案已经相对成熟了,除非出现新的 JDK 特性,否则很难出现重大的变革。
目前各家 RASP 厂商主要都是针对性能及其他的辅助功能进行开发和优化,比如 OpenRASP 提出了用 RASP 构建 SIEM 以及实现被动扫描器的思路,这其实是一个非常好的思路,RASP 配合被动扫描器能很方便的对企业内部的资产进行扫描,从而实现一定程度上的漏洞管控。
但是 RASP 不是万能的,并不能高效的防御所有的漏洞,其优劣势是非常明显的,应当正确的理解 RASP 本身的司职联合其他的防御措施构建完整的防御体系才能更好的做好安全防护。