Referencia del lenguaje de reglas personalizadas de Google Cloud Armor

Google Cloud Armor te permite definir reglas prioritarias con condiciones y acciones de coincidencia que se pueden configurar en una política de seguridad. Se aplica una regla, lo que significa que se aplica la acción configurada si la regla es la de mayor prioridad en la que los atributos de la solicitud entrante coinciden con los expresados en la condición de coincidencia de la regla.

Hay dos tipos de condiciones de coincidencias.

  • Una condición de coincidencia básica contiene listas de direcciones IP o listas de rangos de direcciones IP.
  • Una condición de coincidencia avanzada contiene una expresión con múltiples subexpresiones para coincidir en función de una variedad de atributos de una solicitud entrante.

El lenguaje de reglas personalizadas se usa a fin de escribir las expresiones en condiciones de coincidencia avanzadas para las reglas de políticas de seguridad. El lenguaje de reglas personalizadas de Google Cloud Armor es una extensión del Common Expression Language (CEL).

Una expresión requiere los siguientes dos componentes:

  • Atributos que se pueden inspeccionar en expresiones de reglas
  • Operaciones que se pueden realizar en los atributos como parte de una expresión

Por ejemplo, en la siguiente expresión, se usan los atributos origin.ip9.9.9.0/24 en la operación inIpRange(). En este caso, la expresión muestra true si origin.ip está dentro del rango de direcciones IP 9.9.9.0/24.

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

Atributos

Los atributos representan información de una solicitud entrante, como la dirección IP de origen o la ruta de URL solicitada.

Campo Tipo Descripción del campo
origin.ip string Es la dirección IP de origen de la solicitud.
request.headers mapa Es un mapa de string a string de los encabezados de solicitud HTTP. Si un encabezado contiene varios valores, el valor de este mapa es una string separada por comas de todos los valores del encabezado. Las claves en este mapa están en minúsculas. Solo se pueden inspeccionar los primeros 16 KB de cada valor de encabezado. Cualquier valor de encabezado superior a 16 KB se trunca según las especificaciones del balanceador de cargas de Google Cloud.
request.method string El método de solicitud HTTP, como GET o POST.
request.path string La ruta de URL HTTP solicitada.
request.scheme string El esquema de URL HTTP, como http o https. Todos los valores de este atributo están en minúsculas.
request.query string Es la consulta de URL HTTP en el formato name1=value&name2=value2, como aparece en la primera línea de la solicitud HTTP. No se realiza ninguna decodificación.
origin.region_code string Es el código de país de Unicode que está asociado a la IP de origen, como US. Si creas una regla o expresión que usa códigos de país o región ISO 3166-1 Alfa 2, Google Cloud Armor trata cada código de forma independiente. Las reglas y expresiones de Google Cloud Armor usan esos códigos de región de forma explícita para permitir o rechazar solicitudes.

Para obtener más información, consulta unicode_region_subtag en el estándar técnico de Unicode.

Operaciones

En la siguiente referencia, se describen los operadores que puedes usar con atributos (representados por xyk) para definir expresiones de reglas.

Expresiones Descripción
x == "foo" El resultado es verdadero si x es igual al literal de string constante determinado.
x == R"fo'o" El resultado es verdadero si x es igual al literal de string sin procesar determinado que no interpreta las secuencias de escape. Los literales de string sin procesar son convenientes para expresar strings que deben usar caracteres de secuencia de escape.
x == y Muestra TRUE si x es igual que y.
x != y Muestra TRUE si x no es igual que y.
x + y El resultado es la string concatenada xy.
x && y El resultado es verdadero si tanto xcomo y son verdaderos.
x || y El resultado es verdadero si x, y, o ambos son verdaderos.
!x El resultado es verdadero si el valor booleano x es falso. En cambio, el resultado es falso si el valor booleano x es verdadero.
x.contains(y) El resultado es verdadero si la string x contiene la substring y.
x.startsWith(y) El resultado es verdadero si la string x comienza con la substring y.
x.endsWith(y) El resultado es verdadero si la string x termina con la substring y.
x.matches(y) El resultado es verdadero si la string x coincide con el patrón RE2 y especificado. El patrón RE2 se compila con la opción RE2::Latin1 que inhabilita las funciones de Unicode.
inIpRange(x, y) Muestra true si la dirección IP x está incluida en el rango de IP y. Las máscaras de subred para las direcciones IPv6 no pueden ser mayores que /64.
x.lower() Muestra el valor de la string x en minúsculas.
x.upper() Muestra el valor de la string x en mayúsculas.
x.base64Decode() Muestra el valor decodificado en Base64 de x. A los caracteres _ - se los reemplaza primero por / + respectivamente. Muestra "" (string vacía) si x no es un valor base64 válido.
has(m['k']) El resultado es verdadero si la clave k está disponible en el mapa m.
m['k'] Muestra el valor de la clave k en el mapa de string a string m si k está disponible; de lo contrario, muestra un error. El enfoque recomendado es verificar primero la disponibilidad mediante "has(m['k'])==true".
int(x) Convierte el resultado de la string de x en un tipo de int. Luego, se puede usar para hacer una comparación de números enteros con operadores aritméticos estándar como > y <=. Esto funciona solo para los valores que se supone que son números enteros.

Expresiones de ejemplo

Para cada una de estas expresiones, la acción realizada depende de si la expresión se incluye en una regla de rechazo o una regla de permiso.

Permite o deniega el acceso según un rango de direcciones IP en IPv4 o IPv6

  • La siguiente expresión coincide con las solicitudes del rango de direcciones IP 9.9.9.0/24:

    inIpRange(origin.ip, '9.9.9.0/24')
    
  • La siguiente expresión coincide con las solicitudes del rango de direcciones IP 2001:db8::/32:

    inIpRange(origin.ip, '2001:db8::/32')
    
  • La siguiente expresión coincide con las solicitudes que tienen una cookie que tiene 80=BLAH.

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

