配置自定义规则语言属性

每条 Google Cloud Armor 安全政策规则都有一个优先级、一个匹配条件和一项操作。Google Cloud Armor 执行与请求匹配的最高优先级规则的操作。系统不会评估优先级低于最高优先级匹配规则的规则,即使它们具有相同的匹配条件也是如此。

每个安全政策规则都支持两种类型的匹配条件:

  • 基本匹配条件包含 IP 地址或 IP 地址范围列表。使用 Google Cloud CLI 创建规则时,使用 --src-ip-ranges 标志定义基本匹配条件。
  • 高级匹配条件包含一个表达式,最多可以有五个子表达式,可以匹配传入请求的各种属性。使用 Google Cloud CLI 创建规则时,使用 --expression 标志定义高级匹配条件。

本页面讨论了高级匹配条件以及在安全政策规则的高级匹配条件中编写表达式时使用的 Google Cloud Armor 自定义规则语言。Google Cloud Armor 自定义规则语言是通用表达式语言 (CEL) 的子集。使用 Google Cloud Armor 自定义规则语言编写的表达式需要两个组件:

  • 属性:要检查的数据
  • 操作:如何使用数据

例如,以下表达式在操作 inIpRange() 中使用属性 origin.ip9.9.9.0/24。在这种情况下,如果 origin.ip9.9.9.0/24 IP 地址范围内,则表达式返回 true。

inIpRange(origin.ip, '9.9.9.0/24')

虽然前面的示例表达式仅匹配源 IP 地址,但在 Google Cloud Armor 安全政策规则中使用该示例表达式时,从配额的角度来看,该规则被视为具有高级匹配条件的规则。如需了解详情,请参阅 Google Cloud Armor 配额和限制

特性

属性表示来自传入请求的信息,例如源 IP 地址或请求的网址路径。

字段 类型 字段说明
origin.ip 字符串 请求的源 IP 地址。
origin.user_ip 字符串 源客户端的 IP 地址,由上游代理添加到 HTTP-HEADER 中。在使用此属性之前,您必须在安全政策的 advancedOptionsConfig 字段中配置 userIpRequestHeaders[] 选项,以匹配 True-Client-IPX-Forwarded-ForX-Real-IP 等来源。

如果未配置 userIpRequestHeaders[] 选项、配置的标头包含无效的 IP 地址值,或者配置的标头不存在,则 origin.user_ip 默认为 origin.ip。如需了解详情,请参阅 securityPolicy 资源参考文档。

origin.tls_ja3_fingerprint 字符串 JA3 TLS/SSL 指纹(如果客户端使用 HTTPSHTTP/2HTTP/3 进行连接)。如果不可用,则返回空字符串。
request.headers 地图 HTTP 请求标头的字符串到字符串映射。如果标头包含多个值,则此映射中的值将是标头所有值的英文逗号分隔字符串。此映射中的键全部为小写。系统会检查外部应用负载均衡器接受的所有标头,并应用相同的标头限制
request.method 字符串 HTTP 请求方法,例如 GETPOST
request.path 字符串 所请求的 HTTP 网址路径。
request.scheme 字符串 HTTP 网址架构,例如 httphttps。此属性的值均为小写
request.query 字符串 HTTP 网址查询,格式为 name1=value&name2=value2,显示在 HTTP 请求的第一行中。未执行解码。
origin.region_code 字符串 与原始 IP 相关联的 Unicode 国家/地区代码,例如 US。如果您要创建使用 ISO 3166-1 alpha 2 国家或地区代码的规则或表达式,请注意 Google Cloud Armor 会独立处理每个代码。Google Cloud Armor 规则和表达式显式使用这些地区代码来允许或拒绝请求。

如需了解详情,请参阅 Unicode 技术标准中的 unicode_region_subtag

origin.asn 整数 与源 IP 地址关联的自治系统编号 (ASN)。全局唯一 ASN 根据支持包含源 IP 地址的 IP 地址前缀的网络运营商确定。

