Consulta de registros de AWS WAF - Amazon Athena

Consulta de registros de AWS WAF

AWS WAF es un firewall de aplicaciones web que permite supervisar y controlar las solicitudes HTTP y HTTPS que reciben sus aplicaciones web protegidas de los clientes. Usted define cómo gestionar las solicitudes web configurando las reglas dentro de una lista de control de acceso (ACL) web AWS WAF. A continuación, se protege una aplicación web asociándole una ACL web. Entre los ejemplos de recursos de aplicaciones web que puede proteger con AWS WAF incluyen las distribuciones de Amazon CloudFront, las API de REST de Amazon API Gateway y los equilibradores de carga de aplicación. Para obtener más información acerca de las AWS WAF, consulte AWS WAF en la Guía para desarrolladores de AWS WAF.

Los registros de AWS WAF incluyen información sobre el tráfico que analiza su ACL web, como la hora en que AWS WAF recibió la solicitud de su recurso de AWS, información detallada sobre la solicitud y la acción de la regla con la que coincide cada solicitud.

Puede configurar una web ACL AWS WAF para publicar los registros en uno de varios destinos, donde podrá consultarlos y verlos. Para obtener más información sobre cómo configurar el registro de ACL web y el contenido de los registros AWS WAF, consulte Registrar el tráfico de ACL AWS WAF web en la Guía para desarrolladores de AWS WAF.

Para ver un ejemplo sobre cómo agregar registros AWS WAF en un repositorio de lago de datos central y consultarlos con Athena, consulte la publicación del Blog de macrodatos de AWS Análisis de registros AWS WAF con OpenSearch Service, Amazon Athena y Amazon QuickSight.

Este tema ofrece dos ejemplos de instrucciones CREATE TABLE: una que usa particiones y otra que no.

nota

Las instrucciones CREATE TABLE de este tema se pueden usar tanto para registros AWS WAF v1 como v2. En v1, el campo webaclid contiene un ID. En v2, el campo webaclid contiene un ARN completo. Las instrucciones CREATE TABLE aquí tratan este contenido de forma agnóstica mediante el uso de tipos de datos string.

Creación de una tabla para los registros S3 de AWS WAF en Athena por medio de la proyección de particiones

Dado que los registros de AWS WAF tienen una estructura conocida cuyo esquema de partición puede especificar de antemano, puede reducir el tiempo de ejecución de las consultas y automatizar la administración de particiones mediante la característica de proyección de particiones de Athena. La proyección de particiones agrega de forma automática nuevas particiones a medida que se agregan nuevos datos. Esto hace que no sea necesario agregar particiones manualmente mediante ALTER TABLE ADD PARTITION.

La siguiente instrucción de ejemplo CREATE TABLE utiliza automáticamente la proyección de particiones en registros AWS WAF desde una fecha especificada hasta el presente para cuatro regiones diferentes de AWS. La cláusula PARTITION BY de este ejemplo particiona por región y por fecha, pero puede modificarla según sus requisitos. Modifique los campos según sea necesario para que coincidan con el resultado del registro. En las cláusulas LOCATION y storage.location.template, reemplace los marcadores de posición de bucket y accountID por valores que identifiquen la ubicación del bucket de Amazon S3 de los registros de AWS WAF. Para projection.day.range, reemplace 2021/01/01 por la fecha de inicio que quiere utilizar. Una vez ejecutada la consulta correctamente, puede consultar la tabla. No tiene que ejecutar ALTER TABLE ADD PARTITION para cargar las particiones.

