Python para SOC: análise de logs e alertas defensivos
Aprenda a analisar logs com Python para SOC: leia JSON Lines, normalize eventos, detecte falhas repetidas e gere alertas defensivos com testes na prática.
Python para SOC é útil para transformar logs brutos em eventos consistentes, contar padrões e gerar alertas que uma pessoa analista possa revisar. Um bom projeto inicial lê dados sintéticos em JSON Lines, valida campos, agrupa falhas de autenticação dentro de uma janela de tempo e exporta um relatório. A regra não “detecta ataques” sozinha: ela produz um sinal defensivo que precisa de contexto, investigação e procedimentos definidos pela organização.
Neste tutorial, você vai construir esse fluxo apenas com a biblioteca padrão do Python e pytest. O exemplo não faz varredura, exploração, coleta de terceiros nem conexão com sistemas reais. Essa abordagem é adequada para estudar, montar portfólio e se preparar para estágio em segurança da informação com Python sem ultrapassar limites éticos.
O que é SOC e onde Python entra
SOC é a sigla de Security Operations Center, ou Centro de Operações de Segurança. Dependendo da empresa, a equipe acompanha alertas, investiga eventos, documenta incidentes, administra processos de detecção e trabalha com ferramentas como SIEM, EDR, plataformas de identidade, serviços de nuvem e sistemas de chamados.
Python aparece como ferramenta de apoio em tarefas como:
- converter logs entre formatos;
- validar campos antes da ingestão;
- remover duplicidades;
- agrupar eventos por usuário, IP, ativo ou período;
- enriquecer indicadores por meio de APIs internas e autorizadas;
- gerar relatórios Markdown, CSV ou JSON;
- testar regras de detecção com dados conhecidos;
- automatizar etapas repetitivas de um playbook;
- conferir se um lote contém segredos ou dados indevidos antes do compartilhamento.
Em um ambiente profissional, essas automações devem respeitar controle de acesso, retenção, privacidade, revisão de código e gestão de mudanças. O script deste artigo é um laboratório. Ele ensina o raciocínio, mas não deve ser conectado à produção sem revisão da equipe responsável.
Se seu objetivo é carreira, veja também Python para cibersegurança júnior. O guia explica as trilhas de SOC, automação, resposta a incidentes e portfólio; aqui, o foco é a implementação técnica.
Arquitetura do projeto
Vamos criar um analisador pequeno com esta estrutura:
soc-logs-python/
├── dados/
│ └── autenticacao.jsonl
├── src/
│ ├── __init__.py
│ └── analisador.py
├── tests/
│ └── test_analisador.py
└── README.md
O arquivo usa JSON Lines (.jsonl): cada linha contém um objeto JSON independente. Isso permite processar o arquivo aos poucos, sem carregar tudo na memória, e facilita identificar qual linha está inválida.
Crie o ambiente e instale somente a dependência de desenvolvimento:
python -m venv .venv
source .venv/bin/activate
python -m pip install pytest
No PowerShell:
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install pytest
Você também pode usar o uv para gerenciar o projeto, mas a lógica do analisador será a mesma.
Criando logs sintéticos e seguros
Salve em dados/autenticacao.jsonl:
{"timestamp":"2026-08-24T09:00:00+00:00","usuario":"ana","ip":"192.0.2.10","resultado":"falha","sistema":"portal"}
{"timestamp":"2026-08-24T09:00:35+00:00","usuario":"ana","ip":"192.0.2.10","resultado":"falha","sistema":"portal"}
{"timestamp":"2026-08-24T09:01:10+00:00","usuario":"ana","ip":"192.0.2.10","resultado":"falha","sistema":"portal"}
{"timestamp":"2026-08-24T09:01:42+00:00","usuario":"ana","ip":"192.0.2.10","resultado":"falha","sistema":"portal"}
{"timestamp":"2026-08-24T09:02:05+00:00","usuario":"ana","ip":"192.0.2.10","resultado":"sucesso","sistema":"portal"}
{"timestamp":"2026-08-24T09:04:00+00:00","usuario":"bruno","ip":"198.51.100.8","resultado":"sucesso","sistema":"vpn"}
Os blocos 192.0.2.0/24 e 198.51.100.0/24 são reservados para documentação. Os nomes e sistemas são fictícios. Em um repositório público, não troque esse conjunto por logs da empresa em que você trabalha ou estagia.
Logs reais podem conter e-mail, CPF, IP interno, hostname, caminho de arquivo, identificador de sessão, token e outras informações sensíveis. “Remover a senha” não torna automaticamente um log publicável. Use dados produzidos especificamente para o laboratório.
Modelando e validando cada evento
Crie src/analisador.py:
from __future__ import annotations
import json
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Iterator
RESULTADOS_VALIDOS = {"sucesso", "falha"}
@dataclass(frozen=True)
class EventoAutenticacao:
timestamp: datetime
usuario: str
ip: str
resultado: str
sistema: str
def converter_evento(dados: dict) -> EventoAutenticacao:
campos = {"timestamp", "usuario", "ip", "resultado", "sistema"}
ausentes = campos - dados.keys()
if ausentes:
nomes = ", ".join(sorted(ausentes))
raise ValueError(f"Campos ausentes: {nomes}")
resultado = str(dados["resultado"]).strip().lower()
if resultado not in RESULTADOS_VALIDOS:
raise ValueError(f"Resultado inválido: {resultado}")
return EventoAutenticacao(
timestamp=datetime.fromisoformat(str(dados["timestamp"])),
usuario=str(dados["usuario"]).strip(),
ip=str(dados["ip"]).strip(),
resultado=resultado,
sistema=str(dados["sistema"]).strip(),
)
def ler_eventos(caminho: Path) -> Iterator[EventoAutenticacao]:
with caminho.open(encoding="utf-8") as arquivo:
for numero_linha, linha in enumerate(arquivo, start=1):
if not linha.strip():
continue
try:
dados = json.loads(linha)
yield converter_evento(dados)
except (json.JSONDecodeError, TypeError, ValueError) as erro:
raise ValueError(
f"Evento inválido na linha {numero_linha}: {erro}"
) from erro
Há quatro escolhas importantes nesse código:
dataclass(frozen=True)cria um registro legível e evita alteração acidental depois da validação;datetime.fromisoformat()converte a data para um tipo adequado a comparações;- o leitor é um gerador, portanto processa uma linha por vez;
- a mensagem de erro inclui o número da linha, facilitando a correção do lote.
Em produção, você talvez envie registros inválidos para uma quarentena em vez de interromper todo o processamento. Para o primeiro projeto, falhar de forma explícita é melhor do que ignorar silenciosamente um evento ruim. Leia tratamento de erros em Python para comparar estratégias.
Regra defensiva: falhas repetidas em uma janela
Uma quantidade alta de falhas do mesmo usuário e IP em poucos minutos merece triagem. Porém, o limite é apenas uma hipótese. Pode ser senha expirada, aplicativo antigo, serviço mal configurado ou atividade maliciosa.
Adicione ao mesmo arquivo:
from collections import defaultdict, deque
from dataclasses import asdict
from datetime import timedelta
@dataclass(frozen=True)
class Alerta:
regra: str
usuario: str
ip: str
sistema: str
inicio: datetime
fim: datetime
total_falhas: int
def detectar_falhas_repetidas(
eventos: Iterator[EventoAutenticacao],
limite: int = 4,
janela: timedelta = timedelta(minutes=5),
) -> list[Alerta]:
historico: dict[
tuple[str, str, str],
deque[datetime],
] = defaultdict(deque)
alertas: list[Alerta] = []
for evento in sorted(eventos, key=lambda item: item.timestamp):
if evento.resultado != "falha":
continue
chave = (evento.usuario, evento.ip, evento.sistema)
ocorrencias = historico[chave]
inicio_minimo = evento.timestamp - janela
while ocorrencias and ocorrencias[0] < inicio_minimo:
ocorrencias.popleft()
ocorrencias.append(evento.timestamp)
if len(ocorrencias) == limite:
alertas.append(
Alerta(
regra="falhas_repetidas_autenticacao",
usuario=evento.usuario,
ip=evento.ip,
sistema=evento.sistema,
inicio=ocorrencias[0],
fim=ocorrencias[-1],
total_falhas=len(ocorrencias),
)
)
return alertas
A deque guarda apenas os horários relevantes para cada combinação de usuário, IP e sistema. Antes de adicionar o evento atual, o código remove ocorrências antigas que ficaram fora da janela de cinco minutos.
A condição len(ocorrencias) == limite, em vez de >=, evita gerar um alerta novo a cada falha posterior da mesma sequência. Uma implementação real precisaria definir reabertura, supressão, severidade e identificação única do alerta.
O que essa regra não prova
Ela não prova que houve brute force, comprometimento ou incidente. Também não considera:
- IPs compartilhados por NAT ou proxy;
- contas de serviço;
- mudança recente de senha;
- falha de integração;
- autenticação multifator;
- perfil histórico do usuário;
- criticidade do sistema;
- lista de origens conhecidas;
- sucesso posterior e atividade da sessão.
Esse trecho é importante no README e na entrevista. Segurança defensiva exige distinguir sinal, evidência e conclusão.
Exportando um relatório JSON
Adicione:
def serializar_alerta(alerta: Alerta) -> dict:
dados = asdict(alerta)
dados["inicio"] = alerta.inicio.isoformat()
dados["fim"] = alerta.fim.isoformat()
return dados
def salvar_relatorio(alertas: list[Alerta], destino: Path) -> None:
conteudo = {
"gerado_em": datetime.now().astimezone().isoformat(),
"total_alertas": len(alertas),
"alertas": [serializar_alerta(alerta) for alerta in alertas],
}
destino.write_text(
json.dumps(conteudo, ensure_ascii=False, indent=2),
encoding="utf-8",
)
E um ponto de entrada:
if __name__ == "__main__":
origem = Path("dados/autenticacao.jsonl")
destino = Path("relatorio_alertas.json")
eventos = ler_eventos(origem)
alertas = detectar_falhas_repetidas(eventos)
salvar_relatorio(alertas, destino)
print(f"Relatório salvo em {destino} com {len(alertas)} alerta(s).")
Execute:
python -m src.analisador
A saída esperada terá um alerta para ana, com quatro falhas dentro da janela. O ensure_ascii=False mantém acentos legíveis. Em um sistema corporativo, o relatório precisaria de política de retenção, acesso restrito e sanitização de campos; não salve indiscriminadamente dados de autenticação em diretórios públicos.
Para projetos maiores, use logging em Python em vez de depender apenas de print, mas evite registrar tokens, cookies e payloads sensíveis.
Escrevendo testes com pytest
Crie tests/test_analisador.py:
from datetime import datetime, timedelta, timezone
import pytest
from src.analisador import (
EventoAutenticacao,
converter_evento,
detectar_falhas_repetidas,
)
def evento(minuto: int, resultado: str = "falha") -> EventoAutenticacao:
return EventoAutenticacao(
timestamp=datetime(2026, 8, 24, 9, minuto, tzinfo=timezone.utc),
usuario="ana",
ip="192.0.2.10",
resultado=resultado,
sistema="portal",
)
def test_detecta_quatro_falhas_em_cinco_minutos() -> None:
eventos = [evento(0), evento(1), evento(2), evento(3)]
alertas = detectar_falhas_repetidas(
iter(eventos),
limite=4,
janela=timedelta(minutes=5),
)
assert len(alertas) == 1
assert alertas[0].total_falhas == 4
assert alertas[0].usuario == "ana"
def test_nao_detecta_eventos_fora_da_janela() -> None:
eventos = [evento(0), evento(2), evento(4), evento(8)]
alertas = detectar_falhas_repetidas(
iter(eventos),
limite=4,
janela=timedelta(minutes=5),
)
assert alertas == []
def test_ignora_autenticacao_com_sucesso() -> None:
eventos = [evento(0), evento(1), evento(2), evento(3, "sucesso")]
assert detectar_falhas_repetidas(iter(eventos), limite=4) == []
def test_rejeita_campo_obrigatorio_ausente() -> None:
dados = {
"timestamp": "2026-08-24T09:00:00+00:00",
"usuario": "ana",
"resultado": "falha",
"sistema": "portal",
}
with pytest.raises(ValueError, match="ip"):
converter_evento(dados)
Rode:
pytest -q
Os testes mostram mais do que domínio de sintaxe. Eles documentam o significado da regra: quantidade, janela, comportamento diante de sucesso e validação de entrada. Em um teste técnico, essa clareza costuma valer mais do que adicionar muitas funcionalidades sem cobertura.
Aprofunde fixtures e parametrização no guia de testes com pytest.
Como reduzir falsos positivos
Uma regra inicial deve ser simples, mas você pode evoluí-la com cuidado:
Separar conta humana de conta de serviço
Contas técnicas podem falhar repetidamente por segredo expirado. Em vez de ignorá-las, use uma regra e um fluxo de atendimento específicos.
Considerar o sistema afetado
Quatro falhas em um portal de treinamento e quatro falhas em uma conta administrativa não têm a mesma prioridade. Criticidade é contexto, não propriedade do IP.
Correlacionar sucesso após falhas
Um sucesso depois de várias falhas pode merecer revisão, mas também pode ser apenas a pessoa finalmente digitando a senha correta. Combine com origem incomum, horário, dispositivo e política de identidade.
Aplicar supressão
Se a causa já está em investigação, gerar centenas de alertas iguais aumenta ruído. Uma chave de deduplicação e um período de supressão ajudam, desde que não escondam mudança relevante.
Registrar a justificativa
Inclua no alerta os campos que explicam por que ele foi criado: regra, período, total, usuário, origem e sistema. Uma pontuação misteriosa sem evidência dificulta a triagem.
O objetivo não é zerar falsos positivos. É produzir sinais úteis, explicáveis e compatíveis com a capacidade de investigação do time.
JSON Lines, CSV ou texto livre?
| Formato | Vantagem | Limitação |
|---|---|---|
| JSON Lines | campos explícitos e leitura incremental | arquivos maiores que CSV em alguns cenários |
| CSV | simples para tabela e planilha | tipos, delimitadores e quebras exigem cuidado |
| Texto livre | comum em sistemas antigos | parsing frágil e dependente do formato |
| Syslog estruturado | padrão conhecido em operações | variações entre produtores e transporte |
Para portfólio, JSON Lines facilita validação e demonstra uso real de JSON com Python. Se quiser praticar dados tabulares, adapte o projeto para CSV com o módulo csv. Evite começar por regex complexa sobre texto livre: primeiro entregue um pipeline confiável com formato conhecido.
Como transformar o projeto em portfólio
Um repositório convincente deve conter:
- descrição do problema;
- aviso de dados totalmente sintéticos;
- instruções de instalação e execução;
- exemplo de entrada e saída;
- explicação da janela e do limite;
- testes automatizados;
- seção de falsos positivos;
- limitações do projeto;
- próximos passos realistas;
- licença e escopo de uso defensivo;
- histórico de commits compreensível.
Uma boa descrição para currículo seria:
Desenvolvi analisador defensivo de logs em Python com leitura incremental de JSON Lines, validação de eventos, correlação de falhas por janela temporal, relatório JSON e testes com pytest. Usei somente dados sintéticos e documentei falsos positivos e limites da regra.
Essa frase é mais forte do que “criei detector de hackers”, porque descreve a entrega sem fazer uma alegação que o código não sustenta.
Para organizar o repositório, consulte projetos de portfólio Python e o modelo de currículo para vaga júnior.
Próximas evoluções seguras
Depois da primeira versão, você pode:
- aceitar origem e destino por argumentos com
argparse; - colocar registros inválidos em quarentena;
- produzir métricas por sistema e resultado;
- gerar relatório Markdown para leitura humana;
- adicionar configuração TOML para limite e janela;
- testar arquivos grandes com geradores;
- criar uma API local para receber eventos sintéticos;
- construir dashboard sobre dados fictícios;
- empacotar o projeto com
pyproject.toml; - executar testes e lint no GitHub Actions.
Se integrar uma API autorizada, configure timeout, retry e limite de chamadas. O guia de HTTPX moderno mostra esses padrões. Nunca inclua chaves no código ou no repositório; para geração de valores seguros, veja o módulo secrets.
Evite transformar a evolução em scanner de terceiros, coletor de vazamentos ou ferramenta de tentativa de senha. Há muito espaço para aprender Blue Team com dados sintéticos, laboratórios próprios e plataformas educacionais autorizadas.
Perguntas frequentes
Como Python é usado em um SOC?
Python normaliza e correlaciona eventos, integra APIs permitidas, gera relatórios e automatiza partes repetitivas da triagem. A linguagem apoia o trabalho; SIEM, EDR, processos, conhecimento do ambiente e análise humana continuam essenciais.
Qual formato de log é melhor para analisar com Python?
JSON Lines é uma ótima escolha didática por manter um objeto por linha e permitir processamento incremental. CSV funciona bem para tabelas. Texto livre é comum, porém precisa de parsing mais cuidadoso e testes contra mudanças de formato.
Contar falhas de login detecta um ataque?
Não. A contagem encontra um padrão que merece contexto. Senha esquecida, integração quebrada, NAT e atividade maliciosa podem produzir sinais parecidos. Trate o resultado como alerta para triagem, não como veredito.
Posso publicar logs reais no meu portfólio?
Não publique dados corporativos, pessoais ou internos sem autorização formal e processo adequado. Para GitHub público, produza um dataset sintético e use endereços reservados para documentação.
Esse projeto ajuda a conseguir estágio em segurança da informação?
Sim. Ele demonstra arquivos, JSON, datas, estruturas de dados, testes e pensamento defensivo. Para gerar valor na candidatura, documente como executar, por que escolheu a regra e quais situações podem gerar falsos positivos.
Conclusão
Analisar logs com Python é um projeto acessível e relevante para quem quer trabalhar em SOC ou começar em segurança da informação. O fluxo fundamental é: receber dados conhecidos, validar cada evento, aplicar uma regra explicável, gerar um relatório e testar o comportamento esperado.
A principal lição não é o limite de quatro falhas. É aprender a não confundir automação com certeza. Uma regra defensiva gera evidência para investigação; seu valor depende da qualidade dos dados, do contexto e da forma como a equipe responde.
Implemente primeiro a versão com JSON Lines e testes. Depois, publique o repositório com dados sintéticos e uma seção honesta de limitações. Continue pelo guia de estágio em segurança da informação, revise Python para cibersegurança júnior e acompanhe as vagas de estágio para comparar seu projeto com os requisitos que aparecem no mercado brasileiro.