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.

24 Aug 2026 11 min de leitura Equipe Python Dev BR

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:

  1. dataclass(frozen=True) cria um registro legível e evita alteração acidental depois da validação;
  2. datetime.fromisoformat() converte a data para um tipo adequado a comparações;
  3. o leitor é um gerador, portanto processa uma linha por vez;
  4. 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?

FormatoVantagemLimitação
JSON Linescampos explícitos e leitura incrementalarquivos maiores que CSV em alguns cenários
CSVsimples para tabela e planilhatipos, delimitadores e quebras exigem cuidado
Texto livrecomum em sistemas antigosparsing frágil e dependente do formato
Syslog estruturadopadrão conhecido em operaçõesvariaçõ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:

  1. aceitar origem e destino por argumentos com argparse;
  2. colocar registros inválidos em quarentena;
  3. produzir métricas por sistema e resultado;
  4. gerar relatório Markdown para leitura humana;
  5. adicionar configuração TOML para limite e janela;
  6. testar arquivos grandes com geradores;
  7. criar uma API local para receber eventos sintéticos;
  8. construir dashboard sobre dados fictícios;
  9. empacotar o projeto com pyproject.toml;
  10. 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.

E
Equipe Python Dev BR

Contribuidor do Python Dev BR

Artigos relacionados