Pular para o conteúdo
Novo

DeepSeek V4.1 Flash grátis por 7 dias: 10M tokens por dia após qualquer recarga. Até 30/11.

Como funciona

Noções básicas do OWASP ZAP contra o seu Juice Shop local

Rode a varredura passiva do ZAP contra o Juice Shop na sua máquina, leia o relatório e confirme um alerta à mão antes de confiar nele.

Iniciante13 min de leituraPor NotHoly

O que é o ZAP e onde ele fica

O Zed Attack Proxy, normalmente chamado de ZAP, é uma ferramenta gratuita para encontrar problemas em aplicações web. Ele faz duas coisas que importam para este guia. Funciona como um proxy interceptador, então você consegue ler cada requisição que o navegador envia e cada resposta que ele recebe. Também roda scanners que verificam esse tráfego em busca de padrões fracos.

Um proxy é um intermediário. Normalmente, o navegador manda a requisição direto para 127.0.0.1:3000. Quando ela passa pelo ZAP, o navegador envia a requisição para o ZAP. O ZAP encaminha a requisição para a aplicação, recebe a resposta, guarda o par no histórico e devolve a resposta ao navegador. A aplicação não percebe nada diferente. Você só fica com uma cópia da conversa.

Essa posição é o que torna a ferramenta útil. Um scanner vê o que o servidor devolve. Um proxy mostra o que o navegador realmente fez, incluindo cabeçalhos, cookies, redirecionamentos e o JavaScript que a página carregou. Muitos problemas web só aparecem quando você compara essas duas visões.

Varredura passiva e ativa

O ZAP tem dois tipos de varredura, e a diferença define o que você pode executar e onde.

A varredura passiva lê o tráfego que já passou pelo ZAP. Cada resposta é verificada em busca de coisas como o cabeçalho Content-Security-Policy ausente, cookies sem a flag HttpOnly ou rastros de pilha em páginas de erro. As regras passivas não enviam strings de ataque. Elas só olham o que voltou.

A varredura ativa envia requisições novas, montadas para provocar uma reação. Ela altera parâmetros, envia entradas incomuns e observa como a aplicação responde. A maioria dos achados sérios vem desse tipo de teste, e também é o tipo que se comporta como um ataque. Execute-a só contra um laboratório seu, ou contra um sistema em que você tem permissão por escrito e um escopo definido.

A varredura de linha de base deste guia é passiva. A documentação do ZAP a descreve como um script que roda o spider contra o alvo por um minuto, por padrão, espera as regras passivas terminarem e depois gera o relatório. Ela não executa cargas de ataque, por isso é um primeiro passo razoável no seu laboratório.

Por que um alerta é uma pista, não uma prova

Cada alerta do ZAP é uma regra que bateu. Uma regra pode encontrar um cabeçalho ausente, um padrão no corpo da resposta, ou uma combinação dos dois. Ela não sabe para que serve a sua aplicação, quais dados ela protege, nem se um atacante consegue chegar ao caminho que importa. Um alerta diz onde olhar. Ele não confirma que uma fraqueza pode ser explorada.

Dois tipos de erro aparecem o tempo todo. Alguns alertas são falsos positivos: a regra encontrou algo inofensivo. Outros problemas reais passam despercebidos, porque o scanner nunca chegou à página ou nunca enviou a entrada que dispara a falha. O que transforma uma pista em achado é um passo que você consegue repetir: uma requisição que você enviou, a resposta que viu e uma explicação simples de por que aquilo importa. Enquanto não tiver as três coisas, o alerta fica numa lista marcada como "a verificar".

Guarde essa regra durante o resto do guia. O scanner dá um ponto de partida, e o trabalho de verificação é seu.

Antes de começar

Você precisa de quatro coisas:

  • Docker. O Docker Desktop no Windows ou no macOS funciona, e o Docker Engine no Linux também. Confirme com docker version.
  • Uma máquina que seja sua, onde você roda apenas o laboratório descrito aqui.
  • Um terminal. Os exemplos usam um shell no padrão POSIX. No Windows, eu rodei os comandos no Git Bash.
  • Um navegador, se quiser a etapa opcional com o ZAP desktop.

O laboratório fica na sua máquina. Publique a porta somente em 127.0.0.1. Uma porta publicada sem esse prefixo pode escutar em todas as interfaces de rede, e o Juice Shop foi feito para ser quebrado.

Passo 1: subir o laboratório

Inicie o Juice Shop em segundo plano, com um nome que você possa usar depois:

bash
docker run -d --name juice-local -p 127.0.0.1:3000:3000 bkimminich/juice-shop

