Note: Zach Rice, Head of Secrets Scanning at Aikido, is also the founder of Gitleaks. This post originally appeared on his blog, where he covers secrets scanning, software engineering, and open source topics.
En «Regex es (casi) todo lo que necesitas», aprendimos que el uso combinado de patrones de expresiones regulares, entropía y filtros basados en reglas es una forma eficaz de detectar secretos candidatos. Regex se utiliza para lanzar una amplia red con el fin de identificar candidatos. La entropía se utiliza como filtro principal sobre los candidatos capturados y, por último, se aplican filtros adicionales como la presencia de palabras inglesas de uso común o el filtrado de archivos «seguros» conocidos, como go.sum.
La entropía hace un buen trabajo filtrando los falsos positivos, pero deja mucho que desear, especialmente cuando se evalúan secretos genéricos. ¿Podría haber algo mejor que la entropía para ese filtro primario posterior a la captura de expresiones regulares? En esta publicación se examina si la codificación por pares de bytes puede servir como una alternativa más eficaz que la entropía para escaneo de secretos.

Entropía
¿Qué diablos es la entropía? Según John von Neumann, en una conversación con Claude Shannon: «nadie lo sabe realmente”, pero Wikipedia sí. La entropía de Shannon mide la imprevisibilidad media de una cadena, es decir, la cantidad de información que contiene cada carácter. Cuando los caracteres están distribuidos de manera uniforme (muchos caracteres distintos, sin un patrón claro), cada uno es más difícil de predecir, por lo que la entropía es alta. Cuando unos pocos caracteres predominan, el siguiente carácter es fácil de adivinar, por lo que la entropía es baja. En la práctica, eso significa algo así como aaaaaa111111 obtiene una puntuación baja, mientras que algo como xA9fP2qL0sRw obtiene una puntuación alta. En lo que respecta a detección de secretos, esto hace que la entropía sea una primera aproximación decente para detectar cadenas «de aspecto aleatorio» (secretos candidatos).
Pero, ¿realmente queremos que la aleatoriedad sea nuestro filtro principal para detección de secretos? Disculpen el tropo LLM de «no es X, es Y», pero los secretos no son solo aleatorios, sino que son estadísticamente inusuales en comparación con la distribución natural del texto escrito por humanos. En pocas palabras, los secretos son raros. Una cadena codificada en b64, un UUID, un secreto real y una cadena de dependencia de aspecto extraño pueden tener puntuaciones de entropía similares a pesar de ser fundamentalmente diferentes en cuanto a la frecuencia con la que aparecen en el mundo real. La entropía no puede distinguir entre «esto parece aleatorio» y «esto casi nunca aparece en un texto o código fuente en inglés». En lugar de medir la aleatoriedad con la entropía, ¿qué pasaría si intentáramos medir lo fuera de vocabulario o lo poco natural que es una cadena?
Byte-pair encoding
Bien, ¿cómo detectamos si una cadena parece poco natural o poco común? ¡Con la codificación de pares de bytes (BPE), por supuesto! La tokenización con codificación de pares de bytes refleja implícitamente la distribución de frecuencias del texto con el que se ha entrenado. Las palabras y subpalabras comunes se fusionan en tokens largos, mientras que las cadenas poco comunes o poco naturales se dividen en muchos tokens cortos.
Aquí hay un par de ejemplos que utilizan eltokenizador cl100k_base1:
- «Hola mundo» → [15339, 1917]
- «mirando el ordenador» → [20986, 266, 44211]
- «kj2h3f2fuaafewa» → [93797, 17, 71, 18, 69, 17, 69, 4381, 2642, 365, 64]
Dado que BPE construye su vocabulario fusionando repetidamente los pares de caracteres más comunes en los datos de entrenamiento, su tokenización refleja naturalmente la frecuencia con la que aparecen los diferentes patrones. Suena un poco como esa rareza que estamos tratando de medir, ¿no?
Las palabras comunes en inglés tienen sus propios tokens individuales porque aparecen con frecuencia en el entrenamiento, por ejemplo, «password» es el token [3918]. «github» es el token [5316]. «function» es el token [1723]. Pero, ¿qué pasa con una clave API aleatoria como `ghp_xK7mP9qL2wR5nT3vJ8fY`?