reCAPTCHA Enterprise 特性

本部分列出了仅适用于 reCAPTCHA Enterprise 令牌或豁免 Cookie 的特性。如果要评估的 reCAPTCHA Enterprise 令牌或豁免 Cookie 因以下某种原因而不可用或无效,则基于这些属性的子表达式会返回 false

  • 该令牌格式错误,无法解码。
  • 该令牌包含无效属性。例如,令牌是使用与规则的关联 reCAPTCHA 密钥不匹配的 reCAPTCHA 密钥生成的。
  • 此令牌已过期。
字段 类型 字段说明
token.recaptcha_exemption.valid bool 是否存在有效的 reCAPTCHA 豁免 Cookie。

操作令牌特性

字段 类型 字段说明
token.recaptcha_action.score float reCAPTCHA 操作令牌中的分数。有效分数的范围为 0.01.0,其中 0.0 表示很可能是非法用户,1.0 表示很可能是合法用户。
token.recaptcha_action.captcha_status string reCAPTCHA 操作令牌中的人机识别系统状态。有效状态为 NONEPASSFAIL,其中 NONE 表示 reCAPTCHA 评估期间未涉及任何验证,因此操作令牌中缺少人机识别系统字段。
token.recaptcha_action.action string reCAPTCHA 操作令牌中的操作名称(最多 100 个字符)。请参阅操作名称
token.recaptcha_action.valid bool 是否存在有效的 reCAPTCHA 操作令牌。

会话令牌特性

字段 类型 字段说明
token.recaptcha_session.score float reCAPTCHA 会话令牌的分数。有效分数的范围为 0.01.0,其中 0.0 表示很可能是非法用户,1.0 表示很可能是合法用户。
token.recaptcha_session.valid bool 是否存在有效的 reCAPTCHA 会话令牌。

运维

以下参考信息描述了可与属性(由 xyk 表示)搭配使用来定义规则表达式的运算符。

表达式 说明
x == "foo" 如果 x 等于指定常量字符串字面量,则返回 true。x
x == R"fo'o" 如果 x 等于不解释转义序列的指定原始字符串字面量,则返回 true。x原始字符串字面量便于表示自己必须使用转义序列字符的字符串。
x == y 如果 x 等于 y,则返回 true。xx
x != y 如果 x 不等于 y,则返回 true。xx
x + y 返回串联的字符串 xy。
x && y 如果 x 和 y 均为 true,则返回 true。xx
x || y 如果 x、y 或两者均为 true,则返回 true。xx
!x 如果布尔值 x 为 false,则返回 true;如果布尔值 x 为 true,则返回 false。xx
x.contains(y) 如果字符串 x 包含子字符串 y,则返回 true。xx
x.startsWith(y) 如果字符串 x 以子字符串 y 开头,则返回 true。xx
x.endsWith(y) 如果字符串 x 以子字符串 y 结尾,则返回 true。xx
x.matches(y) 如果字符串 x 与指定的 RE2 模式 y 部分匹配,则返回 true。RE2 模式是使用停用 Unicode 功能的 RE2::Latin1 选项编译的。
inIpRange(x, y) 如果 IP 地址 x 包含在 IP 范围 y 内,则返回 true。xxIPv6 地址的子网掩码不能大于 /64。
x.lower() 返回字符串 x 的小写值。x
x.upper() 返回字符串 x 的大写值。x
x.base64Decode() 返回 x 的 base64 解码值;字符 _ - 首先被相应替换为 / +。如果 x 不是有效的 base64 值,则返回 ""(空字符串)。
has(m['k']) 如果键 'k' 在映射 'm' 中可用,则返回 true。
m['k'] 如果 'k' 可用,则返回字符串到字符串映射 m 中的键 k 处的值,否则返回错误。建议先使用 "has(m['k'])==true" 检查可用性。
int(x) 将字符串结果 x 转换为 int 类型。x接着可以将其用于使用标准算术运算符(例 > 和 <= 等)进行整数比较。这仅适用于应该为整数的值。
size(x) 返回字符串 x 的长度。x
x.urlDecode() 返回 x 的网址解码值;%## 格式的字符序列会被替换为非 ASCII 等效字符,而 + 则被替换为空格。无效编码会按原样返回。
x.urlDecodeUni() 返回 x 的网址解码值;除了 urlDecode() 之外,还会处理 %u### 格式的 Unicode 字符序列。无效编码会按原样返回。
x.utf8ToUnicode() 返回 UTF-8 编码 x小写 Unicode 表示形式。