A flag -d executa o contêiner em segundo plano. Deixei o --rm de fora de propósito, para o contêiner continuar existindo até você pará-lo e removê-lo pelo nome. O prefixo 127.0.0.1: mantém a porta fora da sua rede.

Confirme que ele responde:

bash
curl -i http://127.0.0.1:3000/

Na minha máquina, as primeiras linhas da resposta foram estas. Removi a barra de progresso que o curl mostra quando não é usado com -s:

text
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Feature-Policy: payment 'self'
X-Recruiting: /#/jobs
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sun, 08 Nov 2026 09:06:18 GMT
ETag: W/"2700-1a1ba6896a5"
Content-Type: text/html; charset=UTF-8
Content-Length: 9984

Os seus cabeçalhos e datas serão diferentes. Anote também a versão do Juice Shop, porque a aplicação muda entre as releases. Este endpoint informa a versão:

bash
curl -s http://127.0.0.1:3000/rest/admin/application-version

Na minha máquina, ele imprimiu {"version":"20.3.0"}. Guarde esse número nas suas anotações, junto dos resultados da varredura.

Passo 2: rodar a varredura de linha de base

Execute o scanner a partir da pasta onde quer que o relatório apareça. Este é o comando para o Docker Desktop:

bash
docker run --rm -t -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

Cada parte tem uma função:

  • --rm remove o contêiner do scanner quando a varredura termina.
  • -v "$(pwd)":/zap/wrk monta a pasta atual dentro do contêiner, para o relatório ser gravado no seu disco.
  • -t http://host.docker.internal:3000 é o alvo. De dentro de um contêiner, host.docker.internal aponta para o seu computador, onde o laboratório escuta.
  • -r zap-report.html grava o relatório HTML completo na pasta montada.

No Linux, o nome host.docker.internal não existe por padrão no Docker. Adicione um mapeamento de host ao mesmo comando:

bash
docker run --rm -t --add-host=host.docker.internal:host-gateway -v "$(pwd)":/zap/wrk ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

Testei apenas a forma do Docker Desktop. No Linux, uma porta publicada em 127.0.0.1 pode não aceitar conexões que chegam pelo endereço da ponte do Docker. Se o scanner mostrar erros de conexão, leia as primeiras linhas da saída antes de mudar qualquer outra coisa.

Dois problemas que encontrei

A primeira tentativa falhou sem varrer nada. O console mostrou A file based option has been specified but the directory '/zap/wrk' is not mounted, depois imprimiu o texto de uso e saiu com código 3. A montagem não funcionou na minha sessão do Git Bash, e não descobri o motivo. Rodar o comando com MSYS_NO_PATHCONV=1 e o caminho completo do Windows na flag -v resolveu:

bash
MSYS_NO_PATHCONV=1 docker run --rm -t -v "D:/caminho/para/sua/pasta:/zap/wrk" ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t http://host.docker.internal:3000 -r zap-report.html

Troque o caminho pela sua pasta. A primeira execução também baixa a imagem do ZAP, e na minha conexão isso levou quase doze minutos. As execuções seguintes começam em segundos.

Lendo a saída do console

O scanner imprime uma linha por regra. Esta é a parte que importa, da minha execução, com a longa lista de regras aprovadas encurtada:

text
Using the Automation Framework
Total of 116 URLs
PASS: Vulnerable JS Library (Powered by Retire.js) [10003]
PASS: Cookie No HttpOnly Flag [10010]
...
PASS: CSP [10055]
...
WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 5
WARN-NEW: Cross-Domain Misconfiguration [10098] x 5
WARN-NEW: Modern Web Application [10109] x 5
FAIL-NEW: 0	FAIL-INPROG: 0	WARN-NEW: 3	WARN-INPROG: 0	INFO: 0	IGNORE: 0	PASS: 59

WARN-NEW quer dizer que uma regra sinalizou algo que a varredura ainda não tinha visto. PASS quer dizer que a regra rodou e não encontrou nada. O número depois do nome da regra é o identificador dela no ZAP, que você pode pesquisar na documentação.

O comando saiu com código 2 na minha execução. A documentação da linha de base lista os códigos de saída: 0 significa sucesso, 1 significa pelo menos uma falha (FAIL), 2 significa pelo menos um aviso (WARN) e nenhuma falha, e 3 significa qualquer outro erro. Código 2 é o esperado aqui. Ele só quer dizer que o scanner encontrou algo para você ler.

Passo 3: ler o relatório

