O scripting em vários sites baseado em DOM (DOM XSS) acontece quando dados de uma
origem controlada pelo usuário (como um nome de usuário ou um URL de redirecionamento extraído do fragmento de URL
) chegam a um coletor, que é uma função como eval() ou um setter de propriedade
como .innerHTML que pode executar 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. Tipos confiáveis oferecem as ferramentas para escrever, revisar a segurança e manter os aplicativos livres de DOM XSS vulnerabilidades, 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 oferecem suporte a eles.
Contexto
Por muitos anos, DOM XSS foi uma das vulnerabilidades de segurança da Web mais comuns e perigosas.
Há dois tipos de scripting em vários sites. Algumas vulnerabilidades XSS são causadas por código do lado do servidor que cria o código HTML que forma o site de maneira não segura. Outras 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. Use bibliotecas de modelos de autoescapamento contextual seguro, além de uma Política de Segurança de Conteúdo baseada em nonce para mais mitigação de bugs.
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 de receptor arriscadas. Talvez você já reconheça algumas delas, porque os fornecedores de navegadores e frameworks da Web já o impedem de usar esses recursos por motivos de segurança.
- Manipulação de scripts:
<script src>e definição do conteúdo de texto de<script>elementos. - Gerar HTML de uma string:
- Executar conteúdo do plug-in:
- Compilação de código JavaScript no ambiente de execução:
evalsetTimeoutsetIntervalnew Function()
Os Tipos confiáveis exigem que você processe os dados antes de transmiti-los a essas funções de receptor. Usar apenas 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;
TrustedHTML
objeto para receptores que esperam snippets HTML. Também há objetos
TrustedScript e TrustedScriptURL para outros
receptores sensíveis.
Os Tipos confiáveis reduzem significativamente a superfície de ataque DOM XSS do aplicativo. Ele simplifica as revisões de segurança e permite aplicar as verificações de segurança baseadas em tipo feitas ao compilar, fazer a lintagem ou agrupar o código no ambiente de execução, no navegador.
Como usar Tipos confiáveis
Preparar relatórios 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 go-csp-collector, ou usar um dos equivalentes comerciais. Também é possível adicionar registros personalizados e violação de 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 de relatório
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 de Tipos confiáveis
A partir de agora, sempre que os Tipos confiáveis detectarem uma violação, o navegador 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 indica 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 DOM XSS 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 que apresentou 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 que apresentou 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 podem ser transmitidos para as funções de receptor. Por exemplo, é possível usar o DOMPurify para higienizar 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 higienizado encapsulado 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á uma biblioteca para higienizar o valor e criar um Tipo confiável para você. Nesses casos, é possível criar um objeto de Tipo confiável.
Primeiro, crie uma política. As políticas são fábricas de Tipos confiáveis que aplicam determinadas regras de segurança à entrada:
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 escapam de caracteres < HTML para impedir a criação de novos elementos HTML.
Use a política desta 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 mudar o código que apresentou 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 chamada default é usada sempre que uma string é usada em um receptor que aceita apenas o Tipo confiável.
Mudar para a aplicação da Política de Segurança de Conteúdo
Quando o aplicativo não produzir mais violações, você poderá começar a aplicar Tipos confiáveis:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Agora, 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 das suas políticas, e você pode bloqueá-lo ainda mais limitando a criação de políticas.