表达式示例

对于这些表达式中的每一个,所采取的操作取决于该表达式是包含在拒绝规则还是允许规则中。

根据 IPv4 或 IPv6 的 IP 地址范围允许或拒绝访问

  • 以下表达式与来自 198.51.100.0/24 IP 地址范围的请求匹配:

    inIpRange(origin.ip, '198.51.100.0/24')
    
  • 以下表达式与来自 2001:db8::/32 IP 地址范围的请求匹配:

    inIpRange(origin.ip, '2001:db8::/32')
    

根据上游代理后面的自定义客户端 IP 地址范围允许或拒绝访问

如果您已配置 origin.user_ip 运算符,则可以根据在 advancedOptionsConfig.userIpRequestHeaders[] 字段中指定的标头值进行匹配。

  • 以下表达式与源自 192.0.2.0/24 IP 地址范围的请求匹配:

    inIpRange(origin.user_ip, '192.0.2.0/24')
    
  • 以下表达式与源自 2001:db8::/32 IP 地址范围的请求匹配:

    inIpRange(origin.user_ip, '2001:db8::/32')
    
  • 以下表达式与具有包含 80=BLAH 的 Cookie 的请求匹配:

    has(request.headers['cookie']) && request.headers['cookie'].contains('80=BLAH')
    

使用非空 referer 标头允许或拒绝流量

  • 以下表达式与具有非空 referer 标头的请求匹配:

    has(request.headers['referer']) && request.headers['referer'] != ""
    

根据标头中的主机网址允许或拒绝流量

  • 以下表达式与对特定网址的请求匹配:

    request.headers['host'].lower().contains('test.example.com')
    

允许或拒绝来自特定区域的流量

如果您的 Web 应用在 AU 区域尚不可用,则必须阻止来自该区域的所有请求。

  • 在拒绝规则中,使用以下表达式,该表达式与来自 AU 区域的请求匹配:

    origin.region_code == 'AU'
    

或者,如果 Web 应用仅AU 区域中可用,则来自所有其他区域的请求必须被阻止。

  • 在拒绝规则中,使用以下表达式,该表达式匹配来自 AU 区域以外的所有区域的请求:

    origin.region_code != 'AU'
    

地区代码基于 ISO 3166-1 alpha 2 代码。在某些情况下,一个地区对应一个国家/地区,但并非总是如此。例如,US 代码包括美国各州、一个特区和六个美国本土外地区。

允许或拒绝来自特定 ASN 的流量

如果您的 Web 应用需要阻止由特定网络运营商提供服务的客户,则可以使用网络运营商的 ASN 编号进行阻止。

  • 在拒绝规则中,使用以下表达式,该表达式匹配来自特定 ASN 的请求:

    origin.asn == 123
    

或者,如果您的 Web 应用仅可供特定网络运营商的用户使用,则必须阻止来自所有其他网络运营商的请求。

  • 在拒绝规则中,使用以下表达式,该表达式匹配除您有兴趣允许的网络运营商之外的所有其他网络运营商:

    origin.asn != 123
    

多个表达式

如需在单条规则中添加多个条件,请组合多个子表达式。

  • 在以下示例中,来自 AU 地区中的 1.2.3.0/24(例如您的 Alpha 版测试人员)的请求与以下表达式匹配:

    origin.region_code == "AU" && inIpRange(origin.ip, '1.2.3.0/24')
    
  • 以下表达式与来自 1.2.3.4 的请求匹配,其中用户代理包含字符串 WordPress

    inIpRange(origin.ip, '1.2.3.4/32') &&
    has(request.headers['user-agent']) && request.headers['user-agent'].contains('WordPress')
    