Es probable que el tokenizador nunca haya visto esa secuencia específica durante el entrenamiento, por lo que divide la cadena en pares más pequeños y, finalmente, recurre a bytes individuales que terminan tokenizándose en [876, 79, 3292, 42, 22, 76, 47, 24, 80, 43, 17, 86, 49, 20, 77, 51, 18, 85, 41, 23, 69, 56]. Son 22 tokens para una cadena de 24 caracteres, lo que significa que el tokenizador apenas reconoció nada en ella.
Visita https://tiktokenizer.vercel.app/?model=cl100k_base para ver cómo se tokenizan diferentes cadenas.
Token efficiency
Si los tokenizadores BPE dividen las cadenas poco frecuentes en muchos tokens cortos, entonces podemos medir lo poco frecuente que es una cadena comparando la longitud de la cadena original con el número de tokens producidos. Bueno, llamémoslo Eficiencia de los tokens.eficiencia_token = len(cadena) / len(tokens)
El lenguaje natural se adapta bien al vocabulario del tokenizador, por lo que las frases comunes producen menos tokens. Las cadenas secretas no lo hacen, por lo que producen muchos tokens.
Consideremos nuestro ejemplo de ghp_xK7mP9qL2wR5nT3vJ8fY. Tiene una eficiencia de tokens de 1,1 (una cadena de 24 caracteres que produce 22 tokens). Una frase como Hola mundo tiene una eficiencia de 3,7 (11 caracteres divididos en 3 tokens). Si los secretos producen sistemáticamente puntuaciones de eficiencia de tokens más bajas y el texto cotidiano produce puntuaciones más altas, entonces la eficiencia de tokens podría ser un filtro post-regex útil para detección de secretos.
Para comprobar esta idea, podemos recurrir al conjunto de datos CredData, que contiene miles de ejemplos etiquetados de secretos reales y no secretos extraídos de repositorios del mundo real. Si la eficiencia de los tokens realmente refleja la rareza o el «carácter no cotidiano del lenguaje», entonces observar la distribución de la eficiencia de los tokens en los valores secretos del conjunto de datos CredData podría revelar una diferencia entre los secretos y los no secretos.
CredData
El conjunto de datos CredData se divide en archivos de índice y archivos de datos. Los archivos de índice almacenan los metadatos que necesita, como etiquetas, rangos de líneas y columnas, y nombres de archivos. No contienen los valores secretos reales, por lo que debe reconstruir cada secreto dividiendo los archivos de origen en los rangos especificados. Ese es el enfoque que adopté. Extraje todos los valores secretos etiquetados directamente del conjunto de datos. Esto significa que no estamos evaluando si la eficiencia de los tokens puede detectar secretos por sí sola. En cambio, estamos evaluando si la eficiencia de los tokens puede clasificar los secretos candidatos ya capturados, lo que lo convierte en un paso de filtrado posterior a la expresión regular en lugar de un detector independiente.
Puedes echar un vistazo al código que genera estos gráficos aquí:

¡Parece prometedor! Parece que 2,5 es un buen límite mínimo para la eficiencia de tokens. Gitleaks utiliza un límite de entropía de 3,5 para secretos genéricos.
Utilizando esos límites, echemos un vistazo a las clasificaciones.

Eficiencia del token: Precisión=57,3% Recuperación=98,6% F1=0,725
Entropía: Precisión=21,1% Recuperación=70,4% F1=0,325Una recuperación del 98,6 % es bastante buena. Estamos clasificando correctamente casi todos los secretos verdaderos y solo dejamos 149 falsos negativos sobre la mesa. Hay una cantidad considerable de falsos positivos para la eficiencia de tokens, pero la diferencia entre esto y la entropía es como la noche y el día. La entropía genera 28 000 FP (casi cuatro veces más que la eficiencia de tokens) y 3000 FN. Añadir un filtro de palabras simple a la mezcla ayuda a ambos métodos, pero la eficiencia de tokens sigue ganando en la puntuación F1. El filtro de palabras ignora los secretos con más de una aparición de una palabra de cuatro o más caracteres.

