CEL-GO 实践 - 基础
0x01:前言
最近的漏扫开发工作当中要用到POC的加载,站在巨人的肩膀上看的更远,在参考nuclei和xray的扫描POC设计时发现都用到了CEL-GO,那就来看看这厮。
什么是CEL
介绍
CEL是一种非图灵完备[^1]的表达式语言,旨在快速、可移植和安全地执行。
CEL被设计为一种可以安全执行用户代码的语言。虽然盲目调用eval()
的代码很危险,但是CEL可以安全的执行,因为CEL阻止了会降低性能和破坏安全性的行为。
CEL计算表达式,类似于单行行数或lambda表达式。虽然CEL通常用于bool判断,但是它也可用于构造更复杂的对象,比如JSON和protobuf[^2]消息。
从路由到RPC到定义安全策略,CEL已应用于各种应用程序。CEL是可拓展的,与应用程序无关。
Compilation
表达式是根据环境进行编译的,编译步骤生成一个protobuf[2]形式的语抽象语法树(AST)[3]编译步骤生成一个protobuf形式的抽象语法树(AST)。编译后的表达式通常会进行存储,以供将来重复使用,尽可能快的进行评估。
可以通过许多不同的输入来评估单个编译过的表达式。
Expressions
用户来定义表达式,应用程序和服务定义表达式的运行环境,函数签名声明输入,并写入CEL表达式之外。CEL可用的函数库是自动导入的。
在下面的示例当中,表达式接受一个请求对象,该请求包含一个声明令牌。表达式返回一个bool
值,指示声明令牌是否有效。
// 通过JSON Web Token中的exp属性来检查 Token是否已经过期。
// 参数:
// claims - 身份验证要求。
// now - 当前系统时间戳。
// Returns: 如果令牌已经过期,返回为true
timestamp(claims["exp"]) < now
Environment
环境是由服务定义的。使用CEL的服务和应用程序会声明表达式环境,环境是可以在表达式中使用的变量和函数的集合。
CEL类型检查器使用基于原型的声明来确保表达式是否被正确声明,并且确保正确使用表达式中的所有标识符和函数引用。
解析表达式的三个阶段
CEL表达式的处理有三个阶段,分别是:解析、检查和求值。CEL最常见的模式是让Control Plane
在配置时解析和检查表达式,并存储AST。
在运行时,Data Plane
检索并且反复评估AST。CEL针对运行时效率进行了优化,但是解析和检查不应该在延迟的关键代码路径中。
CEL使用ANTLR[^4] Lexer/parser语法将CEL从人类可读的表达式解析为抽象语法树。解析阶段发出一个基于proto
的抽象语法树,其中AST中的每个Expr节点都包含一个整数id,用于索引解析和检查期间生成的元数据。解析过程中产生的syntax.proto标识了以表达式的字符串形式输入内容的抽象表示。
一旦解析了表示,就可以对照环境对其进行检查,以确保表达式中的所有变量和函数标识符都已声名并被正确使用。类型检查器生成包含类型、变量和函数解析元数据的checked.proto,这可以极大的提高评估效率。
执行类型检查可以提高解析表达式的速度和安全性,即使对于JSON这种类型推断受限的动态数据也是如此。
CEL evaluator 需要三样东西:
1:任何自定义扩展的函数绑定
2.变量绑定
3.需要评估的AST
函数和变量绑定应该与用于编译AST的内容相匹配。这些输入中的任何一个都可以在多次评估中使用,比如一个AST在多组变量绑定中被评估,或者相同的变量在多个AST中被使用,或者函数绑定在一个流程的整个生命周期中被使用(常见的情况)
注脚
[^1]非图灵完备: 图灵完备是指在可计算性理论里,如果一系列操作数据的规则(如指令集、编程语言、细胞自动机)可以用来模拟单带图灵机。 这个词源于引入图灵机概念的数学家艾伦·图灵。 虽然图灵机会受到储存能力的物理限制,图灵完全性通常指“具有无限存储能力的通用物理机器或编程语言”。
[^2]protobuf: Protobuf全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。
[^3]AST 抽象语法树: 在计算机科学中,抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。 它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
[^4]ANTLR: ANother Tool for Language Recognition是一种语言工具,它提供了一个框架,可以通过包含 Java, C++, 或 C# 动作(action)的语法描述来构造语言识别器,编译器和解释器。 计算机语言的解析已经变成了一种非常普遍的工作。使用 Antlr 等识别工具来识别,解析,构造编译器比手工编程更加容易,同时开发的程序也更易于维护。
设置
项目的引用代码在https://github.com/google/cel-go/tree/master/codelab
当中,克隆并且打开对应的项目工程,在此笔者使用Goland。
在Goland当中,打开codelab/codelab.go
,可以看到Google提供用于练习CEL-GO的main方法,然后是三个辅助方法。
Compile
功能:针对环境解析、检查和输入表达式。Eval
功能:根据输入评估编译的程序。Report
功能:打印评估结果。
此外,还提供了request
和auth
来协助各种练习的输入构造。
练习将通过它们的短包名来引用包,如果你想深入的了解细节,下面是google/cel-go repo中从包到源位置的映射:
| Package | Source Location | Description |
|-----------|-------------------------------------------|-------------|
| cel | cel-go/cel/cel.go | 顶级接口 |
| decls | cel-go/checker/decls/decls.go | 变量和函数声明实用程序 |
| functions | cel-go/interpreter/functions/functions.go | 运行时绑定 |
| ref | cel-go/common/types/ref/reference.go | 参考接口 |