CREATE EXTERNAL TABLE `waf_logs`( `timestamp` bigint, `formatversion` int, `webaclid` string, `terminatingruleid` string, `terminatingruletype` string, `action` string, `terminatingrulematchdetails` array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, `httpsourcename` string, `httpsourceid` string, `rulegrouplist` array < struct < rulegroupid: string, terminatingrule: struct < ruleid: string, action: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > > >, nonterminatingmatchingrules: array < struct < ruleid: string, action: string, overriddenaction: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, challengeresponse: struct < responsecode: string, solvetimestamp: string >, captcharesponse: struct < responsecode: string, solvetimestamp: string > > >, excludedrules: string > >, `ratebasedrulelist` array < struct < ratebasedruleid: string, limitkey: string, maxrateallowed: int > >, `nonterminatingmatchingrules` array < struct < ruleid: string, action: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, challengeresponse: struct < responsecode: string, solvetimestamp: string >, captcharesponse: struct < responsecode: string, solvetimestamp: string > > >, `requestheadersinserted` array < struct < name: string, value: string > >, `responsecodesent` string, `httprequest` struct < clientip: string, country: string, headers: array < struct < name: string, value: string > >, uri: string, args: string, httpversion: string, httpmethod: string, requestid: string >, `labels` array < struct < name: string > >, `captcharesponse` struct < responsecode: string, solvetimestamp: string, failureReason: string >, `challengeresponse` struct < responsecode: string, solvetimestamp: string, failureReason: string >, `ja3Fingerprint` string, `oversizefields` string, `requestbodysize` int, `requestbodysizeinspectedbywaf` int ) PARTITIONED BY ( `region` string, `date` string) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://DOC-EXAMPLE-BUCKET/AWSLogs/accountID/WAFLogs/region/DOC-EXAMPLE-WEBACL/' TBLPROPERTIES( 'projection.enabled' = 'true', 'projection.region.type' = 'enum', 'projection.region.values' = 'us-east-1,us-west-2,eu-central-1,eu-west-1', 'projection.date.type' = 'date', 'projection.date.range' = '2021/01/01,NOW', 'projection.date.format' = 'yyyy/MM/dd', 'projection.date.interval' = '1', 'projection.date.interval.unit' = 'DAYS', 'storage.location.template' = 's3://DOC-EXAMPLE-BUCKET/AWSLogs/accountID/WAFLogs/${region}/DOC-EXAMPLE-WEBACL/${date}/')
nota

El formato de la ruta de la cláusula LOCATION del ejemplo es estándar, pero puede variar en función de la configuración de AWS WAF que haya implementado. Por ejemplo, el siguiente ejemplo de ruta de registros de AWS WAF es para una distribución de CloudFront:

s3://DOC-EXAMPLE-BUCKET/AWSLogs/12345678910/WAFLogs/cloudfront/cloudfronyt/2022/08/08/17/55/

Si tiene problemas al crear o consultar la tabla de registros de AWS WAF, confirme la ubicación de los datos de registro o póngase en contacto con AWS Support.

Para obtener más información sobre la proyección de particiones, consulte Proyección de particiones con Amazon Athena.

Creación de una tabla para registros de AWS WAF sin particionar

En esta sección se describe cómo crear una tabla para registros AWS WAF sin particiones ni proyección de particiones.

nota

Por motivos de rendimiento y costo, no le recomendamos utilizar un esquema no particionado para las consultas. Para obtener información, consulte Los 10 principales consejos de ajuste de rendimiento de Amazon Athena en el Blog de macrodatos de AWS.

