O scripting em vários sites baseado em DOM (DOM XSS) ocorre quando os dados de uma origem controlada pelo usuário (como um nome de usuário ou um URL de redirecionamento retirado do fragmento de URL) alcançam 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 da Web mais comuns, e é comum que as equipes de desenvolvimento 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
Por muitos anos, o DOM XSS tem sido uma das vulnerabilidades de segurança da Web mais perigosas e prevalentes.
Há dois tipos de scripting em vários sites. Algumas vulnerabilidades XSS são causadas por um código do lado do servidor que cria de forma insegura 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 XSS do lado do servidor, não gere HTML concatenando strings. Em vez disso, use bibliotecas seguras de modelos de escape automático contextual e uma Política de Segurança de Conteúdo baseada em valor de uso único para reduzir ainda mais os bugs.
Agora, os navegadores também podem evitar XSS baseado em DOM do lado do cliente usando Tipos confiáveis.
Introdução à API
Os Tipos confiáveis bloqueiam as funções de coletor arriscadas a seguir. Talvez você já reconheça alguns deles, porque os fornecedores de navegadores e os frameworks da Web já impedem 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 com base em uma string:
- Execução do conteúdo do plug-in:
- Compilação de código JavaScript no ambiente de execução:
eval
setTimeout
setInterval
new Function()
Para usar tipos confiáveis, é necessário processar os dados antes de transmiti-los para essas funções de coletor. O uso de apenas uma string resulta em 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 XSS do DOM do aplicativo. Ele simplifica as análises de segurança e permite aplicar as verificações de segurança baseadas em tipo ao compilar, inspecionar ou agrupar o código durante a execução no navegador.
Como usar Tipos confiáveis
Preparar para denúncias de violação da Política de Segurança de Conteúdo
É possível implantar um coletor de relatórios, como o reporting-api-processor de código aberto ou o go-csp-collector, ou usar um dos equivalentes comerciais. Também é possível adicionar violações personalizadas de geração de registros e depuração no navegador usando um ReportingObserver:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
ou adicionando um listener de eventos:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Adicionar um cabeçalho CSP somente para relatórios
Adicione o seguinte cabeçalho de resposta HTTP aos documentos que você quer migrar para os 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 ao //my-csp-endpoint.example
, mas o
site continua funcionando. A próxima seção explica como
//my-csp-endpoint.example
funciona.
Identificar violações de 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 para 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 apresentando XSS do DOM e precisam ser alteradas.
Corrigir as violações
Há algumas opções para corrigir uma violação de tipo confiável. É possível remover o código com problemas, usar uma biblioteca, criar uma política de tipo confiável ou, como último recurso, criar uma política padrão.
Reescrever o código 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 = '<img src=xyz.jpg>';
Usar uma biblioteca
Algumas bibliotecas já geram Tipos confiáveis que você pode transmitir para as funções de coletor. Por exemplo, é possível usar o DOMPurify 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 unido 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 está causando 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. As políticas são fábricas de Tipos confiáveis que impõem determinadas 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()
. As regras definidas fazem escape de caracteres <
em HTML para impedir a criação de novos elementos HTML.
Use a política da seguinte forma:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Usar uma política padrão
Às vezes, não é possível alterar o código com problemas, por exemplo, se você estiver carregando uma biblioteca de terceiros de 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 denominada default
é usada sempre que uma string é utilizada em um coletor que aceita apenas o tipo confiável.
Migre para a aplicação da Política de Segurança de Conteúdo
Quando seu aplicativo não produz mais violações, você pode começar a aplicar os Tipos confiáveis:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Não importa a complexidade do seu aplicativo da Web, a única coisa que pode introduzir uma vulnerabilidade DOM XSS é o código em uma de suas políticas. Você pode bloquear ainda mais ao limitar a criação de políticas.