允许或拒绝与正则表达式匹配的请求 URI 的流量

  • 以下表达式与 URI 中包含字符串 bad_path 的请求匹配:

    request.path.matches('/bad_path/')
    
  • 以下表达式与 User-Agent 标头字段中包含 Chrome 的请求匹配:

    request.headers['user-agent'].matches('Chrome')
    
  • 以下表达式显示了包含 wordpressUser-Agent 标头的不区分大小写的匹配;它与 User-Agent:WordPress/605.1.15User-Agent:wordPress 以及 wordpress 的其他变体匹配:

    request.headers['user-agent'].matches('(?i:wordpress)')
    

允许或拒绝包含特定 base64 解码值的流量

  • 以下表达式与 user-id 标头的 base64 解码值为 myValue 的请求匹配:

    has(request.headers['user-id']) && request.headers['user-id'].base64Decode().contains('myValue')
    

允许或拒绝包含特定长度的字符串值的流量

  • 以下表达式与网址长度超过 10 个字符的请求匹配:

    size(request.path) < 10
    
  • 以下表达式与标头 x-data 长度大于或等于 1024 个字符的请求匹配:

    size(request.headers['x-data']) >= 1024
    

允许或拒绝 HTTP 正文中 content-length 为零的流量

  • 以下表达式与 HTTP 正文中 content-length 为零的请求匹配:

    int(request.headers["content-length"]) == 0
    

允许或拒绝包含特定网址编码值的流量

  • 以下表达式与 Cookie 值包含 %3c 的请求匹配:

    has(request.headers['cookie']) && request.headers['cookie'].urlDecode().contains('<')
    

允许或拒绝包含 Unicode 字符串的特定网址编码值的流量

  • 以下表达式与 Cookie 值等于 Match%2BValueMatch%u002BValue 的请求匹配:

    has(request.headers['cookie']) && request.headers['cookie'].urlDecodeUni() == 'Match+Value'
    

允许或拒绝包含 UTF-8 文本的特定 Unicode 字符串的流量

  • 以下表达式与 Cookie 值等于 ¬ 的请求匹配:

    has(request.headers['cookie']) && request.headers['cookie'].utf8ToUnicode() == '%u00ac'
    

根据已知的 JA3 指纹允许或拒绝流量

  • 以下表达式与 JA3 指纹等于 e7d705a3286e19ea42f587b344ee6865 的请求匹配:

    origin.tls_ja3_fingerprint == 'e7d705a3286e19ea42f587b344ee6865'
    

根据 JA3 指纹列表允许或拒绝流量

  • 以下表达式与具有等于以下任一 JA3 指纹的 JA3 指纹的请求匹配:

    • e7d705a3286e19ea42f587b344ee6865
    • f8a5929f8949e846267b582072e35f84
    • 8f8b62163873a62234c14f15e7b88340
    origin.tls_ja3_fingerprint == 'e7d705a3286e19ea42f587b344ee6865' || origin.tls_ja3_fingerprint == 'f8a5929f8949e846267b582072e35f84' || origin.tls_ja3_fingerprint == '8f8b62163873a62234c14f15e7b88340'
    

预配置的 WAF 规则

预配置的 WAF 规则使用预配置的静态签名、正则表达式或两者,以匹配 HTTP POST 正文、HTTP 请求标头和查询参数。可用的预配置 WAF 规则基于 OWASP Modsecurity 核心规则集 3.3 版。Google Cloud Armor 提供了多种预定义的预配置 WAF 规则。如需查看预配置的 WAF 规则的完整列表,请参阅 Google Cloud Armor 的预配置 WAF 规则概览

如需列出所有可用的预配置 WAF 规则,请参阅列出可用的预配置 WAF 规则

