Segurança em aplicações Web: XSS

Bons tempos aqueles quando os computadores não eram ligados em rede, não existia Internet e as aplicações eram todas Desktop. Em tempos de Web 2.0, Mashups e Aplicativos Web, as dores de cabeça são absurdamente maiores assim como o número de aplicações expostas a ataques. Seu sistema é seguro?

Ultimamente tenho notado como poucas pessoas se preocupam de verdade com segurança na web, ou pior, como os desenvolvedores desconhecem os problemas envolvidos na programação pra web. É preciso entender como o HTTP funciona, como os browsers funcionam, o que o JavaScript é capaz de fazer e até as implicações de segurança dos ingênuos HTML e CSS.

Pensando nisso, resolvi escrever uma série de artigos sobre segurança em aplicações Web e começo falando do famoso Cross-site Scripting, carinhosamente chamado de XSS.

Samy is my hero

Em 2005, Samy Kankar, insatisfeito com sua pequena quantidade de amigos no MySpace, escreveu um worm (depois batizado de JS.Spacehero) para turbinar sua lista de amigos.

O script rodava quando alguém simplesmente visitava o perfil de Samy, adicionava o visitante à sua lista de amigos e colocava no perfil do infectado a frase “samy is my hero“. Além disso, o script se propagava para o perfil do usuário infectado, possibilitando que esse infectasse mais pessoas.

Em menos de 20h, Samy tinha um milhão de amigos e o MySpace foi tirado do ar. A aventura ingênua de Samy (com a falha que ele descobriu, poderia ter feito algo bem pior) foi narrada de forma muito cômica no site dele. E, em 2007, lhe rendeu uma condenação nos tribunais com direito à multa, serviço comunitário e afastamento de computadores.

Mas o que o bravo Samy descobriu?

Não confie nos inputs de seus usuários

O MySpace é conhecido pela papagaiada permitida nos perfis dos usuários. Diferente do Orkut, por exemplo, o dono do perfil do MySpace pode mudar praticamente todas as características visuais do site (fundo, cores, letras, imagens, vídeos etc). Para isso, o MySpace aceita que o usuário digite HTML e CSS para personalizar sua página, mas com limites para a coisa não virar zona.

O que Samy fez? Descobriu uma falha no filtro do MySpace que limita os inputs e conseguiu colocar em seu perfil um código JavaScript que seria executado por cada visitante do seu perfil. Com técnicas de Ajax e um pouco de tempo livre, Samy fez um JavaScript que executava as ações descritas acima.

XSS – Cross-site scripting

Um ataque XSS é aquele que permite a injeção de scripts no site atacado, em geral por meio de algum campo de input do usuário. Note que um ataque XSS é um ataque ao usuário do site e não ao sistema servidor em si. Isso porque o script injetado nunca será executado no servidor, mas sim nos navegadores dos clientes que visitarem a página infectada.

A regra de ouro de qualquer sistema com input de usuários é que não devemos confiar jamais naquilo que o usuário digitou. Além de XSS, podemos ser alvos de muitos outros ataques, como SQL Injection, injeção de parâmetros e outros (quem sabe abordamos num artigo futuro).

Embora pareça simples, a OWASP (Open Web Application Security Project) diz que nada menos que 9 em cada 10 sites estão vulneráveis a esse tipo de ataque. Anualmente, eles fazem um estudo das 10 falhas mais encontradas na Web, e no ano passado, XSS foi o campeão.

Como se proteger

Todo input do usuário deve ser sanitizado antes de qualquer coisa. Isso significa passar algum tipo de filtro que consiga remover tags potencialmente perigosas.

Se o seu site não deve ser customizado pelo usuário de nenhuma forma e o input dele deveria ser texto puro, basta remover toda e qualquer tag encontrada. Mas o caso mais complicado (aqui entra o MySpace) é quando você deseja permitir certas tags (negrito, por exemplo) e qual abordagem seguir para filtrar as tags não desejadas.

É considerado, hoje, total insanidade mental e causa da maioria das vulnerabilidades de XSS, tentar escrever esse filtro de input sozinho. Ele é extremamente complexo. Pense no caso simples, aquele onde não desejamos aceitar nenhuma tag. 99% das pessoas pensariam que o código abaixo faz um excelente trabalho:
String limpo = input.replaceAll("<", "").replaceAll(">", "");

É um código Java que transforma:
<script>alert('XSS');</script>

…nisso (claramente inofensivo):
scriptalert('XSS');/script

E o código abaixo então?
+ADw-script+AD4-alert('XSS')+ADw-/script+AD4-

Esse código é executado por qualquer navegador atual usando codificação UTF-7. E obviamente aqueles nossos “replaceAll” não resolvem o problema. Há centenas de outros exemplos que usam encodings estranhos, html entities, carateres especiais no meio etc.

E isso com o objetivo de tirar todas as tags da frente. E quando queremos que algumas sejam aceitas? Quais tags são seguras? E os atributos nessas tags? Até um <img> com atributo src apenas está vulnerável se não tomarmos cuidado.

Moral da história: fazer os replacezinhos na mão é insano. Não fazer nada a respeito é suicídio.

Arrumando o problema

