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 whose conditions match the attributes of the incoming request.
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 subexpressions 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 within 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 16 KB of each header value is available for inspection. Any header value over 16 KB is truncated per Google Cloud load balancer specifications. |
request.method |
string | The HTTP request method, such as GET or POST . |
request.path |
string | The requested HTTP URL path. |
request.scheme |
string | The HTTP URL scheme such as http or 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, Google Cloud Armor treats each code
independently. Google Cloud Armor rules and expressions explicitly
use those region codes to allow or deny requests.
For more information, see unicode_region_subtag in the Unicode Technical Standard. |
origin.asn |
integer | The autonomous system number (ASN) that is associated with the origin IP address. The globally unique ASN is determined based on the network operator supporting the IP address prefixes that contain the origin IP address. |
reCAPTCHA Enterprise attributes
This section lists attributes that are only applicable to
reCAPTCHA Enterprise tokens or exemption cookies. A subexpression
based on these attributes returns false
if the reCAPTCHA token or exemption
cookie to be evaluated is not available or is invalid (malformed, mismatched,
or expired).
Exemption cookie attributes
Field | Type | Field description |
---|---|---|
token.recaptcha_exemption.valid |
bool |
The presence of a valid reCAPTCHA exemption cookie. |
Action-token attributes
Field | Type | Field description |
---|---|---|
token.recaptcha_action.score |
float |
The score from a reCAPTCHA action-token. A valid score ranges from
0.0 to 1.0 , with 0.0 being very
likely an illegitimate user, and 1.0 being very likely a
legitimate user. |
token.recaptcha_action.captcha_status |
string |
The captcha status from a reCAPTCHA action-token. A valid status is
NONE , PASS , or FAIL , where
NONE refers to when there are no challenges involved during
reCAPTCHA assessment, such that the captcha field is missing in the
action-token. |
token.recaptcha_action.action |
string |
The action name (up to 100 characters) from a reCAPTCHA action-token. See Action names. |
token.recaptcha_action.valid |
bool |
The presence of a valid reCAPTCHA action-token. |
Session-token attributes
Field | Type | Field description |
---|---|---|
token.recaptcha_session.score |
float |
The score from a reCAPTCHA session-token. A valid score ranges from
0.0 to 1.0 , with 0.0 being very
likely an illegitimate user, and 1.0 being very likely a
legitimate user. |
token.recaptcha_session.valid |
bool |
The presence of a valid reCAPTCHA session-token. |
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 address x is contained within the IP range y. Subnet masks for IPv6 addresses cannot be larger than /64. |
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 availability by 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 by using standard arithmetic
operators such as > and <=. 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 rule 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::/32
IP address range:inIpRange(origin.ip, '2001:db8::/32')
Allow or deny traffic with a specific cookie
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'] != ""
Allow or deny traffic based on host URL in header
The following expression matches with requests to a specific URL:
request.headers['host'].lower().contains('test.example.com')
Allow or deny traffic from a specific region
If your web application isn't available in the AU
region, then all
requests from that region must be blocked.
In a deny rule, use the following expression, which matches requests from the
AU
region:origin.region_code == 'AU'
Alternatively, if your web application is only available in the AU
region,
then requests from all other regions must be blocked.
In a deny rule, use the following expression, which matches requests from all regions other than the
AU
region: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.
Allow or deny traffic from a specific ASN
If your web application needs to be blocked to customers serviced by a specific network operator, you can use the ASN number of the network operator to block.
In a deny rule, use the following expression, which matches requests from a specific ASN:
origin.asn == 123
Alternatively, if your web application is to be only available to customers behind a specific network operator, then requests from all other network operators must be blocked.
In a deny rule, use the following expression, which matches all other network operators other than the one that you are interested in allowing.
origin.asn != 123
Multiple expressions
To include multiple conditions in a single rule, combine multiple subexpressions.
In the following example, requests from
1.2.3.0/24
(such as your alpha testers) in theAU
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 stringWordPress
: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 theUser-Agent
header field:request.headers['user-agent'].matches('Chrome')
The following expression shows case-insensitive matching for the
User-Agent
header containingwordpress
; it matches withUser-Agent:WordPress/605.1.15
,User-Agent:wordPress
, and other variations ofwordpress
: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 theuser-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.2. Google Cloud Armor provides these predefined expression sets:
xss-<version>
: defends against cross-site scripting attackssqli-<version>
: defends against SQL injection attackslfi-<version>
: defends against local file inclusion attacksrfi-<version>
: defends against remote file inclusion attacksrce-<version>
: defends against remote code execution 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 Mitigate application layer attacks by using preconfigured rules.
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 as 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 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 the expression set, such as
|
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 IDs981136
and981138
: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
- Configure security policies, rules, and expressions
- Tune web application firewall (WAF) rules
- Troubleshoot issues