Para crear la tabla de AWS WAF

  1. Copie y pegue la siguiente instrucción DDL en la consola de Athena. Modifique los campos según sea necesario para que coincidan con el resultado del registro. Modifique el valor de LOCATION para indicar el bucket de Amazon S3 que se corresponda con el bucket donde se almacenan los registros.

    Esta consulta utiliza El SerDe JSON de OpenX.

    nota

    El SerDe espera que cada documento JSON esté en una sola línea de texto sin caracteres de terminación de línea que separen los campos del registro. Si el texto JSON está en formato de impresión, puede recibir un mensaje de error como HIVE_CURSOR_ERROR: la fila no es un objeto JSON válido o HIVE_CURSOR_ERROR: JsonParseException: fin de entrada inesperado: marcador de cierre esperado para OBJECT cuando intenta consultar la tabla después de crearla. Para obtener más información, consulte los Archivos de datos JSON en la documentación de OpenX SerDE en GitHub.

    CREATE EXTERNAL TABLE `waf_logs`( `timestamp` bigint, `formatversion` int, `webaclid` string, `terminatingruleid` string, `terminatingruletype` string, `action` string, `terminatingrulematchdetails` array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, `httpsourcename` string, `httpsourceid` string, `rulegrouplist` array < struct < rulegroupid: string, terminatingrule: struct < ruleid: string, action: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > > >, nonterminatingmatchingrules: array < struct < ruleid: string, action: string, overriddenaction: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, challengeresponse: struct < responsecode: string, solvetimestamp: string >, captcharesponse: struct < responsecode: string, solvetimestamp: string > > >, excludedrules: string > >, `ratebasedrulelist` array < struct < ratebasedruleid: string, limitkey: string, maxrateallowed: int > >, `nonterminatingmatchingrules` array < struct < ruleid: string, action: string, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < string > > >, challengeresponse: struct < responsecode: string, solvetimestamp: string >, captcharesponse: struct < responsecode: string, solvetimestamp: string > > >, `requestheadersinserted` array < struct < name: string, value: string > >, `responsecodesent` string, `httprequest` struct < clientip: string, country: string, headers: array < struct < name: string, value: string > >, uri: string, args: string, httpversion: string, httpmethod: string, requestid: string >, `labels` array < struct < name: string > >, `captcharesponse` struct < responsecode: string, solvetimestamp: string, failureReason: string >, `challengeresponse` struct < responsecode: string, solvetimestamp: string, failureReason: string >, `ja3Fingerprint` string, `oversizefields` string, `requestbodysize` int, `requestbodysizeinspectedbywaf` int ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://DOC-EXAMPLE-BUCKET/prefix/'
  2. Ejecute la instrucción CREATE EXTERNAL TABLE en el editor de consultas de la consola de Athena. Esto registra la tabla waf_logs y hace que los datos que contiene estén disponibles para las consultas de Athena.

Ejemplos de consultas de registros de AWS WAF

En muchos de los siguientes ejemplos de consultas se utiliza la tabla de proyección de particiones creada anteriormente en este documento. Modifique el nombre de la tabla, los valores de columna y otras variables de los ejemplos según sus requisitos. Para mejorar el rendimiento de las consultas y reducir el costo, agregue la columna de partición en la condición de filtro.

 

 

 

ejemplo
: contar el número de referencias que contienen un término especificado

La siguiente consulta cuenta el número de referencias que contienen el término “amazon” para el intervalo de fechas especificado.

WITH test_dataset AS (SELECT header FROM waf_logs CROSS JOIN UNNEST(httprequest.headers) AS t(header) WHERE "date" >= '2021/03/01' AND "date" < '2021/03/31') SELECT COUNT(*) referer_count FROM test_dataset WHERE LOWER(header.name)='referer' AND header.value LIKE '%amazon%'
ejemplo
: contar todas las direcciones IP coincidentes en los últimos 10 días que coincidieron con las reglas excluidas

En la siguiente consulta se cuenta el número de veces que, en los últimos 10 días, la dirección IP coincidió con la regla excluida del grupo de reglas.

WITH test_dataset AS (SELECT * FROM waf_logs CROSS JOIN UNNEST(rulegrouplist) AS t(allrulegroups)) SELECT COUNT(*) AS count, "httprequest"."clientip", "allrulegroups"."excludedrules", "allrulegroups"."ruleGroupId" FROM test_dataset WHERE allrulegroups.excludedrules IS NOT NULL AND from_unixtime(timestamp/1000) > now() - interval '10' day GROUP BY "httprequest"."clientip", "allrulegroups"."ruleGroupId", "allrulegroups"."excludedrules" ORDER BY count DESC
ejemplo
: agrupar todas las reglas administradas contadas por el número de veces que coinciden

Si estableció las acciones de reglas del grupo de reglas en Contar en su configuración de ACL web antes del 27 de octubre de 2022, AWS WAF guardó las anulaciones en el archivo JSON de ACL web como excludedRules. Ahora, la configuración JSON para anular una regla en Contar se encuentra en la configuración ruleActionOverrides. Para obtener más información, consulte Anulación de acciones en grupos de reglas en la Guía para desarrolladores de AWS WAF. Para extraer las reglas administradas en el modo Contar de la estructura de registro nueva, consulte nonTerminatingMatchingRules en la sección ruleGroupList en lugar del campo excludedRules, como en el siguiente ejemplo.

SELECT count(*) AS count, httpsourceid, httprequest.clientip, t.rulegroupid, t.nonTerminatingMatchingRules FROM "waf_logs" CROSS JOIN UNNEST(rulegrouplist) AS t(t) WHERE action <> 'BLOCK' AND cardinality(t.nonTerminatingMatchingRules) > 0 GROUP BY t.nonTerminatingMatchingRules, action, httpsourceid, httprequest.clientip, t.rulegroupid ORDER BY "count" DESC Limit 50
ejemplo
: agrupar todas las reglas personalizadas contadas por el número de veces que coinciden

En la siguiente consulta se agrupan todas las reglas personalizadas contadas según el número de veces que coinciden.

SELECT count(*) AS count, httpsourceid, httprequest.clientip, t.ruleid, t.action FROM "waf_logs" CROSS JOIN UNNEST(nonterminatingmatchingrules) AS t(t) WHERE action <> 'BLOCK' AND cardinality(nonTerminatingMatchingRules) > 0 GROUP BY t.ruleid, t.action, httpsourceid, httprequest.clientip ORDER BY "count" DESC Limit 50

Para obtener información sobre las ubicaciones de registro de las reglas personalizadas y los grupos de reglas administrados, consulte Supervisión y ajuste en la Guía para desarrolladores de AWS WAF.

Uso de fecha y hora

ejemplo
: devolver el campo de marca de tiempo en formato ISO 8601 legible por humanos

La siguiente consulta utiliza las funciones from_unixtime y to_iso8601 para devolver el campo timestamp en formato ISO 8601 legible por humanos (por ejemplo, 2019-12-13T23:40:12.000Z en lugar de 1576280412771). La consulta devuelve también el nombre de origen HTTP, el ID de origen y la solicitud.

SELECT to_iso8601(from_unixtime(timestamp / 1000)) as time_ISO_8601, httpsourcename, httpsourceid, httprequest FROM waf_logs LIMIT 10;
ejemplo
: devolver registros de las últimas 24 horas

La siguiente consulta utiliza un filtro en el cláusula WHERE para devolver el nombre de origen HTTP, el ID de origen HTTP y los campos de solicitud HTTP para los registros de las últimas 24 horas.

SELECT to_iso8601(from_unixtime(timestamp/1000)) AS time_ISO_8601, httpsourcename, httpsourceid, httprequest FROM waf_logs WHERE from_unixtime(timestamp/1000) > now() - interval '1' day LIMIT 10;
ejemplo
: devolver registros para un intervalo de fechas y una dirección IP especificados

En la siguiente consulta, se enumeran los registros de un intervalo de fechas especificado para una dirección IP de cliente especificada.

SELECT * FROM waf_logs WHERE httprequest.clientip='53.21.198.66' AND "date" >= '2021/03/01' AND "date" < '2021/03/31'
ejemplo
: contar el número de direcciones IP en intervalos de cinco minutos para un intervalo de fechas especificado

La siguiente consulta cuenta el número de direcciones IP en intervalos de cinco minutos para un intervalo de fechas determinado.

WITH test_dataset AS (SELECT format_datetime(from_unixtime((timestamp/1000) - ((minute(from_unixtime(timestamp / 1000))%5) * 60)),'yyyy-MM-dd HH:mm') AS five_minutes_ts, "httprequest"."clientip" FROM waf_logs WHERE "date" >= '2021/03/01' AND "date" < '2021/03/31') SELECT five_minutes_ts,"clientip",count(*) ip_count FROM test_dataset GROUP BY five_minutes_ts,"clientip"
ejemplo
: contar el número de IP X-Forwarded-For en los últimos 10 días

En la siguiente consulta se filtran los encabezados de la solicitud y se cuenta el número de IP de X-Forwarded-For de los últimos 10 días.

WITH test_dataset AS (SELECT header FROM waf_logs CROSS JOIN UNNEST (httprequest.headers) AS t(header) WHERE from_unixtime("timestamp"/1000) > now() - interval '10' DAY) SELECT header.value AS ip, count(*) AS COUNT FROM test_dataset WHERE header.name='X-Forwarded-For' GROUP BY header.value ORDER BY COUNT DESC

Para obtener más información sobre las funciones de fecha y hora, consulte Funciones y operadores de fecha y hora en la documentación de Trino.

Trabajar con solicitudes y direcciones bloqueadas

ejemplo
: extraer las 100 direcciones IP principales bloqueadas por un tipo de regla especificado

La siguiente consulta extrae y cuenta las 100 direcciones IP principales que han sido bloqueadas por la regla de finalización RATE_BASED durante el intervalo de tiempo especificado.

SELECT COUNT(httpRequest.clientIp) as count, httpRequest.clientIp FROM waf_logs WHERE terminatingruletype='RATE_BASED' AND action='BLOCK' and "date" >= '2021/03/01' AND "date" < '2021/03/31' GROUP BY httpRequest.clientIp ORDER BY count DESC LIMIT 100
ejemplo
: contar el número de veces que se bloqueó una solicitud de un país especificado

La siguiente consulta cuenta el número de veces que la solicitud ha llegado de una dirección IP que pertenece a Irlanda (IE) y ha sido bloqueada por la regla de terminación RATE_BASED.

SELECT COUNT(httpRequest.country) as count, httpRequest.country FROM waf_logs WHERE terminatingruletype='RATE_BASED' AND httpRequest.country='IE' GROUP BY httpRequest.country ORDER BY count LIMIT 100;
ejemplo
: contar el número de veces que se bloqueó una solicitud, agrupando por atributos específicos

La siguiente consulta cuenta el número de veces que la solicitud se ha bloqueado, con los resultados agrupados por WebACL, RuleId, ClientIP y URI de la solicitud HTTP.

SELECT COUNT(*) AS count, webaclid, terminatingruleid, httprequest.clientip, httprequest.uri FROM waf_logs WHERE action='BLOCK' GROUP BY webaclid, terminatingruleid, httprequest.clientip, httprequest.uri ORDER BY count DESC LIMIT 100;
ejemplo
: contar el número de veces que se encontraron coincidencias con un ID de regla de terminación específico

La siguiente consulta cuenta el número de veces que se han encontrado coincidencias con un ID de regla de terminación específico (WHERE terminatingruleid='e9dd190d-7a43-4c06-bcea-409613d9506e'). La consulta agrupa después los resultados por WebACL, Action, ClientIP y URI de solicitud HTTP.

SELECT COUNT(*) AS count, webaclid, action, httprequest.clientip, httprequest.uri FROM waf_logs WHERE terminatingruleid='e9dd190d-7a43-4c06-bcea-409613d9506e' GROUP BY webaclid, action, httprequest.clientip, httprequest.uri ORDER BY count DESC LIMIT 100;
ejemplo
: recuperar las 100 direcciones IP principales bloqueadas durante un intervalo de fechas especificado

La siguiente consulta extrae las 100 direcciones IP principales que se han bloqueado durante un intervalo de tiempo especificado. La consulta muestra también el número de veces que se han bloqueado las direcciones IP.

SELECT "httprequest"."clientip", "count"(*) "ipcount", "httprequest"."country" FROM waf_logs WHERE "action" = 'BLOCK' and "date" >= '2021/03/01' AND "date" < '2021/03/31' GROUP BY "httprequest"."clientip", "httprequest"."country" ORDER BY "ipcount" DESC limit 100