Precisamos sanitizar o input dos usuários e precisamos de algo pronto que faça isso. Há trocentos projetos por aí que fazem esse tipo de serviço. Em especial, o AntiSamy do pessoal da OWASP que tem versões em Java e .Net. A Microsoft tem uma API anti-XSS para .Net. Há ainda o HtmlPurifier para PHP, o sanitize do Ruby on Rails e muitos outros em outras plataformas.

O AntiSamy é bastante customizável, permitindo que definamos níveis diferentes de purificação através de regras em um XML. Ele ainda é capaz de mostrar mensagens de erros amigáveis para os usuários. Usá-lo é bem simples:


AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(suspeito);
String limpo = cr. getCleanHTML()

É considerada boa prática também purificar a saída dessas strings, por precaução. Com JSP, isso é muito fácil:

<c:out value="${suspeito}" escapeXml="true" />

Conclusão

XSS é um problema real e muitas aplicações estão vulneráveis. Samy atacou o MySpace em 2005 e conseguiu 1 milhão de amigos. A busca do Google tinha as falhas do UTF-7 comentadas acima em 2005. Apoiadores do Firefox sequestraram as maiores comunidades do Orkut em 2005 e mudaram os logos para o do Firefox. Eleitores de Hillary Clinton redirecionavam visitantes do site de Barack Obama para o site de sua adversária nas prévias da eleição americana em abril desse ano. E milhares de outros casos públicos.

Proteja sua aplicação!

Referências

14 Comentários

  1. Germano 25/11/2008 at 22:36 #

    Muito interessante. Já tive de tratar essse tipo de input, mas não conhecia essa questão de segurança chamada XSS.
    Valeu Sergio!

  2. Rafael Carneiro 26/11/2008 at 02:53 #

    Post altamente útil, parabéns!

  3. Luiz Aguiar 27/11/2008 at 05:02 #

    Muito bom Sérgio, XSS parece uma coisa tão besta né, e a maioria não tem nem idéia de como se proteger.

  4. Alberto 30/11/2008 at 23:15 #

    Também gostei muito deste post Sergio, acho que vai ser muito legal se vc arranjar tempo para fazer toda a sequência.

  5. Mateus Prado 01/12/2008 at 08:31 #

    Parabens mais uma vez pelo post Sergio.

  6. Whocares 16/01/2009 at 05:49 #

    Informações essenciais. Espero que estas providências sobre o XSS sejam suficientes para combater o problema. Parabéns!

  7. Vinicius Senger 03/02/2009 at 23:57 #

    Pois é, nosso site vive cheio de mensagens “faca curso na caelum” no google analytics.

    81 ataques no dia 28/2/2008
    23 ataques no dia 1/3/2008
    11 ataques no dia 22/9/2008
    3 ataques no dia 5/1/2009

    Vamos pedir juridicamente quebra de sigilo para analisar a fonte do ataque. Adianto que este seu post foi perfeito para nosso advogado!

  8. Paulo Silveira 04/02/2009 at 01:26 #

    jaja vem a nasa.gov atras de voce tambem heinn? :).

    vinicius, o site novo nao tem mais html/script injection, nao sei dizer o que sao esses acessos recentes que aparecem caelum na url.

  9. Wagner Elias 24/04/2009 at 06:55 #

    Sérgio,

    parabéns pelo post!

    Eu vejo muitos posts, artigos por aí, mas a maioria deles pecam no tratamento e no entendimento do ataque. Principalmente não verificando, validando os ataques “encodados”.

    Além dos controles mencionados por você, o mais importante hoje na OWASP é o ESAPI (http://www.owasp.org/index.php/ESAPI ) uma biblioteca completa e bastante eficaz.

    Como líder do capítulo Brasil da OWASP, convido a todos para conhecer o capítulo ( http://www.owasp.org/index.php/Brazilian ) e participar da lista de discussão (http://lists.owasp.org/mailman/listinfo/owasp-brazilian).

    Abs.

  10. Ranieri Marinho de Souza 01/06/2010 at 10:18 #

    Boa dica

  11. Rasa Lariguet 18/09/2013 at 09:50 #

    Muito bom o post, parabéns.

    Sérgio, tenho uma dúvida, eu tenho um site pequeno que tem uma intranet que fiz em struts 2 a uns 2 anos ou mais já, e decidi testar a intranet depois do seu post, com a certeza de que iria dar erro, pois na época não me preveni de XSS. Então fiz um cadastro de um novo usuário com o nome alert(‘NOME’); e depois listei os usuários e o html exibiu normalmente o nome do usuário da mesma forma que eu escrevi: alert(‘NOME’);, não deu nenhum alert. Saberia me explicar o que aconteceu?

  12. Rasa Lariguet 18/09/2013 at 09:52 #

    Muito bom o post, parabéns.

    Sérgio, tenho uma dúvida, eu tenho um site pequeno que tem uma intranet que fiz em struts 2 a uns 2 anos ou mais já, e decidi testar a intranet depois do seu post, com a certeza de que iria dar erro, pois na época não me preveni de XSS. Então fiz um cadastro de um novo usuário colocando no nome o script com o alert e depois listei os usuários e o html exibiu normalmente o nome do usuário da mesma forma que eu escrevi com o script e o alert no meio, não deu nenhum alert do javascript na tela. Saberia me explicar o que aconteceu?

Deixe uma resposta