Aikido

Raro, não aleatório: usando a eficiência de tokens para Secrets

Escrito por
Zach Rice

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.

Em Regex é (quase) tudo o que precisa, aprendemos que usar uma combinação de padrões de expressão regular, entropia e filtros baseados em regras é uma maneira eficaz de detectar secrets candidatos. Regex é usado para lançar uma ampla rede para identificar candidatos. A entropia é usada como um filtro primário nos candidatos capturados e filtros adicionais, como a presença de palavras em inglês comumente usadas ou a filtragem em ficheiros conhecidos como "seguros", como go.sum, são aplicados por último.

A entropia faz um trabalho razoável na filtragem de falsos positivos, mas deixa muito a desejar, especialmente na avaliação secrets genéricos. Poderia haver algo melhor do que a entropia para esse filtro primário pós-captura regex? Esta publicação examina se a codificação Byte-Pair pode servir como uma alternativa mais eficaz à entropia para secrets .

Um rarotrevo de quatrofolhas

Entropia

O que diabos é entropia? De acordo com John von Neumann, numa conversa com Claude Shannon, “ninguém sabe ao certo”, mas a Wikipedia sim. A entropia de Shannon mede a imprevisibilidade média de uma sequência, ou seja, a quantidade de informação que cada caractere transporta. Quando os caracteres estão distribuídos uniformemente (muitos caracteres distintos, sem padrão claro), cada um é mais difícil de prever, pelo que a entropia é elevada. Quando alguns caracteres dominam, o caractere seguinte é fácil de adivinhar, pelo que a entropia é baixa. Na prática, isso significa algo como aaaaaa111111 tem uma pontuação baixa, enquanto algo como xA9fP2qL0sRw tem uma pontuação elevada. No que diz respeito à secrets , isto torna a entropia uma primeira tentativa decente para identificar cadeias de caracteres «aparentemente aleatórias» ( secrets candidatos).

Mas será que realmente queremos que a aleatoriedade seja o nosso principal filtro para secrets ? Desculpem o cliché do LLM «não é X, é Y», mas secrets apenas aleatórios, são estatisticamente incomuns em comparação com a distribuição natural do texto escrito por humanos. Em termos mais simples, secrets raros. Uma string codificada em b64, um UUID, um segredo real e uma string de dependência com aparência estranha podem ter pontuações de entropia semelhantes, apesar de serem fundamentalmente diferentes na frequência com que aparecem no mundo real. A entropia não consegue distinguir entre «isto parece aleatório» e «isto quase nunca aparece em texto ou código-fonte em inglês». Em vez de medir a aleatoriedade com entropia, e se tentássemos medir o quão fora do vocabulário ou quão não natural é uma string?

Byte-pair encoding

Então, como podemos detectar se uma sequência de caracteres parece não natural ou é rara? Com a codificação de pares de bytes (BPE), é claro! A tokenização por codificação de pares de bytes reflete implicitamente a distribuição de frequência do texto com o qual foi treinada. Palavras e subpalavras comuns são mescladas em tokens longos, enquanto sequências raras ou não naturais são divididas em muitos tokens curtos.

Aqui estão alguns exemplos usando otokenizador cl100k_base1:

  • “Olá, mundo” → [15339, 1917]
  • «olhando para o computador» → [20986, 266, 44211]
  • “kj2h3f2fuaafewa” → [93797, 17, 71, 18, 69, 17, 69, 4381, 2642, 365, 64]

Como o BPE constrói o seu vocabulário através da fusão repetida dos pares de caracteres mais comuns nos dados de treino, a sua tokenização reflete naturalmente a frequência com que os diferentes padrões aparecem. Parece um pouco aquela coisa da raridade que estamos a tentar medir, não é?

Palavras comuns em inglês recebem os seus próprios tokens individuais porque aparecem com frequência no treinamento, por exemplo, «password» é o token [3918]. «github» é o token [5316]. «function» é o token [1723]. Mas e uma chave API aleatória como `ghp_xK7mP9qL2wR5nT3vJ8fY`?

ghp_xK7mP9qL2wR5nT3vJ8fY

