O scripting em vários sites (DOM XSS) baseado em DOM acontece quando os dados de uma
fonte controlada pelo usuário (como um nome de usuário ou um URL de redirecionamento retirado do fragmento de
URL) chegam a um coletor, que é uma função como eval()
, ou um setter de
propriedades, como .innerHTML
, que pode executar um código JavaScript arbitrário.
O DOM XSS é uma das vulnerabilidades de segurança mais comuns na Web, e é comum que as equipes de desenvolvedores o introduzam acidentalmente nos apps. Os Tipos confiáveis oferecem as ferramentas para escrever, analisar a segurança e manter os aplicativos livres de vulnerabilidades XSS do DOM, tornando as funções perigosas da API da Web seguras por padrão. Os Tipos confiáveis estão disponíveis como um polyfill para navegadores que ainda não são compatíveis com eles.
Contexto
Há muitos anos, o DOM XSS é uma das vulnerabilidades de segurança mais comuns e perigosas da Web.
Há dois tipos de scripting em vários sites. Algumas vulnerabilidades XSS são causadas por um código no lado do servidor que cria de forma não segura o código HTML que forma o site. Outros têm uma causa raiz no cliente, em que o código JavaScript chama funções perigosas com conteúdo controlado pelo usuário.
Para evitar o XSS do lado do servidor, não gere HTML concatenando strings. Para mitigar outros bugs, use bibliotecas de modelos de escape automático protegido contextualmente, além de uma Política de Segurança de conteúdo baseada em valor de uso único.
Agora, os navegadores também podem ajudar a evitar XSS baseado em DOM do lado do cliente usando Tipos confiáveis.
Introdução à API
Os Tipos confiáveis funcionam bloqueando as seguintes funções arriscadas de coletor. Talvez você já reconheça alguns deles, porque os fornecedores de navegadores e frameworks da Web já evitam o uso desses recursos por motivos de segurança.
- Manipulação de script:
<script src>
e configuração do conteúdo de texto de elementos<script>
. - Como gerar HTML a partir de uma string:
- Como executar o conteúdo do plug-in:
- Compilação de código JavaScript em tempo de execução:
eval
setTimeout
setInterval
new Function()
Os tipos confiáveis exigem que você processe os dados antes de passá-los para essas funções de coletor. O uso apenas de uma string falha, porque o navegador não sabe se os dados são confiáveis:
anElement.innerHTML = location.href;
Para indicar que os dados foram processados com segurança, crie um objeto especial, um tipo confiável.
anElement.innerHTML = aTrustedHTML;
Os tipos confiáveis reduzem significativamente a superfície de ataque (link em inglês) do DOM XSS do aplicativo. Ele simplifica as revisões de segurança e permite aplicar as verificações de segurança baseadas em tipos feitas ao compilar, inspecionar ou agrupar seu código durante a execução no navegador.
Como usar os Tipos confiáveis
Preparar-se para denúncias de violação da Política de Segurança de Conteúdo
Você pode implantar um coletor de relatórios, como o go-csp-collector de código aberto, ou usar um dos equivalentes comerciais. Também é possível depurar violações no navegador:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Adicionar um cabeçalho da CSP somente para relatórios
Adicione o seguinte cabeçalho de resposta HTTP aos documentos que você quer migrar para Tipos confiáveis:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Agora, todas as violações são informadas a //my-csp-endpoint.example
, mas o
site continua funcionando. A próxima seção explica como
//my-csp-endpoint.example
funciona.
Identificar violações dos Tipos confiáveis
A partir de agora, toda vez que os Tipos confiáveis detectarem uma violação, o navegador vai enviar um
relatório a um report-uri
configurado. Por exemplo, quando o aplicativo
transmite uma string para innerHTML
, o navegador envia o seguinte relatório:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Isso informa que em https://my.url.example/script.js
na linha 39, innerHTML
foi
chamado com a string que começa com <img src=x
. Essas informações ajudam
a restringir quais partes do código podem estar introduzindo o XSS do DOM e que precisam ser alteradas.
Corrigir as violações
Há algumas opções para corrigir uma violação do "Tipo confiável". É possível remover o código incorreto, usar uma biblioteca, criar uma política de tipo confiável ou, como última reclassificação, criar uma política padrão.
Reescrever o código que está com problemas
É possível que o código não conforme não seja mais necessário ou possa ser reescrito sem as funções que causam as violações:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '';
Usar uma biblioteca
Algumas bibliotecas já geram Tipos confiáveis que podem ser passados para as funções do coletor. Por exemplo, é possível usar o DOMPurify (link em inglês) para limpar um snippet HTML, removendo payloads XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
O DOMPurify oferece suporte a tipos confiáveis
e retorna HTML limpo em um objeto TrustedHTML
para que o navegador
não gere uma violação.
Criar uma política de Tipo confiável
Às vezes, não é possível remover o código que causa a violação e não há biblioteca para limpar o valor e criar um Tipo confiável para você. Nesses casos, você mesmo pode criar um objeto de tipo confiável.
Primeiro, crie uma política. Políticas são fábricas para Tipos confiáveis que impõem certas regras de segurança às entradas:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Esse código cria uma política chamada myEscapePolicy
, que pode produzir objetos TrustedHTML
usando a função createHTML()
. Os caracteres <
de escape HTML das regras definidas
para impedir a criação de novos elementos HTML.
Use a política desta forma:
const escaped = escapeHTMLPolicy.createHTML('<img src=x
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x
Usar uma política padrão
Às vezes, não é possível alterar o código incorreto, por exemplo, se você estiver carregando uma biblioteca de terceiros por uma CDN. Nesse caso, use uma política padrão:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
A política chamada default
é usada sempre que uma string é utilizada em um coletor que aceita apenas o tipo confiável.
Passar para a aplicação da Política de Segurança de Conteúdo
Quando o aplicativo não produzir mais violações, você poderá aplicar Tipos confiáveis:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Seja qual for a complexidade do seu aplicativo da Web, o único fator que pode introduzir uma vulnerabilidade de XSS do DOM é o código em uma das suas políticas, e você pode bloquear isso ainda mais limitando a criação de políticas.