Abra o zap-report.html no navegador. O resumo no topo do meu relatório mostrou High 0, Medium 2, Low 0 e Informational 1. Três alertas formaram a lista inteira:

  • Content Security Policy (CSP) Header Not Set, Medium. A resposta não envia o cabeçalho Content-Security-Policy, que informa ao navegador de quais fontes ele pode carregar scripts e frames.
  • Cross-Domain Misconfiguration, Medium. A resposta inclui Access-Control-Allow-Origin: *, o que permite que qualquer site leia a resposta no navegador.
  • Modern Web Application, Informational. O ZAP percebeu que o site se comporta como uma aplicação de página única. Isso descreve a aplicação. Não é um defeito.

Todo alerta tem as mesmas partes: uma descrição, as URLs onde disparou, a evidência que o ZAP viu, algumas notas e uma sugestão de correção. Leia a evidência e a URL primeiro. A descrição explica o que a regra significa em geral. A evidência mostra o que aconteceu nesta resposta.

Não ordene o relatório só pelo nível de risco. O ZAP atribui o nível a partir da regra, não a partir dos dados da sua aplicação. Um Medium numa página sem dados sensíveis pode importar menos do que um Low num endpoint que trata contas.

Passo 4: verificar um alerta à mão

Escolha o alerta de CSP. A evidência dele é uma ausência, então o teste é simples: peça a página, procure o cabeçalho e registre o resultado. Este comando imprime o número de linhas que batem:

bash
curl -s -i http://127.0.0.1:3000/ -o /dev/null -D - | grep -i -c '^content-security-policy'

Na minha máquina, ele imprimiu 0. A lista completa de cabeçalhos que capturei antes também não tinha nenhuma linha Content-Security-Policy. O alerta bate com o que o servidor envia.

Agora o segundo alerta. A evidência é a linha Access-Control-Allow-Origin: * que você já viu na saída do curl -i. A sua verificação precisa responder a outra pergunta: essa resposta contém algo que um site de terceiros não deveria ler? A página raiz do laboratório é um HTML público, então o cabeçalho em si não é o problema. O texto do ZAP faz o mesmo ponto. Os navegadores não deixam uma página de terceiros ler respostas autenticadas desse jeito. O alerta fica grave só quando um endpoint que devolve dados privados também envia uma origem curinga. Se você encontrar um endpoint assim, teste com uma conta do seu laboratório e registre o que ele devolve.

O terceiro alerta não tem nada a provar. Ele descreve o tipo de aplicação. Deixe-o no relatório como contexto e siga em frente.

Registre cada resultado verificado numa tabela ou num arquivo de texto com quatro colunas: o alerta, a requisição que você enviou, o que observou e o seu veredito. O veredito pode ser "confirmado", "não reproduzido" ou "precisa de mais contexto". Essas três respostas cobrem a maior parte do que você fará com a saída de um scanner.

Passo 5: passar o navegador pelo ZAP

Esta etapa permite observar requisições reais, e é ela que torna o proxy útil além da varredura. Você pode fazer com o aplicativo desktop ou com um contêiner rodando o ZAP em modo daemon. Descrevo as duas. Eu rodei a versão daemon. Não rodei o passo a passo do desktop, então não há saída dele abaixo.

Versão desktop (não executada por mim)

Instale o ZAP pelo site oficial. A página de primeiros passos traz o instalador atual. Abra o programa e escolha Manual Explore, que inicia um navegador que já envia o tráfego pelo ZAP. Digite http://127.0.0.1:3000 e navegue pela página inicial. Depois abra a aba History. Cada linha é um par de requisição e resposta, com o método, a URL e o código de status. Compare essas linhas com o que aparece nas ferramentas de desenvolvedor do seu próprio navegador. As diferenças mostram o que o navegador enviou e que você não esperava.

Versão daemon (o que eu rodei)

Inicie o ZAP como proxy em segundo plano, na porta 8080:

bash
docker run -d --name zap-proxy-lab -p 127.0.0.1:8080:8080 ghcr.io/zaproxy/zaproxy:stable zap.sh -daemon -host 0.0.0.0 -port 8080 -config api.disablekey=true -config api.addrs.addr.name=.* -config api.addrs.addr.regex=true

A opção api.disablekey=true desliga a verificação de chave da API, para os comandos abaixo funcionarem sem configuração extra. Mantenha a porta publicada em 127.0.0.1 e remova o contêiner quando terminar. Quem alcançar a API consegue controlar o ZAP.

Confirme que a API responde:

bash
curl -s http://127.0.0.1:8080/JSON/core/view/version/

Saída na minha máquina:

text
{"version":"2.17.0"}

Envie duas requisições pelo proxy. A flag -x diz ao curl para usar o ZAP. O nome do alvo é host.docker.internal porque o ZAP roda dentro do Docker, e o nome que ele usa para a sua máquina é o que ele resolve a partir de lá:

bash
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied root http=%{http_code}\n' http://host.docker.internal:3000/
curl -s -x http://127.0.0.1:8080 -o /dev/null -w 'proxied api http=%{http_code}\n' "http://host.docker.internal:3000/rest/products/search?q=apple"

Saída:

text
proxied root http=200
proxied api http=200

Pergunte ao ZAP quantas mensagens ele registrou:

bash
curl -s "http://127.0.0.1:8080/JSON/core/view/numberOfMessages/"

Na minha máquina, ele devolveu {"numberOfMessages":"5"}. Depois, liste as mensagens com um one-liner curto em Python, que imprime a linha da requisição e a linha de status da resposta:

bash
curl -s "http://127.0.0.1:8080/JSON/core/view/messages/?start=1&count=5" | python -c "import json,sys; d=json.load(sys.stdin); [print(m['requestHeader'].splitlines()[0], '->', m['responseHeader'].splitlines()[0]) for m in d['messages']]"

Saída:

text
GET http://host.docker.internal:3000/ HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000 HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products/search?q=apple HTTP/1.1 -> HTTP/1.1 200 OK
GET http://host.docker.internal:3000/rest HTTP/1.1 -> HTTP/1.0 0
GET http://host.docker.internal:3000/rest/products HTTP/1.1 -> HTTP/1.0 0

Enviei duas requisições pelo proxy, mas o histórico tem cinco entradas. Três delas não têm resposta, o que aparece como status 0. Não rastreei de onde vieram. Não as leia como evidência sobre o Juice Shop. É por isso que vale conferir o histórico você mesmo, em vez de confiar numa contagem. Se a sua lista for diferente, anote o que vê e pergunte o motivo antes de tirar qualquer conclusão.

Pare e remova o proxy quando terminar:

bash
docker rm -f zap-proxy-lab

Erros comuns

Apontar o scanner para um alvo que você não possui. A flag -t aceita qualquer URL, e o scanner vai enviar requisições para ela. Mantenha o alvo no nome do seu próprio computador dentro do laboratório.

Publicar o laboratório em todas as interfaces. -p 3000:3000 sem endereço escuta em todas as interfaces. Escreva sempre 127.0.0.1:3000:3000.

Tratar avisos como uma nota. Um relatório com três avisos mostra que três regras bateram. Verifique cada uma antes de contar.

Pular a versão. O Juice Shop muda entre as releases, e as regras do ZAP também. Anote as duas versões junto do relatório. A versão que usei foi o Juice Shop 20.3.0 e o ZAP 2.17.0.

Deixar o proxy rodando. O daemon está com a chave da API desligada. Remova o contêiner ao fim de cada sessão.

Ler a coluna de status como prova. Uma linha com status 0 significa que nenhuma resposta foi registrada. Não é um achado.

Montar a pasta errada. Se o relatório não aparecer, confira primeiro a montagem do -v. O scanner pode rodar com sucesso e mesmo assim não gravar nada onde você espera.

Como seguir em frente

Mantenha um arquivo de anotações para o laboratório. Cada linha deve ter um alerta, a requisição que o testou, o que você viu e o seu veredito. Depois de algumas varreduras, esse arquivo vira um registro do que a ferramenta encontrou, do que você confirmou e do que ela errou.

Leia o guia de instalação do Juice Shop se você ainda não o fez, porque este artigo pressupõe o mesmo laboratório. O próximo artigo usa o mesmo laboratório para explicar injeção de SQL. É um bom lugar para ver por que um alerta de scanner precisa de verificação manual: a falha está em como a entrada chega a uma consulta, e uma ferramenta só consegue sugerir isso.

A documentação do ZAP cobre contextos, autenticação e varredura ativa. Esses temas importam depois que você consegue distinguir um problema confirmado de uma regra que bateu. Estude-os no laboratório, com permissão, e não os leve para outros sistemas.

O projeto Juice Shop mantém a lista de desafios e as notas de instalação no repositório do GitHub. Confira a página de releases para saber a versão atual antes de baixar a imagem.

Fontes

Fontes

  1. ZAP documentation
  2. ZAP Baseline Scan (Docker)
  3. ZAP getting started
  4. OWASP Juice Shop (GitHub)

Mais em Ferramentas de segurança

Ferramentas de segurança

Nmap básico no seu próprio laboratório

O Nmap lista as portas abertas do seu próprio computador. Veja scans connect e SYN, detecção de serviço e como ligar cada porta ao processo que a abriu.

Iniciante13 min de leitura