O tokenizador provavelmente nunca viu essa sequência específica durante o treino, então divide a string em pares menores, eventualmente recorrendo a bytes individuais que acabam por ser tokenizados em [876, 79, 3292, 42, 22, 76, 47, 24, 80, 43, 17, 86, 49, 20, 77, 51, 18, 85, 41, 23, 69, 56]. São 22 tokens para uma string de 24 caracteres, o que significa que o tokenizador mal reconheceu algo nela.

Visite https://tiktokenizer.vercel.app/?model=cl100k_base para ver como diferentes cadeias de caracteres são tokenizadas.

Token efficiency

Se os tokenizadores BPE dividirem sequências raras em muitos tokens curtos, então podemos medir o quão rara é uma sequência comparando o comprimento da sequência original com o número de tokens produzidos. Ora, vamos chamá-lo de Eficiência do token.

eficiência_do_token = comprimento(cadeia_de_caracteres) / comprimento(tokens)

A linguagem natural se adapta bem ao vocabulário do tokenizador, portanto, frases comuns produzem menos tokens. Strings semelhantes a segredos não, portanto, produzem muitos tokens.

Considere o nosso exemplo de ghp_xK7mP9qL2wR5nT3vJ8fY. Tem uma eficiência de token de 1,1 (uma sequência de 24 caracteres produzindo 22 tokens). Uma frase como Olá, mundo tem uma eficiência de 3,7 (11 caracteres divididos em 3 tokens). Se secrets produzem secrets pontuações de eficiência de token mais baixas e o texto comum produz pontuações mais altas, então a eficiência de token pode ser um filtro pós-regex útil para secrets .

Para testar essa ideia, podemos recorrer ao conjunto de dados CredData, que contém milhares de exemplos rotulados de secrets verdadeiros secrets secrets de repositórios do mundo real. Se a eficiência do token realmente acompanha a raridade ou a «não linguagem cotidiana», então observar a distribuição da eficiência do token nos valores secretos do conjunto de dados CredData pode revelar uma diferença entre secrets secrets.

CredData

O conjunto de dados CredData é dividido em ficheiros de índice e ficheiros de dados. Os ficheiros de índice armazenam os metadados necessários, como rótulos, intervalos de linhas e colunas e nomes de ficheiros. Eles não contêm os valores secretos reais, portanto, é necessário reconstruir cada segredo dividindo os ficheiros de origem nos intervalos especificados. Essa foi a abordagem que adotei. Extraí todos os valores secretos rotulados diretamente do conjunto de dados. Isto significa que não estamos a avaliar se a eficiência do token pode detetar secrets si só. Em vez disso, estamos a avaliar se a eficiência do token pode classificar secrets candidatos já capturados, o que o torna uma etapa de filtragem pós-regex, em vez de um detetor independente.

Pode ver o código que produz estes gráficos aqui:

Os gráficos mostram que secrets a ter menor eficiência de tokens e menor entropia do quesecrets, com padrões de caracteres mais compactos e menos variados.

Parece promissor! Parece que 2,5 é um bom limite mínimo para a eficiência do token. O Gitleaks usa um limite de entropia de 3,5 para secrets genéricos.

Usando esses limites, vamos dar uma olhada nas classificações.

O filtro de eficiência de token alcança muito menos falsos positivos, ao custo de mais verdadeiros negativos empurrados abaixo do limiar, em comparação com o filtro de entropia.
Eficiência do token: Precisão=57,3%  Recall=98,6%  F1=0,725
Entropia:          Precisão=21,1%  Recall=70,4%  F1=0,325

Uma recuperação de 98,6% é muito boa. Estamos a classificar corretamente quase todos secrets verdadeiros secrets apenas 149 falsos negativos na mesa. Há uma quantidade razoável de falsos positivos para a eficiência de tokens, mas a diferença entre isso e a entropia é como a noite e o dia. A entropia gera 28 mil FPs (quase 4 vezes mais do que a eficiência de tokens) e 3 mil FNs. Adicionar um filtro de palavras simples à mistura ajuda ambos os métodos, mas a eficiência de tokens ainda ganha na pontuação F1. O filtro de palavras ignora secrets mais de uma ocorrência de uma palavra com 4 ou mais caracteres.

