Prácticas recomendadas para la consulta de búsqueda avanzada
Se aplica a:
- Microsoft Defender XDR
Aplique estas recomendaciones para obtener resultados más rápido y evitar tiempos de espera al ejecutar consultas complejas. Para obtener más información sobre cómo mejorar el rendimiento de las consultas, vea procedimientos recomendados de consulta de Kusto.
Descripción de las cuotas de recursos de CPU
En función de su tamaño, cada inquilino tiene acceso a una cantidad establecida de recursos de CPU asignados para ejecutar consultas de búsqueda avanzadas. Para obtener información detallada sobre varios parámetros de uso, lea sobre las cuotas de búsqueda avanzadas y los parámetros de uso.
Después de ejecutar la consulta, puede ver el tiempo de ejecución y su uso de recursos (Bajo, Medio, Alto). High indica que la consulta tardó más recursos en ejecutarse y se pudo mejorar para devolver resultados de forma más eficaz.
Los clientes que ejecutan varias consultas con regularidad deben realizar un seguimiento del consumo y aplicar las instrucciones de optimización de este artículo para minimizar la interrupción resultante de superar las cuotas o los parámetros de uso.
Vea Optimización de consultas KQL para ver algunas de las formas más comunes de mejorar las consultas.
Sugerencias generales de optimización
Ajustar el tamaño de las nuevas consultas: si sospecha que una consulta devolverá un conjunto de resultados grande, evaluela primero con el operador count. Use limit o su sinónimo
take
para evitar conjuntos de resultados grandes.Aplicar filtros al principio: aplique filtros de tiempo y otros filtros para reducir el conjunto de datos, especialmente antes de usar funciones de transformación y análisis, como substring(), replace(), trim(), toupper()o parse_json(). En el ejemplo siguiente, la función de análisis extractjson() se usa después de que los operadores de filtrado hayan reducido el número de registros.
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "UsbDriveMount" | where DeviceName == "user-desktop.domain.com" | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
Contiene latidos: para evitar buscar subcadenas en palabras innecesariamente, use el
has
operador en lugar decontains
. Más información sobre los operadores de cadenaBuscar en columnas específicas: busque en una columna específica en lugar de ejecutar búsquedas de texto completo en todas las columnas. No use
*
para comprobar todas las columnas.Distingue mayúsculas de minúsculas para la velocidad: las búsquedas que distinguen mayúsculas de minúsculas son más específicas y, por lo general, tienen un mayor rendimiento. Los nombres de operadores de cadena que distinguen mayúsculas de minúsculas, como
has_cs
ycontains_cs
, suelen terminar con_cs
. También puede usar el operador==
equals que distingue mayúsculas de minúsculas en lugar de=~
.Analizar, no extraer: siempre que sea posible, use el operador de análisis o una función de análisis como parse_json(). Evite el
matches regex
operador de cadena o la función extract(), que usan la expresión regular. Reserve el uso de la expresión regular para escenarios más complejos. Obtenga más información sobre el análisis de funcionesFiltrar tablas no expresiones: no filtre por una columna calculada si puede filtrar por una columna de tabla.
Sin términos de tres caracteres: evite comparar o filtrar mediante términos con tres caracteres o menos. Estos términos no se indexan y la coincidencia con ellos requerirá más recursos.
Proyecto de forma selectiva: haga que los resultados sean más fáciles de entender proyectando solo las columnas que necesita. La proyección de columnas específicas antes de ejecutar operaciones de combinación o similares también ayuda a mejorar el rendimiento.
Optimización del join
operador
El operador join combina filas de dos tablas mediante la coincidencia de valores en columnas especificadas. Aplique estas sugerencias para optimizar las consultas que usan este operador.
Tabla más pequeña a la izquierda: el
join
operador coincide con los registros de la tabla del lado izquierdo de la instrucción join con los registros de la derecha. Al tener la tabla más pequeña a la izquierda, será necesario buscar menos registros, lo que acelerará la consulta.En la tabla siguiente, se reduce la tabla
DeviceLogonEvents
izquierda para cubrir solo tres dispositivos específicos antes de combinarla conIdentityLogonEvents
los SID de cuenta.DeviceLogonEvents | where DeviceName in ("device-1.domain.com", "device-2.domain.com", "device-3.domain.com") | where ActionType == "LogonFailed" | join (IdentityLogonEvents | where ActionType == "LogonFailed" | where Protocol == "Kerberos") on AccountSid
Usar el sabor de combinación interna: el sabor de combinación predeterminado o innerunique-join desduplica las filas de la tabla izquierda por la clave de combinación antes de devolver una fila para cada coincidencia a la tabla derecha. Si la tabla izquierda tiene varias filas con el mismo valor para la
join
clave, esas filas se desduplicarán para dejar una sola fila aleatoria para cada valor único.Este comportamiento predeterminado puede dejar fuera información importante de la tabla izquierda que puede proporcionar información útil. Por ejemplo, la consulta siguiente solo mostrará un correo electrónico que contenga datos adjuntos determinados, incluso si esos mismos datos adjuntos se enviaron mediante varios mensajes de correo electrónico:
EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Para abordar esta limitación, aplicamos el tipo de combinación interna especificando
kind=inner
para mostrar todas las filas de la tabla izquierda con valores coincidentes a la derecha:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Combinar registros desde un período de tiempo: al investigar eventos de seguridad, los analistas buscan eventos relacionados que se produzcan alrededor del mismo período de tiempo. La aplicación del mismo enfoque cuando se usa
join
también beneficia al rendimiento al reducir el número de registros que se van a comprobar.La consulta siguiente comprueba si hay eventos de inicio de sesión en un plazo de 30 minutos después de recibir un archivo malintencionado:
EmailEvents | where Timestamp > ago(7d) | where ThreatTypes has "Malware" | project EmailReceivedTime = Timestamp, Subject, SenderFromAddress, AccountName = tostring(split(RecipientEmailAddress, "@")[0]) | join ( DeviceLogonEvents | where Timestamp > ago(7d) | project LogonTime = Timestamp, AccountName, DeviceName ) on AccountName | where (LogonTime - EmailReceivedTime) between (0min .. 30min)
Aplicar filtros de tiempo en ambos lados: incluso si no está investigando un período de tiempo específico, la aplicación de filtros de tiempo en las tablas izquierda y derecha puede reducir el número de registros para comprobar y mejorar
join
el rendimiento. La consulta siguiente se aplicaTimestamp > ago(1h)
a ambas tablas para que se una solo a los registros de la última hora:EmailAttachmentInfo | where Timestamp > ago(1h) | where Subject == "Document Attachment" and FileName == "Document.pdf" | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
Usar sugerencias para el rendimiento: use sugerencias con el
join
operador para indicar al back-end que distribuya la carga al ejecutar operaciones que consumen muchos recursos. Más información sobre las sugerencias de combinaciónPor ejemplo, la sugerencia de orden aleatorio ayuda a mejorar el rendimiento de las consultas al combinar tablas mediante una clave con una cardinalidad alta (una clave con muchos valores únicos), como en la
AccountObjectId
consulta siguiente:IdentityInfo | where JobTitle == "CONSULTANT" | join hint.shufflekey = AccountObjectId (IdentityDirectoryEvents | where Application == "Active Directory" | where ActionType == "Private data retrieval") on AccountObjectId
La sugerencia de difusión ayuda cuando la tabla izquierda es pequeña (hasta 100 000 registros) y la tabla derecha es extremadamente grande. Por ejemplo, la consulta siguiente está intentando unirse a algunos correos electrónicos que tienen asuntos específicos con todos los mensajes que contienen vínculos en la
EmailUrlInfo
tabla:EmailEvents | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now") | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
Optimización del summarize
operador
El operador summarize agrega el contenido de una tabla. Aplique estas sugerencias para optimizar las consultas que usan este operador.
Buscar valores distintos: en general, use
summarize
para buscar valores distintos que puedan ser repetitivos. Puede ser innecesario usarlo para agregar columnas que no tienen valores repetitivos.Aunque un único correo electrónico puede formar parte de varios eventos, el ejemplo siguiente no es un uso eficaz de porque un identificador de mensaje de
summarize
red para un correo electrónico individual siempre viene con una dirección de remitente única.EmailEvents | where Timestamp > ago(1h) | summarize by NetworkMessageId, SenderFromAddress
El
summarize
operador se puede reemplazar fácilmente porproject
, lo que produce potencialmente los mismos resultados y consume menos recursos:EmailEvents | where Timestamp > ago(1h) | project NetworkMessageId, SenderFromAddress
El ejemplo siguiente es un uso más eficaz de
summarize
porque puede haber varias instancias distintas de una dirección de remitente que envía correo electrónico a la misma dirección de destinatario. Estas combinaciones son menos distintas y es probable que tengan duplicados.EmailEvents | where Timestamp > ago(1h) | summarize by SenderFromAddress, RecipientEmailAddress
Orden aleatorio de la consulta: aunque
summarize
se usa mejor en columnas con valores repetitivos, las mismas columnas también pueden tener una cardinalidad alta o un gran número de valores únicos. Al igual que eljoin
operador , también puede aplicar la sugerencia de orden aleatorio consummarize
para distribuir la carga de procesamiento y potencialmente mejorar el rendimiento cuando se trabaja en columnas con alta cardinalidad.La consulta siguiente usa
summarize
para contar una dirección de correo electrónico de destinatario distinta, que se puede ejecutar en los cientos de miles de organizaciones grandes. Para mejorar el rendimiento, incorporahint.shufflekey
:EmailEvents | where Timestamp > ago(1h) | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
Escenarios de consulta
Identificación de procesos únicos con identificadores de proceso
Los Id. de proceso (PID) se reciclan en Windows y se reutilizan para los nuevos procesos. Por sí solos, no pueden servir como identificadores únicos para procesos específicos.
Para obtener un identificador único para un proceso en un equipo específico, utilice el Id. de proceso conjuntamente con la hora de creación del proceso. Cuando se une a los datos en torno a los procesos, debe incluir columnas para el identificador de la máquina (tanto DeviceId
como DeviceName
), el identificador de proceso (ProcessId
o InitiatingProcessId
) y la hora de creación del proceso (ProcessCreationTime
o InitiatingProcessCreationTime
).
En la siguiente consulta de ejemplo se buscan procesos que obtienen acceso a más de 10 direcciones IP por el puerto 445 (SMB), lo que podría buscar recursos compartidos de archivos.
Consulta de ejemplo:
DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(12h) and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessFileName
| where RemoteIPCount > 10
La consulta resume tanto en InitiatingProcessId
como en InitiatingProcessCreationTime
para que se vea un único proceso, sin mezclar varios procesos con el mismo Id. de proceso.
Líneas de comandos de consulta
Hay varias formas de crear una línea de comandos para llevar a cabo una tarea. Por ejemplo, un atacante podría hacer referencia a un archivo de imagen sin una ruta de acceso, sin una extensión de archivo, usando variables de entorno o entre comillas. El atacante también podría cambiar el orden de los parámetros o agregar varias comillas y espacios.
Para crear consultas más duraderas en torno a las líneas de comandos, aplique las siguientes prácticas:
- Identifique los procesos conocidos (como net.exe o psexec.exe) mediante la coincidencia en los campos de nombre de archivo, en lugar de filtrar en la propia línea de comandos.
- Análisis de secciones de línea de comandos mediante la función parse_command_line()
- Al consultar argumentos de la línea de comandos, no busque una coincidencia exacta en varios argumentos no relacionados en un orden determinado. En su lugar, use expresiones regulares o use operadores de contenedores separados múltiples.
- Use coincidencias que no distinga mayúsculas de minúsculas. Por ejemplo, use
=~
,in~
ycontains
en lugar de==
,in
ycontains_cs
. - Para mitigar las técnicas de ofuscación de línea de comandos, considere la posibilidad de quitar comillas, reemplazar comas por espacios y reemplazar varios espacios consecutivos por un solo espacio. Hay técnicas de ofuscación más complejas que requieren otros enfoques, pero estos ajustes pueden ayudar a abordar los más comunes.
En los ejemplos siguientes se muestran varias maneras de construir una consulta que busca el archivo net.exe para detener el servicio de firewall "MpsSvc":
// Non-durable query - do not use
DeviceProcessEvents
| where ProcessCommandLine == "net stop MpsSvc"
| limit 10
// Better query - filters on file name, does case-insensitive matches
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe") and ProcessCommandLine contains "stop" and ProcessCommandLine contains "MpsSvc"
// Best query also ignores quotes
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe")
| extend CanonicalCommandLine=replace("\"", "", ProcessCommandLine)
| where CanonicalCommandLine contains "stop" and CanonicalCommandLine contains "MpsSvc"
Ingesta de datos de orígenes externos
Para incorporar listas largas o tablas grandes en la consulta, use el operador externaldata para ingerir datos de un URI especificado. Puede obtener datos de archivos en TXT, CSV, JSON u otros formatos. En el ejemplo siguiente se muestra cómo puede usar la amplia lista de hashes SHA-256 de malware proporcionados por MalwareBazaar (abuse.ch) para comprobar los datos adjuntos en los correos electrónicos:
let abuse_sha256 = (externaldata(sha256_hash: string)
[@"https://bazaar.abuse.ch/export/txt/sha256/recent/"]
with (format="txt"))
| where sha256_hash !startswith "#"
| project sha256_hash;
abuse_sha256
| join (EmailAttachmentInfo
| where Timestamp > ago(1d)
) on $left.sha256_hash == $right.SHA256
| project Timestamp,SenderFromAddress,RecipientEmailAddress,FileName,FileType,
SHA256,ThreatTypes,DetectionMethods
Análisis de cadenas
Hay varias funciones que puede usar para controlar de forma eficaz las cadenas que necesitan análisis o conversión.
Cadena | Función | Ejemplo de uso |
---|---|---|
Líneas de comandos | parse_command_line() | Extraiga el comando y todos los argumentos. |
Paths | parse_path() | Extraiga las secciones de una ruta de acceso de archivo o carpeta. |
Números de versión | parse_version() | Deconstruye un número de versión con hasta cuatro secciones y hasta ocho caracteres por sección. Use los datos analizados para comparar la antigüedad de la versión. |
Direcciones IPv4 | parse_ipv4() | Convierta una dirección IPv4 en un entero largo. Para comparar direcciones IPv4 sin convertirlos, use ipv4_compare(). |
Direcciones IPv6 | parse_ipv6() | Convierta una dirección IPv4 o IPv6 en la notación IPv6 canónica. Para comparar direcciones IPv6, use ipv6_compare(). |
Para obtener información sobre todas las funciones de análisis admitidas, lea acerca de las funciones de cadena de Kusto.
Nota:
Es posible que algunas tablas de este artículo no estén disponibles en Microsoft Defender para punto de conexión. Active Microsoft Defender XDR para buscar amenazas mediante más orígenes de datos. Puede mover los flujos de trabajo de búsqueda avanzados de Microsoft Defender para punto de conexión a Microsoft Defender XDR siguiendo los pasos descritos en Migración de consultas de búsqueda avanzadas desde Microsoft Defender para punto de conexión.
Temas relacionados
- Documentación del lenguaje de consulta de Kusto
- Parámetros de uso y cuotas
- Controlar errores de búsqueda avanzados
- Información general sobre la búsqueda avanzada de amenazas
- Aprender el lenguaje de consulta
Sugerencia
¿Desea obtener más información? Participe con la comunidad de Seguridad de Microsoft en nuestra Tech Community: Tech Community de Microsoft Defender XDR.