
Triagem de exposição de credenciais e dados sensíveis para compartilhamentos de arquivos
Triagem de exposição de credenciais e dados sensíveis para compartilhamentos de arquivos.
Quando um compartilhamento aberto aparece, a pergunta nunca é "esse repositório tem uma chave vazada". É "o que exatamente foi exposto, e o que preciso rotacionar antes do fim do expediente." o sift foi feito para essa pergunta: alta revocação, uma fila de revisão rápida, e um ciclo de feedback para que qualquer coisa que você identifique a olho nu vire uma regra que encontra as outras duzentas cópias.

Python 3.11+, apenas biblioteca padrão. Sem pip install, sem internet, sem etapa de build. Ele roda em um laptop de IR isolado, que é onde você precisa.```bash sift survey \fileserver\openshare # how big is this thing sift copy \fileserver\openshare C:\IR\case-4471 # take a throttled copy sift scan C:\IR\case-4471 # scan it, opens the triage UI
A verificação no local também funciona. Experimente primeiro em um compartilhamento de credenciais fabricadas:```bash
sift demo C:\temp\demoshare
sift.cmd é um launcher que funciona a partir de qualquer diretório. Para digitar sift em vez
do caminho completo, adicione C:\Dev\sift ao PATH:```bash
setx PATH "%PATH%;C:\Dev\sift"
Para uma máquina sem Python algum, `python build_portable.py` gera
`dist/sift-secrets-<version>-portable-win64.zip`: o runtime embutível oficial do python.org
mais esta árvore de origem, basta descompactar e executar via o `sift.cmd` incluso.
~11 MB, sem instalação, sem direitos de administrador, e nada nele é compilado ou
reempacotado — veja o docstring em `build_portable.py` para entender por que isso
supera um .exe congelado em um laptop de IR bloqueado.
---
## Por que não apenas gitleaks ou trufflehog
Ambas são boas ferramentas que resolvem um problema diferente.
São ferramentas de **precisão** criadas para CI, onde um falso positivo custa a
tarde de um desenvolvedor, então disparam principalmente em coisas com formato de uma
chave de API de fornecedor conhecida. o trufflehog vai além e prefere segredos que consegue *verificar* ao
chamar a API do fornecedor, o que é um sinal genuinamente excelente que regex
não consegue reproduzir.
A triagem de compartilhamento inverte a economia. Um humano já está lendo cada ocorrência, então um
falso positivo custa três segundos. O que te custa é uma **falha**.
Chaves de API de fornecedores definitivamente vazam em compartilhamentos - um backup da raiz da web, um
script de implantação, a pasta de projeto de alguém copiada para o drive departamental, e há
um `.env` com uma chave Stripe viva dentro. Esses casos valem a pena detectar, e o sift
os detecta. Mas eles também são a parte que gitleaks e trufflehog já lidam
bem. A lacuna é todo o resto, e em um compartilhamento de arquivos isso é a maior parte:
| O que os scanners de CI deixam passar | Por que eles passam por cima |
|---|---|
| `web.config` com uma string de conexão SQL | Não é um formato de chave conhecido, não há fornecedor para verificar |
| `Map-Drives.ps1` com `net use ... /user:` | Apenas um comando de shell com uma palavra depois |
| `New Hire Setup Guide.docx` | Arquivo do Office, lido como binário, ignorado completamente |
| `unattend.xml`, GPP `Groups.xml` | Artefatos de implantação do Windows para os quais ninguém escreveu um detector |
| `confCons.xml`, `.rdg`, WinSCP.ini | Senhas armazenadas de forma reversível, mas não um "formato de segredo" |
| `passwords.xlsx` | É um ZIP. Scanners de texto simples veem binário e seguem em frente |
| `.kdbx`, `.pfx`, `id_rsa` | Bytes opacos - o *nome do arquivo* é a descoberta |
| Um `.bak` com uma string de conexão dentro | Binário, então nunca é lido |
o sift cobre esses casos, traz suas próprias regras de chave de fornecedor e **importa
pacotes de regras e descobertas de outras ferramentas** - TOML do gitleaks, YAML do Kingfisher/Titus e
JSON do trufflehog - para que você não esteja escolhendo entre ferramentas.
O trabalho anterior mais próximo é o [Snaffler](https://github.com/SnaffCon/Snaffler), que é
excelente na metade de nomes de arquivo e classificação disso e é a
inspiração direta para as regras de nomes de arquivo. O que ele não tem - e o que acaba
sendo o gargalo real quando você tem 400 ocorrências - é um ciclo de revisão.
---
## O ciclo
1. **Escanee** o compartilhamento.
2. **Trabalhe na fila.** Cada descoberta mostra suas linhas ao redor com a correspondência
destacada. As setas do teclado ampliam o contexto; um clique abre o arquivo inteiro no
VS Code naquela linha, ou no Notepad.
3. **Perceba uma falha.** Você vai. Destaque-a na pré-visualização e pressione `r`.
4. **o sift propõe padrões** e informa ao vivo quantas vezes cada um corresponderia
em tudo que já foi lido.
5. **Salve.** O novo escaneamento em cache leva cerca de um segundo, e as novas ocorrências aparecem
na fila com suas decisões de triagem existentes intactas.
O passo 5 é a parte que faz o resto valer a pena. As descobertas têm como chave
`(path, rule, line, value-hash)`, então um novo escaneamento re-insere as mesmas linhas e seus
status, notas e responsável seguem junto. Sem isso, você teria que revisar as mesmas
300 ocorrências em cada iteração e desistiria na terceira.
---
## Comandos```bash
# size it up first: file count, total bytes, biggest folders, transfer estimates
sift survey \\fileserver\share
# take a rate-limited local copy (resumable; gentle = 5 MB/s by default)
sift copy \\fileserver\share C:\IR\case-4471 --speed gentle
# scan a share and open the triage UI
sift scan \\fileserver\share
# maximum recall: more noise, but a human is reading anyway
sift scan D:\dfs\dept --tier 3
# re-open the UI over the most recent scan
sift ui
# build a share of fabricated credentials, scan it, open the UI
sift demo C:\temp\demoshare
# inherit other tools' vendor-key rules, then use them in the live rescan loop
sift import-rules gitleaks.toml # gitleaks TOML
sift import-rules path/to/kingfisher/data/rules # a directory of YAML
# pull in what the other scanners found, into the same queue
trufflehog filesystem \\fileserver\share --json > th.json
sift import-findings th.json
# hand off to the incident record (redacted unless you say otherwise)
sift export --fmt pdf --status confirmed --out ir-4471.pdf
sift export --fmt csv --out ir-4471.csv
sift export --fmt pdf --no-redact # plaintext; handle as evidence
sift rules # what is loaded
sift selftest # detection tests against a synthetic share
# ask vendors whether confirmed findings are still live. NETWORK. Opt-in.
sift validate --status confirmed
Em qualquer lugar onde sift aparece, você pode usar python -m sift no lugar, a partir do
diretório C:\Dev\sift.
Os achados vão para uma pasta por alvo sob %LOCALAPPDATA%\sift\, nunca para o
diretório de trabalho — o banco de dados contém credenciais em texto puro, e
executar a ferramenta a partir do seu diretório pessoal não deve silenciosamente deixar
uma lá. Cada compartilhamento tem seu próprio armazenamento, então duas investigações
nunca compartilham uma fila de triagem. sift ui sem argumentos reabre o mais
recente; --data DIR substitui.
Regras personalizadas são globais, em %LOCALAPPDATA%\sift\user-rules.json, então um
padrão que você escreve durante uma investigação ajuda no próximo compartilhamento que você
analisar.
Ambos estão no cabeçalho da interface, ao lado da caixa de caminho, e na CLI.
Verificar tamanho é uma varredura somente de stat. Nada é aberto, então é barato até via SMB, e informa a contagem de arquivos, bytes totais, maiores pastas, a divisão por extensão, quanto o sift realmente leria e quanto tempo uma cópia levaria em cada velocidade. Apontar um scanner para uma raiz DFS desconhecida e esperar é como uma tarde desaparece.
Copiar localmente baixa o compartilhamento para uma pasta local primeiro. Vale a pena porque:
A transferência é limitada em taxa, com padrão de 5 MB/s. Saturar o link para um servidor de arquivos de produção às 14h transforma sua investigação em um segundo incidente. Aumente quando você souber que o caminho está ocioso.
| Velocidade | Taxa |
|---|---|
| suave (padrão) | 5 MB/s |
| normal | 25 MB/s |
| rápido | 100 MB/s |
| sem limite | o que o link oferecer |
Transferências retomam: um arquivo de destino com o mesmo tamanho e mtime é ignorado, então uma aquisição interrompida em 80% continua de onde parou. Arquivos que estão bloqueados ou negados são registrados e ignorados em vez de abortar a execução.
PDF, CSV, MD e JSON, a partir do cabeçalho da interface ou sift export --fmt.
O PDF é o que se entrega para um registro de incidente: uma página de capa com o alvo, totais por severidade e estado de triagem, as regras mais frequentes, valores reutilizados entre arquivos e depois os achados agrupados por severidade. Ele é gerado diretamente, sem biblioteca de PDF, então funciona em uma máquina que nunca viu pip.
Exportações são mascaradas por padrão. Valores são mascarados, cada página é
marcada com um banner, e o endpoint só desativa o mascaramento com um redact=0
explícito — uma requisição truncada ou malformada não pode vazar. Desativar o mascaramento
na interface exige confirmar um diálogo de aviso, e o arquivo resultante é
marcado com banner UNREDACTED - CONTAINS PLAINTEXT CREDENTIALS em cada página.
O banco de dados de achados ainda guarda os valores reais, porque um analista precisa saber qual senha vazou para saber o que rotacionar. A fronteira é o que sai da ferramenta.
Tudo é clicável. O controle de ordenação no topo da lista de resultados reordena por severidade, caminho do arquivo, regra, status de triagem, ou mais recente, com um botão para inverter. Os filtros da barra lateral filtram por severidade, categoria, regra e valor reutilizado. Triagem, expansão de contexto, abertura de arquivo e criação de regras são todos botões.
Os atalhos de teclado abaixo são aceleradores para uma fila longa, não a única forma de operá-la.
O botão snapshot renderiza o trecho destacado em um PNG para colar em um ticket de incidente; copiar trecho faz o mesmo como markdown.
A interface renderiza credenciais em texto puro em uma janela do navegador, então:
127.0.0.1 e recusa qualquer outra coisa sem --unsafe-bind;SameSite=Strict;Host, então uma página hostil não pode fazer DNS-rebind para ele;/api/context não é uma leitura arbitrária de arquivo e /api/open não é uma
execução arbitrária de processo.O banco de dados de achados contém credenciais em texto puro por design — um analista
de resposta a incidentes precisa saber qual senha vazou para saber o que rotacionar. Trate
%LOCALAPPDATA%\sift\<target>\findings.db como evidência: mesmo tratamento dado ao
compartilhamento em si, e apague quando a investigação encerrar. Use --redact se ele
for sair do perímetro do incidente.
E o óbvio: execute o sift somente contra sistemas aos quais você está autorizado a acessar.
sift/rules_builtin.py — regras de conteúdo. sift/rules_filename.py — regras
de nome de arquivo. Ambas são Python puro com padrões de raw string, então são legíveis e
passíveis de diff; regras de usuário ficam em JSON em .sift/user-rules.json.
Três níveis permitem trocar precisão por recall:
cpassword
do GPP, dumps NTLM, senhas de bind LDAP. A forma sozinha é prova.Uma regra também pode carregar min_digits, min_lowercase, min_uppercase, e
min_special, então uma única regra barulhenta pode ser apertada sem tocar nas
demais, e examples — strings que ela ainda deve corresponder.
examples é a metade útil. sift selftest executa cada regra contra as
strings para as quais ela foi escrita, por todo o caminho: correspondência, extração, e depois os
filtros de supressão. Esse último passo é o que importa, porque a
regressão que realmente ocorre não é um padrão que deixa de corresponder — é um
filtro de ruído, apertado por bons motivos em outro lugar, silenciosamente engolindo um
achado real no caminho de saída.
Ela se justifica imediatamente. Adicionar exemplos às regras existentes revelou
uma lacuna real: um sublinhado é um caractere de palavra, então o \b inicial na
regra genérica de atribuição se recusava a corresponder dentro de DB_PASSWORD, MYSQL_PASSWORD,
ou REDIS_PASSWORD — três dos nomes de variáveis de credencial mais comuns em
existência, perdidos silenciosamente. O exemplo parecia obviamente correto e não
correspondia, que é exatamente para o que serve.
Regras que você escreve na interface ganham isso de graça: a linha que você selecionou é salva como exemplo da regra, então uma regra que você edita seis meses depois avisa quando ela parou de corresponder àquilo que o fez escrevê-la.
sift import-rules aceita um .toml do gitleaks, um .yml no
estilo Kingfisher/Titus, ou um diretório deles. Contra o pacote do Kingfisher, isso é
1.073 de 1.082 regras importadas, trazendo seus pisos de entropia, requisitos de dígitos e
maiúsculas/minúsculas, e exemplos junto. YAML é lido por sift/yamlmini.py, um leitor para o
subconjunto que esses pacotes usam — sem dependência, e ele levanta erro em âncoras e tags
em vez de fingir que as entende.
Duas coisas são deliberadamente descartadas no caminho de entrada:
validation:, que nomeiam uma URL por regra. O sift só contata
hosts fixos no código em validate.py. Um pacote de regras que pudesse nomear um endpoint
estaria escolhendo para onde seus achados são enviados, e um arquivo de regras é dado, não
uma decisão.[[:alnum:]] é uma classe
POSIX; o Python lê como um conjunto de caracteres literais, compila feliz,
e corresponde à coisa errada. A tradução é verificada contra os exemplos de
cada regra, então um padrão que sobreviveu à compilação mas mudou de significado é
rejeitado em vez de silenciosamente nunca disparar. Doze das regras do Kingfisher falham
nessa verificação e não são importadas.Se a própria supressão do sift descartaria um determinado exemplo não desqualifica
a regra — esses pacotes trazem amostras deliberadamente falsas (keyXXXXXXXX,
...EXAMPLE), então o filtro de espaço reservado está certo sobre o exemplo e nada diz
sobre o padrão. Ser estrito quanto a isso descartou 121 regras funcionais
antes de a distinção ser traçada.
Regras genéricas de segredos são abandonadas por causa do ruído, então os filtros de ruído são ajustados com tanto cuidado quanto os padrões. Medido contra uma árvore real de 6.000 arquivos, as regras de supressão abaixo reduziram os achados de 1.088 para 257 sem perda de recall no corpus de teste:
password: process.env.DB_PASS é uma variável, não
um valor. Essa única supressão remove a maior parte do ruído de regras genéricas em árvores de código-fonte.def login(user: str, password: str) é uma assinatura..wrangler, .next, site-packages,
node_modules, …) além de bundles minificados e source maps são ignorados. Texto
gerado por máquina produz apenas falsos positivos gerados por máquina.Deliberadamente não existe regra de conteúdo https://user:pass@host com uma cauda
solta. A versão óbvia disparou 476 vezes em uma árvore de desenvolvimento, porque JSON minificado
não tem espaços em branco e o padrão percorria de uma URL através de aspas e vírgulas
até encontrar um @ não relacionado centenas de caracteres depois.
python tests/run_all.py
Doze suítes: detecção contra um compartilhamento sintético (dividido em "o que
os scanners de CI já detectam", "a lacuna que esta ferramenta visa" e "iscas que
devem permanecer silenciosas"), a linha de comando, ranqueamento de sugestões de regras, levantamento e
aquisição limitada (incluindo a medição do limite de taxa em relação ao tempo real),
o gerador de PDF (analisado novamente da maneira que um leitor faria, para provar que o expurgo alcançou
o conteúdo da página), os importadores gitleaks/trufflehog, a API HTTP incluindo
cada proteção de segurança, análise estática da interface do usuário, e o leitor de blocos para
arquivos excessivamente grandes (verificando que um segredo no último bloco ainda informa seu número de linha real
no arquivo inteiro).
Para obter um compartilhamento para experimentar:```bash
sift demo C:\temp\demoshare
Toda credencial nesse corpus é fabricada.
Dois níveis, porque carregam riscos muito diferentes.
Checksums são gratuitos e sempre ativos. Tokens ghp_, npm_, Atlassian ATATT,
Bitbucket ATCTT e os glpat- roteáveis do GitLab carregam um CRC32 sobre o
próprio corpo. Recalculá-lo responde offline o que costumava precisar da internet:
isto é um token real, ou o exemplo que alguém colou num README? Achados são
marcados como checksum ok ou malformed na fila. Um checksum prova forma, não
vida — um token bem formado pode ter sido revogado há um ano.
A validação ao vivo fica desligada até você executá-la. sift validate pergunta
ao fornecedor se uma credencial ainda funciona. É um comando separado, não uma
flag em scan, e faz você digitar validate em um prompt que primeiro nomeia cada
endpoint que será contatado. As regras de segurança:
sift/validate.py. Nenhuma regra —
embutida, escrita por usuário ou importada de um pacote de outra pessoa — pode
fornecer uma URL. Sem isso, importar um pacote de regras seria suficiente para
enviar todas as credenciais do compartilhamento para um endereço escolhido pelo
autor do pacote.--redact: se os valores
estão mascarados porque o banco de dados está saindo do perímetro do incidente,
transmitir o texto puro é exatamente o que se está protegendo.validation-log.json ao lado do banco de dados de
achados — provedor, endpoint, horário, resultado — para que o engajamento possa
dizer com precisão o que foi contatado.As tentativas de validação aparecem nos logs de auditoria do proprietário da credencial, atribuídas ao seu endereço, naquele momento. Isso às vezes é exatamente o que você quer e às vezes alerta um adversário que está observando. Decida antes de executar; é por isso que ele pergunta.
Atualmente GitHub, npm, Slack e Stripe. O trufflehog ainda verifica muito mais —
execute-o e use import-findings para obter ambos.
import-findings; acertos verificados ficam no topo..kdbx ou
.pfx é reportado pelo nome; o sift não tenta abri-lo.name=value, sem o que a palavra secreta e seu
valor ficam em colunas separadas e nenhuma regra vê o par..7z, .rar e arquivos aninhados são sinalizados, mas não extraídos. Somente
formatos baseados em ZIP e mensagens .eml são lidos internamente.--max-size são lidos em blocos, não em cache. Eles são
escaneados (uma string de conexão em um .bak de 4 GB é encontrada, em seu
número de linha real), mas o cache de texto guarda apenas o que cabe, então o
reescaneamento em cache de um segundo não os cobre — uma nova regra alcança um
arquivo grande no próximo escaneamento completo.Apache-2.0. Veja LICENSE.
O corpus de teste de detecção (sift/selftest.py, tests/) contém
credenciais com formato correto, porém fabricadas, de propósito; alertas de
varredura de segredos para esses caminhos são suprimidos via
.github/secret_scanning.yml.
| Flag | Efeito |
|---|
--tier 1|2|3 | Controle de recall. 1 = alto sinal, 2 = padrão, 3 = não perca nada |
--redact | Mascara valores no armazenamento e nas exportações. Use se o banco de dados sair do perímetro do incidente |
--no-ui | Preenche o armazenamento e sai, para execuções por script |
--no-browser | Inicia o servidor da interface, mas não abre um navegador (útil via RDP) |
--include/--exclude GLOB | Restringe a varredura |
--no-archives | Não abre contêineres docx/xlsx/zip |
--no-strings | Não executa uma passada de strings em binários |
--no-large | Pula arquivos acima de --max-size em vez de lê-los em blocos |
--jobs N | Processos de trabalho (padrão: automático) |
--max-size MB | Pula arquivos acima disso (padrão 25) |
--port N | Porta da interface (padrão 8973) |
| Tecla | Ação |
|---|
j / k | próximo / achado anterior |
↑ / ↓ | expandir contexto para cima / para baixo |
c / f | confirmar / marcar falso positivo |
x | alternar seleção para triagem em lote |
o / n | abrir no VS Code na linha / abrir o arquivo no Bloco de Notas |
r | criar uma regra a partir do texto destacado |
y | copiar o valor |
/ | pesquisar |
.doc/.xls/.pdf (pré-2007 e PDF) passam por uma passagem de strings em vez
de um parser real, então a recuperação (recall) nesses é menor do que nos
formatos OOXML.