A segurança no front-end é um aspecto fundamental do desenvolvimento web moderno, pois as aplicações JavaScript estão expostas a diversas ameaças que podem comprometer a integridade dos dados e a privacidade dos usuários. Nesta aula, vamos explorar os principais vetores de ataque, como Cross-Site Scripting (XSS), e as estratégias de defesa, incluindo sanitização de entradas, políticas de segurança de conteúdo (CSP) e boas práticas de codificação.

Entender esses conceitos é crucial para construir aplicações robustas e confiáveis, pois mesmo pequenas falhas podem ser exploradas por atacantes. Ao final desta aula, você estará apto a reconhecer riscos e aplicar medidas preventivas eficazes em seus projetos.

XSS

Cross-Site Scripting (XSS) é uma vulnerabilidade que permite a injeção de scripts maliciosos em páginas web visualizadas por outros usuários. O atacante explora a falta de validação ou escape de dados fornecidos pelo usuário, fazendo com que o navegador execute código não autorizado. Existem três tipos principais de XSS: refletido, armazenado e baseado em DOM.

O XSS refletido ocorre quando o script malicioso é enviado na requisição (por exemplo, via parâmetros de URL) e refletido na resposta sem tratamento adequado. O XSS armazenado acontece quando o payload é persistido no servidor (em um banco de dados, por exemplo) e exibido a todos os usuários. Já o XSS baseado em DOM é executado no lado do cliente, manipulando o DOM de forma insegura, sem passar pelo servidor.

Exemplo de código vulnerável a XSS (usando innerHTML):

// Exemplo vulnerável: injetando HTML diretamente no DOM
const userInput = document.getElementById('input').value;
document.getElementById('output').innerHTML = userInput;

Se o usuário digitar <img src=x onerror=alert('XSS')>, o script será executado. Para mitigar, devemos usar textContent em vez de innerHTML quando não queremos interpretar HTML, ou escapar os dados adequadamente.

Outro vetor comum é o uso de atributos como href ou src com dados não validados. Por exemplo, javascript:alert(1) em um link pode ser perigoso. Devemos sempre validar e sanitizar entradas.

Sanitização

Sanitização é o processo de limpar os dados de entrada para remover ou neutralizar conteúdo perigoso, como scripts ou tags HTML indesejadas. É uma medida essencial para prevenir XSS e outras injeções. A sanitização deve ser aplicada tanto no cliente quanto no servidor, mas no front-end, especialmente ao manipular o DOM, é crucial.

Existem bibliotecas como DOMPurify que facilitam a sanitização de HTML. Veja um exemplo:

// Usando DOMPurify para sanitizar HTML
import DOMPurify from 'dompurify';

const dirty = '';
const clean = DOMPurify.sanitize(dirty);
// clean agora é uma string segura, sem o atributo onerror

Além disso, é importante validar o formato dos dados (por exemplo, garantir que um campo de e-mail seja realmente um e-mail) e usar funções de escape para contextos específicos (HTML, atributos, URL, CSS). Por exemplo, ao inserir dados em um atributo, é preciso escapar aspas e caracteres especiais.

Uma abordagem simples para escapar HTML manualmente:

function escapeHtml(str) {
  return str.replace(/[&<>"']/g, function(m) {
    const map = {
      '&': '&',
      '<': '<',
      '>': '>',
      '"': '"',
      "'": '''
    };
    return map[m];
  });
}

No entanto, é preferível usar bibliotecas testadas e atualizadas, pois a sanitização correta é complexa e cheia de nuances.

CSP (visão geral)

Content Security Policy (CSP) é um mecanismo de segurança que permite definir de onde os recursos (scripts, estilos, imagens, etc.) podem ser carregados. Ele é implementado por meio de um cabeçalho HTTP ou de uma meta tag no HTML. O CSP ajuda a mitigar ataques como XSS e injeção de conteúdo, restringindo as fontes de scripts e outros recursos.

Um exemplo de política CSP que permite apenas scripts do próprio domínio e de um CDN confiável:

// Cabeçalho HTTP: Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com;

No HTML, podemos usar a meta tag:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.example.com">

É importante entender que o CSP não é uma solução completa, mas uma camada adicional de defesa. Ele pode ser configurado de forma restritiva, mas requer cuidado para não quebrar funcionalidades legítimas da aplicação. Ferramentas como o CSP Evaluator do Google podem ajudar a testar políticas.

O CSP também permite o uso de nonces ou hashes para permitir scripts inline específicos, o que é útil quando não é possível mover todo o código para arquivos externos. Por exemplo:

// Política com nonce: script-src 'nonce-abc123';

No HTML, o script teria o atributo nonce="abc123". Essa abordagem é mais segura do que usar unsafe-inline.

Boas práticas

Além de XSS, sanitização e CSP, existem outras boas práticas que devemos adotar para melhorar a segurança no front-end. Aqui estão algumas recomendações essenciais:

  • Valide e sanitize todas as entradas do usuário, tanto no cliente quanto no servidor.
  • Use textContent em vez de innerHTML sempre que possível, para evitar interpretação de HTML.
  • Escape dados antes de inserir em atributos HTML e em URLs.
  • Evite o uso de eval() e new Function(), pois podem executar código arbitrário.
  • Mantenha as dependências atualizadas e monitore vulnerabilidades conhecidas (por exemplo, usando npm audit).
  • Implemente CSP e configure corretamente os cabeçalhos de segurança, como X-Content-Type-Options e X-Frame-Options.
  • Use cookies com atributos HttpOnly e Secure para reduzir o risco de roubo de sessão.
  • Evite expor dados sensíveis no front-end e minimize o uso de dados de configuração.

Exemplo de código seguro para manipular entrada do usuário:

// Forma segura de exibir texto do usuário
const userInput = document.getElementById('input').value;
document.getElementById('output').textContent = userInput; // não interpreta HTML

Em resumo, a segurança no front-end é uma responsabilidade contínua que exige atenção aos detalhes e atualização constante. Ao aplicar essas práticas, você reduz significativamente os riscos de ataques e protege seus usuários.

Referências

Exercícios

  1. Explique a diferença entre XSS refletido, armazenado e baseado em DOM, dando um exemplo de cada.

    ✓ Resposta: XSS refletido ocorre quando o payload malicioso é enviado na requisição (ex.: em um parâmetro de URL) e é refletido na resposta sem sanitização, sendo executado no navegador da vítima. Exemplo: http://site.com/pesquisa?q=<script>alert(1)</script> e a página exibe o valor sem escapar. XSS armazenado ocorre quando o payload é persistido no servidor (ex.: em um comentário) e é exibido a todos os usuários que acessam a página. Exemplo: um usuário posta <script>roubarCookie()</script> em um fórum. XSS baseado em DOM ocorre quando o código JavaScript manipula o DOM de forma insegura, usando dados não confiáveis sem sanitização, sem que o servidor esteja envolvido. Exemplo: document.write(location.hash).
  2. Qual é a diferença entre innerHTML e textContent? Por que textContent é mais seguro?

    ✓ Resposta: innerHTML interpreta o conteúdo como HTML, permitindo a execução de scripts maliciosos se o conteúdo não for sanitizado. textContent trata o conteúdo como texto puro, exibindo as tags HTML como texto, sem interpretá-las. Portanto, textContent é mais seguro porque não permite a execução de código HTML/JavaScript.
  3. Escreva uma função em JavaScript que sanitize uma string para evitar XSS, escapando caracteres especiais HTML.

    ✓ Resposta:
    function sanitizeHTML(str) {
      return str.replace(/[&<>"']/g, function(m) {
        const map = {
          '&': '&',
          '<': '<',
          '>': '>',
          '"': '"',
          "'": '''
        };
        return map[m];
      });
    }
    
  4. O que é Content Security Policy (CSP) e como ela ajuda a prevenir XSS? Cite uma configuração básica.

    ✓ Resposta: CSP é um mecanismo de segurança que permite controlar quais recursos o navegador pode carregar, como scripts, estilos e imagens. Ela ajuda a prevenir XSS ao bloquear a execução de scripts inline não autorizados e scripts de origens não confiáveis. Uma configuração básica é: Content-Security-Policy: default-src 'self'; script-src 'self', permitindo apenas scripts do próprio domínio.
  5. Liste pelo menos cinco boas práticas de segurança no front-end além de sanitização e CSP.

    ✓ Resposta: 1) Validar todas as entradas do usuário. 2) Evitar o uso de eval() e new Function(). 3) Manter as dependências atualizadas e usar npm audit. 4) Usar cookies com atributos HttpOnly e Secure. 5) Configurar cabeçalhos de segurança como X-Content-Type-Options e X-Frame-Options. 6) Minimizar a exposição de dados sensíveis no front-end.

Observações finais

Segurança no front-end não é um tópico isolado, mas uma prática contínua que deve ser integrada ao processo de desenvolvimento. Sempre teste sua aplicação em busca de vulnerabilidades e mantenha-se atualizado sobre novas ameaças e técnicas de mitigação. Lembre-se de que a segurança é uma responsabilidade compartilhada entre o front-end e o back-end; nunca confie apenas em uma camada.