Permite o deniega el tráfico con un encabezado referer que no esté vacío

  • La siguiente expresión coincide con las solicitudes que tienen un encabezado referer que no está vacío.

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

Deniega tráfico desde una región específica

Si tu aplicación web aún no está disponible en la región AU, se deben bloquear todas las solicitudes de esa región.

  • En una regla de denegación, usa la siguiente expresión, que coincide con las solicitudes de la región AU:

    origin.region_code == 'AU'
    

Los códigos de región se basan en los códigos ISO 3166-1 alfa-2. En algunos casos, una región corresponde a un país, pero no siempre es así. Por ejemplo, el código US incluye todos los estados de Estados Unidos, un distrito y seis áreas periféricas.

Expresiones múltiples

Para incluir varias condiciones en una sola regla, combina varias subexpresiones.

  • En el siguiente ejemplo, las solicitudes de 1.2.3.0/24 (como tus verificadores Alfa) en la región AU coinciden con la siguiente expresión:

    origin.region_code == "AU" && inIpRange(origin.ip, '1.2.3.0/24')
    
  • La siguiente expresión coincide con las solicitudes de 1.2.3.4 en las que un usuario-agente contiene la string WordPress:

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

Permite o deniega el tráfico para un URI de solicitud que coincida con una expresión regular

  • La siguiente expresión coincide con las solicitudes que contienen la string bad_path en el URI:

    request.path.matches('/bad_path/')
    
  • La siguiente expresión coincide con las solicitudes que tienen Chrome en el campo de encabezado User-Agent:

    request.headers['user-agent'].matches('Chrome')
    
  • En la siguiente expresión, se muestra la coincidencia que no distingue entre mayúsculas y minúsculas para el encabezado User-Agent que contiene wordpress; coincide con User-Agent:WordPress/605.1.15, User-Agent:wordPress y otras variantes de wordpress:

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

Permite o deniega el tráfico que contiene un valor decodificado en Base64 específico

  • La siguiente expresión coincide con las solicitudes que tienen un valor decodificado en Base64 de myValue para el encabezado user-id:

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

Permitir o denegar el tráfico que no tiene content-length en el cuerpo HTTP

  • La siguiente expresión coincide con las solicitudes que tienen un content-length cero en el cuerpo HTTP:

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

Reglas preconfiguradas

Las reglas preconfiguradas usan firmas estáticas preconfiguradas, expresiones regulares o ambas para coincidir con los encabezados de solicitud HTTP y los parámetros de consulta. Las reglas preconfiguradas disponibles se basan en el conjunto de reglas principales de Modsecurity de OWASP versión 3.0.1. Google Cloud Armor proporciona dos conjuntos de expresiones predefinidos:

  • xss-<version>: Defiende contra ataques de secuencias de comandos entre sitios
  • sqli-<version>: Defiende contra ataques de inyección de SQL

Para obtener una lista de todas las reglas preconfiguradas disponibles, consulta Obtén una lista de las reglas preconfiguradas disponibles.

Para obtener más información sobre las reglas preconfiguradas, consulta el caso práctico Mitiga los ataques de la capa de la aplicación mediante reglas preconfiguradas.

Nombres de conjuntos de expresiones

Los nombres de los conjuntos de expresiones tienen el formato <attack category>-<version field>. La categoría de ataque especifica el tipo de ataques que quieres proteger, como xss (secuencias de comandos entre sitios) o sqli (inyección de SQL).

Los campos de la versión compatibles son stable y canary. Las adiciones y modificaciones a las reglas se publican primero en la versión canary. Cuando las incorporaciones y modificaciones se consideran seguras y estables, se ascienden a la versión de stable.

ID de miembros del conjunto de expresiones

Un conjunto de expresiones contiene varias expresiones, cada una con su propio ID de conjunto de reglas principales (CRS). Por ejemplo, el conjunto de expresiones xss-stable incluye una expresión llamada owasp-crs-v020901-id981136-xss, que corresponde al ID de regla 981136 para la version 2.9.1. Puedes usar los ID de CRS para excluir el uso de expresiones específicas, lo que es útil si una expresión en particular siempre activa un falso positivo. Para obtener más información, consulta la información sobre solución de problemas de falsos positivos.

Para obtener información sobre el conjunto de reglas principales y el ajuste de los diferentes niveles de sensibilidad, consulta Ajusta las reglas de WAF de Google Cloud Armor.

Operador para reglas preconfiguradas

Expresiones Descripción
evaluatePreconfiguredExpr(string, LIST)

El resultado es verdadero si alguna de las expresiones dentro del conjunto de expresiones especificado es verdadera.

El primer argumento es el nombre del conjunto de expresiones, como xss-stable. El segundo argumento (opcional) es una lista de string separada por comas de los ID que se debe excluir de la evaluación. La lista de exclusiones es útil cuando un miembro determinado del conjunto de expresiones activa un falso positivo.

Ejemplos de reglas preconfiguradas

  • En la siguiente expresión, se usa la regla preconfigurada xss-stable para mitigar los ataques de XSS:

    evaluatePreconfiguredExpr('xss-stable')
    
  • En la siguiente expresión, se usan todas las expresiones de la regla preconfigurada xss-stable, excepto los ID de miembro 981136981138:

    evaluatePreconfiguredExpr('xss-stable', ['owasp-crs-v020901-id981136-xss',
    'owasp-crs-v020901-id981138-xss'])
    
  • En la siguiente expresión, se usa una regla preconfigurada para mitigar los ataques de SQLi desde el rango de direcciones IP 198.51.100.0/24:

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

Próximos pasos