Adicionar um filtro de palavras aos classificadores de eficiência de tokens e entropia reduz drasticamente os falsos positivos (para cerca de 2.500 cada), mantendo as taxas de verdadeiros positivos relativamente estáveis.
TE + Filtro de palavras: Precisão=80,4%  Recall=95,8%  F1=0,874
Entropia + Filtro de palavras:   Precisão=76,6%  Recall=67,1%  F1=0,715

Este filtro faz grande parte do trabalho pesado especificamente para a entropia, mas também nos ajuda a filtrar FPs para eficiência de tokens. Para a eficiência de tokens, passámos de 7894 FPs → 2508 FPs, introduzindo apenas 308 novos FNs ao aplicar este filtro de palavras, o que nos ajuda significativamente com a pontuação F1.

Se quiser tentar reproduzir estes resultados, pode verificar parte do código no meu Github.

Exemplos

Vamos dar uma olhada em alguns secrets a entropia deixa passar, mas a eficiência dos tokens capta.

e2aa9ae57d893a1
Este indivíduo tem uma entropia de 3,125. Bastante elevada, mas não chega aos 3,5, que é o valor que o Gitleaks e alguns outros detetores de segredos utilizam como limite. e2aa9ae57d893a1 produz [68, 17, 5418, 24, 6043, 3226, 67, 26088, 64, 717] para os seus tokens cl100k_base, o que resulta numa eficiência de token de 1,6, bem abaixo do limite de eficiência de token de 2,5.

mcjrx4
Aqui temos uma palavra-passe, e não uma muito boa. As palavras-passe são uma categoria difícil para o filtro de entropia, porque muitas vezes (e infelizmente) são curtas, e cadeias curtas normalmente têm valores de entropia baixos. Esta tem uma entropia de apenas 2,58. Mas o tokenizador divide-a em tokens quase ao nível do byte [13183, 73, 12940, 19], dando-lhe uma eficiência de token de 1,5. Seis caracteres, quatro tokens. O tokenizador não a reconhece como linguagem natural e esse é exatamente o sinal que queremos.

U@kkf8fo!!
Outra palavra-passe. Esta é interessante devido aos caracteres especiais. Um dos desafios na secrets , especificamente para secrets palavras-passe genéricos, é criar uma expressão regular que capture a maioria secrets. O problema de usar uma expressão regular que visa capturar a maioria secrets que tem o potencial de deixar passar muitos falsos positivos, como e-mails, URLs, etc. Portanto, para cada caractere especial como @ ou ! ou / ao definir a classe de caracteres do seu grupo de captura, aumenta as hipóteses de permitir mais falsos positivos. Por isso, podemos ver que o grupo de captura genérico do Gitleaks é bastante rigoroso: [\w.=-]{10,150}Com um filtro de eficiência de token, poderíamos potencialmente flexibilizar esse padrão para incluir mais caracteres especiais. Ok, então, com esse contexto, veja como a entropia e a eficiência de token se comparam neste exemplo. U@kkf8fo!! tem uma entropia de 2,72 e produz estes tokens [52, 31, 19747, 69, 23, 831, 51447] com uma eficiência de token de 1,42 (10 caracteres, 7 tokens).

Uma breve observação sobre senhas. A eficiência de tokens não funciona bem com a classificação de senhas ruins, como “password123” ou “chibearsfan123”. Essas senhas são basicamente linguagem natural, o que significa um alto valor de eficiência de tokens. Frases-senha também não funcionam bem, pois geralmente são apenas palavras simples.

Desempenho

O impacto no desempenho é insignificante. O tempo médio por string para calcular a entropia nos secrets CredData capturados secrets 4,55 µs contra 11,75 µs para calculara eficiência do token2 (usando cl100k_base). Uma diferença de 2,5x pode parecer muito, mas é preciso lembrar que, quando se trata de secrets , o gargalo são as expressões regulares, não os filtros rápidos como entropia ou eficiência do token que vêm depois.

Token efficiency w/ Betterleaks

Os mantenedores do conjunto de dados CredData criaram um impressionante scanner de segredos chamado CredSweeper, que usa regex, entropia e RNNs para detectar secrets. Num mundo repleto de «LLMs podem detectar secrets ZERO falsos positivos» (tanto na academia quanto naindústria3), é revigorante ver os engenheiros da Samsung a construir um secrets baseado em um «aprendizado de máquina mais tradicional». Parabéns. O CredSweeper ostenta uma impressionante pontuação F1 de 0,85 quando testado contra o CredData. Isso é muito bom! Vamos ver se conseguimos superá-lo com o novo filtro de eficiência de token no Betterleaks.