TE + Filtro de palabras: Precisión=80,4% Recuperación=95,8% F1=0,874
Entropía + Filtro de palabras: Precisión=76,6% Recuperación=67,1% F1=0,715Este filtro realiza gran parte del trabajo pesado en lo que respecta específicamente a la entropía, pero también nos ayuda a filtrar los FP para mejorar la eficiencia de los tokens. En cuanto a la eficiencia de los tokens, pasamos de 7894 FP a 2508 FP, al tiempo que solo introdujimos 308 FN nuevos al aplicar este filtro de palabras, lo que nos ayuda significativamente con la puntuación F1.
Si quieres intentar reproducir estos resultados, puedes consultar parte del código en mi Github.
Ejemplos
Echemos un vistazo a algunos secretos que la entropía pasa por alto, pero que la eficiencia simbólica capta.
e2aa9ae57d893a1
Este tipo tiene una entropía de 3,125. Bastante alta, pero no llega a 3,5, que es el límite que utilizan Gitleaks y otros detectores de secretos. e2aa9ae57d893a1 produce [68, 17, 5418, 24, 6043, 3226, 67, 26088, 64, 717] para sus tokens cl100k_base, lo que da una eficiencia de tokens de 1,6, muy por debajo del límite de eficiencia de tokens de 2,5.
mcjrx4
Aquí tenemos una contraseña, y no es muy buena. Las contraseñas son una categoría difícil para el filtro de entropía porque suelen ser (por desgracia) cortas, y las cadenas cortas suelen tener valores de entropía bajos. Esta tiene una entropía de solo 2,58. Pero el tokenizador la descompone en tokens cercanos al nivel de byte [13183, 73, 12940, 19], lo que le da una eficiencia de token de 1,5. Seis caracteres, cuatro tokens. El tokenizador no la reconoce como lenguaje natural y esa es exactamente la señal que queremos.
¡¡U@kkf8fo!!
Otra contraseña. Esta es interesante por los caracteres especiales. Uno de los retos en detección de secretos para secretos y contraseñas genéricas, es crear una expresión regular que capture la mayoría secretos. El problema de usar una expresión regular que pretende capturar la mayoría El problema de los secretos es que pueden dejar pasar muchos falsos positivos, como correos electrónicos, direcciones URL, etc. Por lo tanto, para cada carácter especial como @ o ! o / al definir la clase de caracteres de tu grupo de captura, aumentas las posibilidades de que se produzcan más falsos positivos. Por este motivo, podemos ver que el grupo de captura genérico de Gitleaks es bastante estricto: [\w.=-]{10,150}Con un filtro de eficiencia de tokens, podríamos flexibilizar ese patrón para incluir más caracteres especiales. Bien, con ese contexto, así es como se comparan la entropía y la eficiencia de tokens en este ejemplo. ¡¡U@kkf8fo!! tiene una entropía de 2,72 y produce estos tokens [52, 31, 19747, 69, 23, 831, 51447] con una eficiencia de tokens de 1,42 (10 caracteres, 7 tokens).
Una breve nota sobre las contraseñas. La eficiencia de tokens no funciona bien con la clasificación de contraseñas malas como «password123» o «chibearsfan123». Estas contraseñas son básicamente lenguaje natural, lo que significa un alto valor de eficiencia de tokens. Las frases de contraseña tampoco funcionan bien porque suelen ser palabras sencillas.
Rendimiento
El impacto en el rendimiento es insignificante. El tiempo medio por cadena para calcular la entropía de los secretos CredData capturados es de 4,55 µs frente a los 11,75 µs necesarios para calcularla eficiencia de los tokens2 (utilizando cl100k_base). Una diferencia de 2,5 veces puede parecer mucho, pero hay que recordar que, cuando se trata de detección de secretos cuello de botella son las expresiones regulares, no los filtros rápidos como la entropía o la eficiencia de los tokens que vienen después.
Token efficiency w/ Betterleaks
Los responsables del mantenimiento del conjunto de datos CredData han creado un impresionante escáner de secretos llamado CredSweeper, que utiliza expresiones regulares, entropía y RNN para detectar secretos. En un mundo en el que se dice que «los LLM pueden detectar secretos con CERO falsos positivos» (tanto en el ámbito académico como enel industrial3), resulta refrescante ver cómo los ingenieros de Samsung han creado un detector de secretos basado en un «aprendizaje automático más tradicional». Felicidades. CredSweeper presume de una impresionante puntuación F1 de 0,85 cuando se prueba con CredData. ¡Eso está muy bien! Veamos si podemos superarlo con el nuevo filtro de eficiencia de tokens en Betterleaks.
Ah, claro. ¿Qué es Betterleaks? Es un nuevo proyecto que se basa en el legado de Gitleaks. Hablaré más sobre ello en otra publicación, pero lo único que necesitas saber es que es un sustituto directo de Gitleaks que yo mantengo... y que va a ser mejor... por el nombre.
Esta configuración añade un par de reglas nuevas y modifica algunos pequeños detalles de la configuración predeterminada existente. Al utilizar esta configuración y ejecutar Betterleaks con el conjunto de datos CredData, se obtiene una puntuación F1 de 0,892.
(Eficiencia de tokens + (baja) entropía en reglas genéricas + ajustes de reglas) Resultados de referencia:
========================================
TP (verdaderos positivos): 10796
FP (falsos positivos): 1031
TN (verdaderos negativos): 42572
FN (falsos negativos): 1578
----------------------------------------
Precisión: 0,9534
Precisión: 0,9128
Recuperación: 0,8725
Puntuación F1: 0,8922Muy bien.