如需详细了解预配置的 WAF 规则,请参阅使用场景使用预配置的 WAF 规则缓解应用层攻击

预配置的 WAF 规则名称

预配置的 WAF 规则名称的格式为 <attack category>-<ModSecurity CRS version>-<version field>。攻击类别指定要防御的攻击类型,例如 xss(跨站脚本攻击)或 sqli(SQL 注入攻击)。

支持的版本字段为 stablecanary。对规则的添加和修改首先在 canary 版本中发布。在添加和修改被认为是安全稳定时,则会将它们升级为 stable 版本。

预配置的 WAF 规则成员 ID

预配置的 WAF 规则包含多个表达式,每个表达式都有自己的签名。例如,预配置的 WAF 规则 xss-v33-stable 包含一个名为 owasp-crs-v030301-id941100-xss 的表达式,它对应于版本 3.3 的规则 ID id941100。您可以使用签名来排除使用特定的表达式,如果特定的表达式始终触发假正例,这将非常有用。如需了解详情,请参阅假正例问题排查信息。

如需了解核心规则集以及在不同敏感度级层下的调整,请参阅调整 Google Cloud Armor WAF 规则

预配置的 WAF 规则的运算符

表达式 说明
evaluatePreconfiguredWaf(string, MAP<string, dyn>) 如果指定 WAF 规则集中的任一 WAF 签名返回 true,则返回 true。第一个参数是 WAF 规则集的名称,例如 xss-v33-stable。第二个参数(可选)是一个映射,其中键是一个字符串,值是根据键动态输入的。此参数的目的是微调所评估的 WAF 签名。接受的键包括:
  • “sensitives”:此键对应于 ModSecurity 核心规则集偏执级别,该级别共有 4 种,范围从 1 到 4。其值为整数,有效范围为 0 到 4。请注意,0 与“opt_in_rule_ids”(稍后介绍)一起使用时会预留为有效值。如果指定敏感度 x (x >= 1),则评估敏感度值介于 1 到 x 内的所有关联的 WAF 签名。如果省略,则使用 4 作为敏感度值。
  • “opt_out_rule_ids”:要退出评估的 WAF 签名(由规则 ID 表示),其中基本集由敏感度值决定。其值是一个字符串列表。允许的规则 ID 数量上限为 128
  • “opt_in_rule_ids”:要评估的 WAF 签名(由规则 ID 表示),其中基本集为空。其值是一个字符串列表。允许的规则 ID 数量上限为 128。使用此键时,必须指定 0“敏感度”。

键“opt_out_rule_ids”和“opt_in_rule_ids”相互排斥。如果您想查看并手动选择启用后续添加到现有规则集的新 WAF 签名,可以选择使用“opt_in_rule_ids”。

evaluatePreconfiguredExpr(string, LIST)

如果指定的预配置 WAF 规则中的任何一个表达式返回 true,则返回 true。

第一个参数是预配置的 WAF 规则的名称,例如 xss-stable。第二个参数(可选)是应从评估中排除的 ID 的英文逗号分隔字符串列表。当预配置的 WAF 规则的给定成员触发假正例时,排除列表很有用。

预配置的 WAF 规则示例

  • 以下表达式使用 xss-v33-stable 预配置的 WAF 规则来缓解 XSS 攻击:

    evaluatePreconfiguredExpr('xss-v33-stable')
    
  • 以下表达式使用 xss-v33-stable 预配置的 WAF 规则中的所有表达式,但成员 ID 941100941110 除外:

    evaluatePreconfiguredExpr('xss-v33-stable', ['owasp-crs-v030301-id941100-xss',
    'owasp-crs-v030301-id941110-xss'])
    
  • 以下表达式使用预配置的 WAF 规则来缓解来自 198.51.100.0/24 IP 地址范围的 SQLi 攻击:

    inIpRange(origin.ip, '198.51.100.0/24') && evaluatePreconfiguredExpr('sqli-v33-stable')
    

后续步骤