Construindo um pipeline de análise de fraude em tempo real
Passo a passo da arquitetura: ingerindo 50 mil eventos/segundo, enriquecendo com Smart Signals e pontuando risco em menos de 10ms usando nosso motor de streaming.
Processar 50.000 eventos de impressão digital por segundo, enriquecer cada um com Smart Signals e retornar uma pontuação de risco em menos de 10 milissegundos exige uma arquitetura de streaming cuidadosamente projetada. Este artigo percorre nosso pipeline da ingestão à decisão.
Camada de ingestão
Os eventos chegam como requisições HTTPS POST a partir do nosso agente JavaScript rodando nos navegadores dos visitantes. Cada evento contém a carga útil criptografada de sinais — tipicamente 8-12KB de dados comprimidos cobrindo mais de 130 sinais do navegador. Nossos servidores de borda encerram o TLS, validam a assinatura da requisição e encaminham a carga útil ao pipeline de processamento.
Usamos uma implantação multirregião em que os servidores de borda ficam colocalizados com os nós de CDN dos nossos clientes. Isso mantém a viagem de ida e volta da rede abaixo de 20ms para 95% das requisições globalmente. Os servidores de borda são serviços Go sem estado rodando atrás de um balanceador de carga, escalando horizontalmente com base no volume de requisições.
Extração de sinais
O primeiro estágio de processamento descriptografa e faz o parse da carga útil de sinais. Cada sinal é extraído, validado e tipado. Os hashes de canvas são verificados contra valores conhecidamente impossíveis (que indicam bloqueio ou falsificação de canvas). Os parâmetros de WebGL são validados cruzadamente quanto à consistência. As propriedades do Navigator são checadas contra combinações válidas conhecidas.
Este estágio também realiza a normalização de sinais. As strings de user agent são analisadas em componentes estruturados (navegador, versão, SO, dispositivo). As dimensões da tela são normalizadas para levar em conta o escalonamento de DPI. Os offsets de fuso horário são validados contra os dados de geolocalização do IP.
Enriquecimento com Smart Signals
Os sinais extraídos são então enriquecidos com a análise dos Smart Signals — nossa camada de inteligência do lado do servidor. Isso inclui detecção de janela anônima (comparando padrões de sinais contra assinaturas conhecidas de navegação privada), detecção de VPN (cruzando dados de IP com sinais de fuso horário e localidade), detecção de adulteração do navegador (identificando inconsistências que indicam falsificação de sinais) e detecção de máquina virtual (reconhecendo perfis de hardware associados a VMware, VirtualBox e VMs de nuvem).
Cada smart signal é computado de forma independente e produz tanto um resultado booleano quanto uma pontuação de confiança. O estágio de enriquecimento adiciona 24 sinais adicionais a cada evento, fornecendo uma avaliação abrangente de ameaças que vai além do que a coleta do lado do cliente sozinha pode alcançar.
Motor de pontuação de risco
O evento enriquecido é passado ao nosso motor de pontuação de risco — um modelo de árvore de decisão com gradient boosting treinado em milhões de eventos rotulados. O modelo considera todos os mais de 130 sinais brutos, os 24 smart signals e vários recursos derivados: métricas de velocidade (quantos eventos deste dispositivo nos últimos 5 minutos, 1 hora e 24 horas), padrões de comportamento histórico e pontuações de reputação de rede.
O modelo produz uma pontuação de risco entre 0 e 100, junto com os principais fatores contribuintes. Uma pontuação de 85, por exemplo, pode vir acompanhada de fatores como "VPN detectada", "modo anônimo" e "alta velocidade — 47 eventos em 5 minutos". Essa explicabilidade é fundamental para os analistas de fraude que precisam entender por que um evento em particular foi sinalizado.
Camada de armazenamento e consulta
Todos os eventos são persistidos no ClickHouse — um banco de dados colunar otimizado para consultas analíticas sobre grandes conjuntos de dados. O ClickHouse lida com nosso volume de escrita (50 mil eventos/segundo) sem esforço, e seu armazenamento colunar viabiliza consultas analíticas em menos de um segundo sobre bilhões de linhas.
Usamos uma estratégia de retenção em múltiplas camadas. Dados quentes (últimos 7 dias) ficam em SSDs NVMe para resposta de consulta em menos de 100ms. Dados mornos (7-90 dias) ficam em SSDs padrão. Dados frios (90+ dias) são comprimidos e movidos para o armazenamento de objetos, consultáveis, mas com maior latência.
Kafka como espinha dorsal
O Apache Kafka amarra o pipeline. Cada estágio lê de e escreve em tópicos do Kafka. A camada de ingestão escreve eventos brutos. O estágio de extração de sinais lê eventos brutos e escreve eventos extraídos. O estágio de enriquecimento com Smart Signals lê eventos extraídos e escreve eventos enriquecidos. O motor de pontuação de risco lê eventos enriquecidos e escreve eventos pontuados.
Essa arquitetura oferece várias vantagens: os estágios podem ser escalados de forma independente, falhas em um estágio não afetam os outros, e podemos reprocessar eventos por qualquer estágio para depuração ou reprocessamento. Os consumer groups do Kafka viabilizam o processamento paralelo dentro de cada estágio, e sua semântica exactly-once garante que nenhum evento seja processado duas vezes ou perdido.
Orçamento de latência
Nossa meta de latência de ponta a ponta é de 10ms, do momento em que a carga útil de sinais enriquecidos chega ao pipeline de processamento até o momento em que a pontuação de risco é retornada. Veja como o orçamento se distribui: a extração de sinais leva 1-2ms, o enriquecimento com Smart Signals leva 3-4ms, a pontuação de risco leva 2-3ms, e a serialização e resposta levam 1-2ms. O salto do Kafka entre estágios adiciona menos de 1ms em nossa implantação colocalizada.
Cumprir esse orçamento de forma consistente a 50 mil eventos/segundo exige otimização cuidadosa em cada estágio. Usamos pools de memória pré-alocados, serialização zero-copy e escritas em lote no ClickHouse. O modelo de pontuação de risco é compilado para código nativo usando o ONNX Runtime, eliminando a sobrecarga do interpretador Python.
Mark passou duas semanas perfilando o pipeline antes de encontrar o gargalo em nossa camada de consulta distribuída — um único mutex estava serializando as consultas em todas as goroutines. Após mudar para um design de lock fragmentado, o p99 caiu de 48ms para 9ms. Às vezes a correção é constrangedoramente simples, uma vez que você a encontra.