Consulta de registros 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 habilitar el registro de acceso para los registros de AWS WAF y guardarlos Amazon S3. Anote el bucket de Amazon S3 en el que guarda estos registros; puede crear una tabla de Athena para ellos y consultarlos en Athena.
Para obtener más información sobre cómo habilitar los registros de AWS WAF y sobre la estructura de registros log, consulte Logging and monitoring web ACL traffic (Registro y supervisión de tráfico de la ACL web) en la Guía para desarrolladores de AWS WAF.
Para obtener más información sobre los campos de registro AWS WAF individuales, consulte Campos de registro en la Guía para desarrolladores de AWS WAF.
Para ver un ejemplo sobre cómo agregar registros de AWS WAF en un repositorio de lago de datos central y consultarlos con Athena, consulte la publicación del blog de macrodatos de AWS Analyzing AWS WAF logs with OpenSearch Service, Amazon Athena, and Amazon QuickSight
Este tema ofrece dos ejemplos de instrucciones CREATE TABLE
: una que usa particiones y otra que no.
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
.
Temas
Creación de la tabla para los registros de AWS WAF en Athena por medio de la proyección de particiones
Dado que los registros 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 función 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, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < 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 > > >, 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 > ) 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://
bucket
/AWSLogs/accountID
/WAFLogs/region
/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}/webACL
/${date}/')
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.
Crear la tabla de AWS WAF
-
Copie y pegue la instrucción DDL siguiente 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: Row is not a valid JSON Object
(HIVE_CURSOR_ERROR: la fila no es un objeto JSON válido) oHIVE_CURSOR_ERROR: JsonParseException: Unexpected end-of-input: expected close marker for OBJECT
(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 JSONen 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, rulematchdetails: array < struct < conditiontype: string, sensitivitylevel: string, location: string, matcheddata: array < 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 > > >, 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 > ) 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
/' -
Ejecute la instrucción
CREATE EXTERNAL TABLE
en el editor de consultas de la consola de Athena. Esto registra la tablawaf_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 el ejemplo siguiente se consultan 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.
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%'
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
Uso de fecha y hora
Devolver el campo de marca de hora 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;
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;
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'
Para un intervalo de fechas especificado, cuente el número de direcciones IP en intervalos de cinco minutos
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"
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 Date and time functions and operators
Trabajar con solicitudes y direcciones bloqueadas
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
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;
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;
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;
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