Glossário · Segurança
Cross-site scripting (XSS)
Em inglês: Cross-site scripting
Também chamado de: XSS, cross site scripting, XSS armazenado, XSS refletido
Definição
XSS é uma vulnerabilidade em que uma entrada não confiável é renderizada como script executável no navegador de outro usuário, permitindo que o atacante aja como ele.
Cross-site scripting (XSS) acontece quando uma aplicação pega a entrada de um usuário e a coloca em uma página sem o encoding adequado, de modo que o navegador a trata como HTML ou JavaScript em vez de texto. O script então roda com a sessão da vítima no site vulnerável: pode ler dados da página, fazer requisições como se fosse o usuário ou alterar o que ele vê. No OWASP Top 10 2021, ele está agrupado em A03 Injection.
Os três tipos
- Armazenado (stored). O payload é salvo (um comentário, um nome de perfil, um chamado de suporte) e entregue a todos que o visualizam. Geralmente é o mais grave.
- Refletido (reflected). O payload está na requisição, muitas vezes em um parâmetro de URL como um termo de busca, e é devolvido diretamente na resposta.
- Baseado em DOM (DOM-based). O JavaScript do lado do cliente lê dados controlados pelo atacante (por exemplo
location.hash) e os escreve na página por um sink inseguro comoinnerHTML.
O que o tester verifica
Coloque um marcador inofensivo em todos os campos que depois são exibidos (para você, para outros usuários, para admins em um back office) e veja se ele volta como texto ou como markup ativo. Uma verificação automatizada em Playwright, apenas para testes autorizados:
import { test, expect } from '@playwright/test';
test('comment body is rendered as text, not HTML', async ({ page }) => {
const probe = '<img src=x onerror="window.__xssProbe=1">';
await page.goto('/posts/1');
await page.getByLabel('Comment').fill(probe);
await page.getByRole('button', { name: 'Post comment' }).click();
// Escaped output shows the literal characters on screen
await expect(page.getByText(probe)).toBeVisible();
// and the handler never ran
expect(await page.evaluate(() => (window as any).__xssProbe)).toBeUndefined();
});
Verifique também contextos de saída além do HTML simples: valores dentro de atributos, dentro de scripts inline, em URLs (links javascript:) e em editores de rich text, porque cada um exige um encoding diferente.
Defesas a verificar
- Encoding de saída por padrão. Frameworks modernos (React, Angular, Vue) fazem escape do texto automaticamente; procure as válvulas de escape como
dangerouslySetInnerHTML,v-htmlouinnerHTML. - Sanitização de HTML nas funcionalidades que realmente aceitam markup.
- Um header Content Security Policy que bloqueie scripts inline, como segunda camada.
- Cookies de sessão
HttpOnly, para que um XSS bem-sucedido não consiga simplesmente ler o cookie de sessão.
Nunca teste em sistemas que não são seus ou para os quais você não tem permissão por escrito.