Google Cloud Armor custom rules language reference

Google Cloud Armor enables you to define prioritized rules with configurable match conditions and actions in a security policy. A rule takes effect, meaning that the configured action is applied, if the rule is the highest priority rule where the incoming request's attributes match those expressed in the rule's match condition.

There are two kinds of match conditions.

  • A basic match condition contains lists of IP addresses or lists of IP address ranges.
  • An advanced match condition contains an expression with multiple sub-expressions to match on a variety of attributes of an incoming request.

The custom rules language is used to write the expressions in advanced match conditions for security policy rules. The Google Cloud Armor custom rules language is an extension of the Common Expression Language (CEL).

An expression requires two components:

  • Attributes that can be inspected in rule expressions.
  • Operations that can be performed on the attributes as part of an expression.

For example, the following expression uses the attributes origin.ip and 9.9.9.0/24 in the operation inIpRange(). In this case, the expression returns true if origin.ip is with the 9.9.9.0/24 IP address range.

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

Attributes

Attributes represent information from an incoming request, such as the origin IP address or the requested URL path.

Field Type Field description
origin.ip string The source IP address of the request.
request.headers map A string to string map of the HTTP request headers. If a header contains multiple values, the value in this map would be a comma-separated string of all of the values of the header. The keys in this map are all lowercase. Only the first 16kb of each header value is available for inspection. Any header value over 16kb is truncated per GCLB specifications.
request.method string The HTTP request method, such as GET, POST.
request.path string The requested HTTP URL path.
request.scheme string The HTTP URL scheme such as http, https. Values for this attribute are all lowercase.
request.query string The HTTP URL query in the format of name1=value&name2=value2, as it appears in the first line of the HTTP request. No decoding is performed.
origin.region_code string The Unicode country code that is associated with the origin IP, such as US. If you are creating a rule or expression that uses ISO 3166-1 alpha 2 country or region codes, note that Google Cloud Armor treats each code independently. Google Cloud Armor rules and expressions explicitly uses those region codes to allow or deny requests. For more information, see unicode_region_subtag in the Unicode Technical Standard.

Operations

The following reference describes the operators that you can use with attributes (represented by x, y, and k) to define rule expressions.

Expressions Description
x == "foo" Returns true if x is equal to the given constant string literal.
x == R"fo'o" Returns true if x is equal to the given raw string literal that does not interpret escape sequences. Raw string literals are convenient for expressing strings that themselves must use escape sequence characters.
x == y Returns true if x is equal to y.
x != y Returns true if x is not equal to y.
x + y Returns the concatenated string xy.
x && y Returns true if both x and y are true.
x || y Returns true if x, y, or both are true.
!x Returns true if the Boolean value x is false or returns false if the Boolean value x is true.
x.contains(y) Returns true if the string x contains the substring y.
x.startsWith(y) Returns true if the string x begins with the substring y.
x.endsWith(y) Returns true if the string x ends with the substring y.
x.matches(y) Returns true if the string x matches the specified RE2 pattern y. The RE2 pattern is compiled by using the RE2::Latin1 option that disables Unicode features.
inIpRange(x, y) Returns true if the IP range x contains the IP address y.
x.lower() Returns the lowercase value of the string x
x.upper() Returns the uppercase value of the string x
x.base64Decode() Returns the base64 decoded value of x; the characters _ - are first replaced with / + respectively. Returns "" (empty string) if x is not a valid base64 value.
has(m['k']) Returns true if key 'k' is available in the map 'm'
m['k'] Returns the value at key k in the string-to-string map m if 'k' is available, otherwise returns an error. Recommended approach is to first check for the availability using "has(m['k'])==true".
int(x) Converts the string result of 'x' to an 'int' type. It can then be used to do an integer comparison using standard arithmetic operators such as '>', '<=' etc. This works only for values that are supposed to be integers.

Example expressions

For each of these expressions, the action taken depends on whether the expression is included in a deny or an allow rule.

Allow or deny access based on an IP address range in IPv4 or IPv6

The following expression matches with requests from the 9.9.9.0/24 IP address range:

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

The following expression matches with requests from the 2001:db8::1 IP address:

inIpRange(origin.ip, '2001:db8::1')

The following expression matches with requests that have a cookie containing 80=BLAH.

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

Allow or deny traffic with a non-empty referer header

The following expression matches with requests that have a non-empty referer header.

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

Deny traffic from a specific region

If your web application isn't available in the AU region yet, all requests from that region must be blocked. Use the following expression, which matches with requests from the AU region, in a deny rule:

origin.region_code == 'AU'

The region codes are based on the ISO 3166-1 alpha 2 codes. In some cases, a region corresponds to a country, but this is not always the case. For example, the US code includes all states of the United States, one district, and six outlying areas.

Multiple expressions

To include multiple conditions in a single rule, combine multiple sub-expressions. In the following example, requests from 1.2.3.0/24 (such as your alpha testers) in the AU region match the following expression:

origin.region_code == "AU" && inIpRange(origin.ip, '1.2.3.0/24')

The following expression matches requests from 1.2.3.4 where a user agent contains the string WordPress:

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

Allow or deny traffic for a request URI that matches a regular expression

The following expression matches with requests that contain the string bad_path in the URI:

request.path.matches('/bad_path/')

The following expression matches with requests that have Chrome in the User-Agent header field:

request.headers['user-agent'].matches('Chrome')

The following expression shows case-insensitive matching for User-Agent header containing wordpress. It matches with User-Agent:WordPress/605.1.15, User-Agent:wordPress, and other variations of wordpress.

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

Allow or deny traffic that contains a specific Base64 decoded value

The following expression matches with requests that have a Base64 decoded value of myValue for the user-id header:

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

Allow or deny traffic that has zero Content-Length in the HTTP body.

The following expression matches requests that have a zero Content-Length in the HTTP body.

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

Preconfigured rules

Preconfigured rules use preconfigured static signatures, regular expressions, or both to match on HTTP request headers and query parameters. The available preconfigured rules are based on the OWASP Modsecurity core rule set version 3.0.1. Cloud Armor provides two predefined expression sets: xss-<version>, to defend against cross-site scripting attacks, and sqli-<version>, to defend against SQL injection attacks. To list all of the available preconfigured rules, see Listing the available preconfigured rules.

For more information about preconfigured rules, see the use case for preconfigured rules in the Google Cloud Armor security policy concepts document.

Expression set names

Expression set names have the format <attack category>-<version field>. The attack category specifies the type of attacks that you want to protect against, such xss (cross-site scripting) or sqli (SQL injection).

The supported version fields are 'stable' and 'canary'. Additions and modifications to the rules are released in the 'canary' version first. When additions and modifications are considered safe and stable, they are promoted to the 'stable' version.

Expression set member IDs

An expression set contains several expressions, each with its own core rule set (CRS) ID. For example, the expression set xss-stable includes an expression called owasp-crs-v020901-id981136-xss, which corresponds to rule ID 981136 for version 2.9.1. You can use the CRS IDs to exclude specific expressions from being used, which is useful if a particular expression consistently triggers a false positive. For more information, see the false positives troubleshooting information.

For full information about the core rule set and tuning at different sensitivity levels, see Tuning Google Cloud Armor WAF rules.

Operator for preconfigured rules

Expressions Description
evaluatePreconfiguredExpr(string, LIST) Returns true if any one of the expressions inside the specified expression set returns true.
The first argument is the name of expression set, such as 'xss-stable'. The second argument (optional) is a comma separated string list of IDs that should be excluded from evaluation. The exclusion list is useful when a given member of the expression set triggers a false positive.

Preconfigured rule examples

The following expression uses the xss-stable preconfigured rule to mitigate XSS attacks:

evaluatePreconfiguredExpr('xss-stable')

The following expression uses all the expressions from the xss-stable preconfigured rule except for member IDs 981136 and 981138:

evaluatePreconfiguredExpr('xss-stable', ['owasp-crs-v020901-id981136-xss',
'owasp-crs-v020901-id981138-xss'])

The following expression uses a preconfigured rule to mitigate SQLi attacks from the 198.51.100.0/24 IP address range:

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

What's next