XSS – Guia explicativo
Na semana passada, um pesquisador encontrou uma vulnerabilidade de Cross-Site Scripting, chamada também de XSS, no site do eBay, um dos mais famosos sites de compra e venda do mundo, e acabou sendo prestigiado no Hall da Fama da empresa. Ontem mesmo foi a vez de @securityshell, outro profissional de segurança da informação, encontrar a mesma falha no site do navegador Yandex. Daí eu lhe pergunto: afinal, o que você sabe a respeito de XSS?
Muito ainda se ouve falar (e ainda vamos ouvir) a respeito de Cross-Site Scripting, embora poucos realmente saibam as diferenças entre os três tipos dessa antiga vulnerabilidade, como descobri-la, como mitigá-la e qual o potencial de um ataque que utilize-se dela. É mais simples do que parece, confira o índice:
- O que é XSS?
- Quais são as implicações de um ataque baseado em XSS?
- Classificação XSS
- XSS Reflected
- XSS Stored
- XSS DOM Based
- Detecção
- Cenário didático
- Mitigação
- Teste seus conhecimentos
1. O que é XSS?
É uma vulnerabilidade comumente encontrada em aplicações web que permite a injeção de códigos no lado do cliente, ou seja, altera a página apenas no computador do usuário. Esta vulnerabilidade é subdividida em três categorias, sendo elas Refletido, Armazenado e baseado em DOM, explicadas abaixo.
2. Quais são as implicações de um ataque baseado em XSS?
Depende. Varia desde um simples alerta na tela ao sequestro de sessão ou redirecionamento para sites maliciosos. Essa vulnerabilidade, que antigamente era subestimada, foi uma das responsáveis por grande parte dos ataques, segundo a OWASP, em seu top 10.
3. Classificação de XSS
3.1. XSS Reflected
Cross-Site Scripting Refletido acontece quando o servidor reflete o que enviamos sem filtrar o que o usuário colocou, ou seja, ao enviar um determinado parâmetro, o servidor repete-o no código-fonte da página sem tratar o que foi inserido, causando essa vulnerabilidade. Para entender melhor, confira o seguinte cenário:
- Acesso http://exemplo
- http://exemplo solicita Usuário e Senha
- Insiro um usuário não existente chamado "PablitoFonsecaDeOliveira"
- http://exemplo imprime na tela "O usuário 'PablitoFonsecaDeOliveira' não existe"
Até aí identificamos que o sistema está refletindo nosso input. Agora, para identificar o XSS Reflected:
- Acesso http://exemplo
- http://exemplo pede meu Usuário e Senha
- Insiro um usuário não existente chamado "<script>alert(1)</script>" - que na verdade é um conjunto de instruções HTML (por causa da tag <script>) e Javascript (por conta do alert(1)) que, ao ser executado, emite um alerta na tela.
- http://exemplo imprime na tela "O usuário " não existe" e gera uma caixa de alerta com o conteúdo "1"
Ilustração de cenários com usuários bem e mal-intencionados:
Para relembrar, as premissas para execução do XSS Reflected são:
- Refletir o input do usuário
- Não filtrar os caracteres nocivos - no caso, "<" e ">"
3.2. XSS Stored
Armazenado, como o próprio nome já diz, é quando o código malicioso a ser injetado não foi filtrado e está armazenado, persistido em algum componente da aplicação, que comumente refere-se ao banco de dados. Quando alguma página for imprimir na tela o conteúdo armazenado, caso não filtre os caracteres nocivos, o XSS Stored é disparado. Acompanhe os cenários abaixo para um melhor entendimento:
- Acesso http://exemplo/cadastro
- http://exemplo/cadastro solicita meu Nome e Senha para me cadastrar
- Preencho "Anderson Dadario" no nome e "teste" na senha
- Após o cadastro, sou redirecionado para uma tela que imprime "Seja bem-vindo Anderson Dadario"
Esse é o cenário feliz, porém, se eu for um usuário que deseje verificar a existência da vulnerabilidade XSS Stored, o cenário seria:
- Acesso http://exemplo/cadastro
- http://exemplo/cadastro solicita meu Nome e Senha para me cadastrar
- Preencho "<script>alert(1)</script>" no nome e "teste" na senha
- Após o cadastro, sou redirecionado para uma tela que imprime "Seja bem-vindo" e gera uma caixa de alerta com o conteúdo "1"
Ilustração:
Para relembrar, as premissas para execução do XSS Stored são:
- Não filtrar os caracteres nocivos - no caso, "<" e ">" - ao persistir o input do usuário
- Não filtrar os caracteres nocivos - no caso, "<" e ">" - ao imprimir o dado persistido
3.3. XSS DOM Based
Este é o tipo mais difícil de ser encontrado entre os três porque depende de uma vulnerabilidade em algum dos componentes da página (geralmente códigos javascript) caracterizado por ser disparado no momento de execução ao invés de vir embutido no código fonte. Complicou? Vou explicar com o exemplo recentemente encontrado no AddThis, site que fornece serviço de compartilhamento para conteúdo.
- página http://exemplo utiliza o componente do AddThis para compartilhar suas notícias, gerando esse resultado:
- Acesso
http://exemplo/#”></fb:like><img/src=”aaa”/onerror=”alert(‘DomXss Found!’)
- É gerado um alerta na tela escrito "DomXss Found!"
Atenção: repare que o código injetado foi inserido após o caractere sustenido (#), extremamente comum neste tipo de XSS. Conhece a utilidade do sustenido? Em caso negativo, leia a seção abaixo:
A história do sustenido e introdução ao Ajax...
O sustenido era utilizado antigamente para criar links âncoras para a mesma página, como víamos nos sites de letras de músicas onde você escolhia o artista por letra "A B C D E" e ao clicarmos éramos redirecionados para a mesma página, porém apenas para uma seção diferente, ou seja, só a barra de rolagem se movimentava.
Atualmente, o sustenido é utilizado por bibliotecas Javascript para manter um histórico de requisições Ajax, requisições essas que atualizam apenas o "miolo" da página, ao invés de carregar a página inteira por completo. Para se ter uma ideia, quando você acessa o Facebook na página "Pessoas que você talvez conheça", desce a barra de rolagem até o final e repara que uma imagem de "carregando" é executada e mais sugestões aparecem, você está presenciando uma chamada Ajax! Note também que a URL de sua barra de endereços não é alterada.
E se o Facebook quisesse paginar essas sugestões que aparecem na sua tela? Ou seja, criar uma URL Única que, ao ser acessada, levasse para a página atual com essa mesma quantidade de sugestões de amigos? Seria necessário adicionar algum mecanismo para toda vez que o script Ajax que retorna mais sugestões for executado, fosse atualizada a URL.
Daí que entra o sustenido, interpretado por uma rotina em javascript que consegue ler o que vem depois do sustenido e gerar novamente a sugestão de amigos até a quantidade desejada. E justamente nessa interpretação é possível descobrir uma vulnerabilidade de Cross-Site Scripting baseada em DOM. E afinal, que raios é DOM?
...voltando ao DOM based XSS
Document Object Model (DOM), segundo a Wikipedia, é uma especificação da W3C, independente de plataforma e linguagem, onde pode-se dinamicamente alterar e editar a estrutura, conteúdo e estilo de um documento eletrônico, permitindo que o documento seja mais tarde processado e os resultados desse processamento, incorporados de volta no próprio documento.
E, justamente, nesse processo de alteração da uma estrutura da página, que se dá por Javascript nesse contexto, caso o código estiver vulnerável, a vulnerabilidade de Cross-Site Scripting baseada em DOM será disparada - isso tudo sem alterar o código fonte.
Vamos supor que o nome desta página seja "xss.html". Caso acessemos a página desta forma: xss.html#<script>alert(1)</script>
, a vulnerabilidade será disparada:
<html>
<script>
document.write("<b>URL atual<b> : " + document.baseURI);
</script>
</html>
4. Detecção
Você pode encontrar vulnerabilidades de Cross-Site Scripting manualmente, conforme as provas de conceito acima, mas isso pode ser árduo se a aplicação for grande ou seu tempo for curto, exceto se estiver buscando por DOM based, visto que é um dos tipos mais difíceis de encontrar. Para auxiliar sua jornada, confira a relação de algumas ferramentas gratuitas e pagas para detectar essa vulnerabilidade:
- Gratuitas
- Comerciais
Confira a lista completa de ferramentas aqui.
Você pode experimentar também o Gauntlet.io e rodar diversas ferramentas de uma vez (disclosure: eu que criei, rs).
5. Cenário Didático
Feita a detecção, uma série de possibilidades para o usuário mal-intencionado surgem, confira um dos possíveis cenários:
6. Mitigação
Para começar, audite o controle de validação de dados de entrada (tudo que o usuário envia) e validação de dados de saída (tudo que o servidor envia), certificando-se de que nada além do esperado é processado.
- Crie uma whitelist de caracteres permitidos, assim você evita não apenas vulnerabilidades de XSS, como também outras vulnerabilidades de injeção de código.
- Utilize uma codificação de confiança, como o UTF-8. E sim, encodings estão relacionados com XSS.
- Utilize um Web Application Firewall (WAF), mas não dependa apenas dele!
- Envie na resposta do servidor o header X-XSS-Protection: 1; mode=block, assim o navegador do usuário tentará bloquear XSS caso detecte.
- Utilize as flags HttpOnly nos cookies para não serem manipulados por Javascript e também a flag Secure para forçar o tráfego apenas por HTTPS.
- Confira outras recomendações na página da OWASP.
7. Teste Seus Conhecimentos
Isso mesmo, coloque em prática o que aprendeu aqui em um mini-game do Google: https://xss-game.appspot.com/
Obs: comecei a lançar alguns cursos de segurança gratuitos, em português e inglês. Confira-os aqui.
Bons estudos!
Ah, e se você gostou desse artigo, pode gostar também deste aqui sobre Cross-Site Request Forgery (CSRF) que publiquei recentemente (2016).