Si utilizamos solo la eficiencia de los tokens, obtenemos:
(Solo eficiencia de tokens + ajustes en las reglas) Resultados de referencia:
========================================
TP (verdaderos positivos): 10843
FP (falsos positivos): 1722
TN (verdaderos negativos): 41881
FN (falsos negativos): 1531
----------------------------------------
Precisión: 0,9419
Precisión: 0,8630
Recuperación: 0,8763
Puntuación F1: 0,8696Sin utilizar un límite de entropía bajo en la regla genérica al utilizar el filtro de eficiencia de tokens, introducimos ~700 FP. Aun así, sin ese filtro de entropía en la regla genérica, obtenemos un F1 de 0,86, lo cual no está mal.
¿Cómo puntuamos sin el filtro de eficiencia de fichas, sino basándonos únicamente en ajustes de reglas y entropía?
(Solo ajustes de entropía + reglas) Resultados de referencia:
========================================
TP (verdaderos positivos): 8498
FP (falsos positivos): 1041
TN (verdaderos negativos): 42562
FN (falsos negativos): 3876
----------------------------------------
Precisión: 0,9122
Precisión: 0,8909
Recuperación: 0,6868
Puntuación F1: 0,7756De acuerdo, entonces 0,892 frente a 0,776 es una diferencia bastante grande. El uso exclusivo del filtro de entropía añade más de 2000 FN y 80 FP frente al filtro de eficiencia de tokens.
Puedes ver el código del filtro Token Efficiency en Betterleaks Github.
func (d *Detector) failsTokenEfficiencyFilter(secret string) bool {
analyzed := secret
if len(analyzed) < 20 && strings.ContainsAny(analyzed, "\n\r") {
analyzed = newlineReplacer.Replace(analyzed)
}
tokens := d.tokenizer.Encode(analyzed, nil, nil)
matches := words.HasMatchInList(analyzed, 5)
if len(matches) > 0 {
return true
}
threshold := 2.5
if len(analyzed) < 12 {
threshold = 2.1
matches := words.HasMatchInList(analyzed, 3)
if len(matches) == 0 {
threshold = 2.5
}
}
return float64(len(analyzed))/float64(len(tokens)) >= threshold
}El filtro se ha adaptado ligeramente en comparación con el utilizado en la comparación de gráficos. Esto es para tener en cuenta las contraseñas cortas y los secretos con saltos de línea (eliminamos los saltos de línea antes de ejecutar el análisis de eficiencia de tokens en el candidato).
Un par de notas más:
- No conseguí que funcionara el script de benchmarking proporcionado por CredData, así que yo (claude) creé uno propio. Lo siento si no es muy científico, pero bueno, puedes comprobar mi trabajo (claude) ya que es de código abierto.
- Se eliminaron los duplicados del conjunto de datos CredData.
- Todas las nuevas reglas y ajustes a las reglas existentes no eran «secretas». Es decir, intenté no manipular el punto de referencia.
- Algunos secretos etiquetados como FP en el conjunto de datos CredData parecen estar etiquetados erróneamente, por lo que, sinceramente, el par F1 podría ser de +/ 0,05 quizás.
1 Para todos los ejemplos que veremos, utilizaremos el tokenizador cl100k_base.
2 Tiempos tomados del script generador de gráficos que calcula la entropía y la eficiencia de los tokens para cada secreto candidato.
Agradecimientos: Quiero dar las gracias al usuario de GitHub«DmitriyAlergant»por enviar esta idea en un problema en el repositorio Gitleaks. Muchas gracias a los mantenedores de CredData/CredSweeper por ayudarme a adentrarme en este mundo.