Ah, certo. O que é Betterleaks? É um novo projeto que se baseia no legado do Gitleaks. Falarei mais sobre isso em outro post, mas tudo o que precisa saber é que é um substituto direto para o Gitleaks que estou a manter... e vai ser melhor... por causa do nome.

Esta configuração adiciona algumas novas regras e ajusta algumas pequenas coisas na configuração padrão existente. Usando esta configuração e executando o Betterleaks no conjunto de dados CredData, obtém-se uma pontuação F1 de 0,892.

(Eficiência do token + (Baixa) Entropia na regra genérica + Ajustes na regra) Resultados da avaliação comparativa:
========================================
TP (Verdadeiros positivos):     10796
FP (Falsos positivos):     1031
TN (Verdadeiros negativos):     42572
FN (Falsos negativos):     1578
----------------------------------------
Precisão:                    0,9534
 Precisão:                   0,9128
 Recuperação:                      0,8725
Pontuação F1:                    0,8922

Muito bom.

Larry David acha que é muito bom.

Usando apenas a eficiência do token, obtemos:

(Apenas eficiência de token + ajustes nas regras) Resultados de benchmark:
========================================
TP (Verdadeiros positivos):     10843
FP (Falsos positivos):     1722
TN (Verdadeiros negativos):     41881
FN (Falsos negativos):     1531
----------------------------------------
Precisão:                    0,9419
 Precisão:                   0,8630
 Recuperação:                      0,8763
Pontuação F1:                    0,8696

Sem utilizar um corte de baixa entropia na regra genérica ao usar o filtro de eficiência de token, introduzimos cerca de 700 FPs. Ainda assim, sem esse filtro de entropia na regra genérica, obtemos um F1 de 0,86, o que não é mau.

Como pontuamos sem o filtro de eficiência de fichas, mas apenas com ajustes nas regras e entropia?

(Apenas ajustes de entropia + regras) Resultados da avaliação comparativa:
========================================
TP (Verdadeiros positivos):      8498
FP (Falsos positivos):     1041
TN (Verdadeiros negativos):     42562
FN (Falsos negativos):     3876
----------------------------------------
Precisão:                    0,9122
 Precisão:                   0,8909
 Recuperação:                      0,6868
Pontuação F1:                    0,7756

Muito bem, então 0,892 contra 0,776 é uma diferença bastante grande. Usar apenas o filtro de entropia adiciona mais de 2000 FNs e 80 FPs em comparação com o filtro de eficiência de token.

Você pode ver o código do filtro Token Efficiency no 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
}

O filtro foi ligeiramente adaptado em relação ao utilizado na comparação do gráfico. Isto foi feito para ter em conta palavras-passe curtas e secrets novas linhas (removemos as novas linhas antes de executar a análise de eficiência do token no candidato).

Algumas outras observações:

  • Não consegui fazer funcionar o script de benchmarking fornecido pela CredData, então eu (claude) criei o meu próprio. Desculpem se isso não for muito científico, mas podem verificar o meu (claude) trabalho, pois é open source.
  • As duplicatas foram removidas do conjunto de dados CredData.
  • Todas as novas regras e ajustes às regras existentes não eram «secretas». Ou seja, tentei não manipular o benchmark.
  • Alguns secrets como FPs no conjunto de dados CredData parecem estar rotulados erroneamente, então, honestamente, o par F1 pode ser +/ 0,05, talvez.

1 Para todos os exemplos que analisaremos, utilizaremos o tokenizador cl100k_base.

2 Tempo medido a partir do script gerador de gráficos que calcula a entropia e a eficiência dos tokens para cada segredo candidato.

Agradecimentos: Quero agradecer ao utilizador do GitHub“DmitriyAlergant”por enviar esta ideia numa questão no repositório Gitleaks. Um grande agradecimento aos mantenedores do CredData/CredSweeper por me terem ajudado a entrar neste mundo.

Compartilhar:

https://www.aikido.dev/blog/token-efficiency-secrets-scan

Assine para receber notícias sobre ameaças.

Comece hoje, gratuitamente.

Comece Gratuitamente
Não é necessário cc

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.