Código seguro

Código limpo e código seguro são a mesma coisa.

Uma vulnerabilidade é quase sempre um bug normal que foi parar ao sítio errado. Eis porque é que a clareza importa, quais são os erros mais comuns e o que verificar antes de cada lançamento.

Porque é que o código limpo é mais seguro

O código que se percebe à primeira revê-se melhor, testa-se melhor e esconde menos surpresas. A segurança não é uma camada a acrescentar no fim: nasce dos mesmos hábitos que tornam o código legível.

Um exemplo: a mesma query, escrita de duas maneiras

Vulnerável SQL injection

// o input do utilizador passa a fazer parte da query
const rows = await db.query(
  `SELECT * FROM users
   WHERE email = '${req.body.email}'`
);

Seguro query parametrizada

// a query e os dados viajam separados
const email = String(req.body.email ?? '').trim();
const rows = await db.query(
  'SELECT id, name FROM users WHERE email = $1',
  [email]
);

Se alguém escrever ' OR '1'='1 no campo do email, a primeira versão devolve todos os utilizadores da base de dados. A segunda procura simplesmente um email estranho e não encontra nada. Além disso, pede só as colunas necessárias: se algo correr mal, sai menos informação.

As 10 vulnerabilidades mais comuns

O OWASP Top 10 é a lista de referência dos riscos para aplicações web, mantida pela comunidade OWASP. A edição mais recente é a de 2025: a cadeia de fornecimento de software sobe para o terceiro lugar e entra uma categoria nova sobre a gestão de erros.

  1. A01

    Controlo de acessos violadoBroken Access Control

    Um utilizador vê ou altera dados que não são seus, por exemplo mudando /encomendas/1042 para /encomendas/1043 no endereço.

    Defesa: verifica as permissões no servidor em cada pedido e nega tudo por omissão.

  2. A02

    Configuração incorretaSecurity Misconfiguration

    Palavras-passe predefinidas, painéis de administração expostos, erros que mostram o stack trace, ficheiros públicos por engano.

    Defesa: configurações mínimas e reproduzíveis, nada de modo debug em produção, headers de segurança.

  3. A03

    Cadeia de fornecimento de softwareSoftware Supply Chain Failures

    Dependências comprometidas, pacotes maliciosos, pipelines de build adulteradas. Em 2025 subiu para o terceiro lugar.

    Defesa: lockfile, 2FA, atualizações controladas e menos dependências (falamos disso mais abaixo).

  4. A04

    Falhas criptográficasCryptographic Failures

    Palavras-passe guardadas em texto simples ou com hashes rápidos como MD5, dados sensíveis que circulam sem HTTPS.

    Defesa: HTTPS em todo o lado, palavras-passe com bcrypt, scrypt ou Argon2, e nunca criptografia feita em casa.

  5. A05

    InjeçãoInjection

    O input do utilizador torna-se código: SQL injection, comandos de sistema e também os XSS que experimentas aqui em baixo.

    Defesa: queries parametrizadas, textContent em vez de innerHTML, validação no servidor.

  6. A06

    Design inseguroInsecure Design

    O problema está na ideia, não no código: uma reposição de palavra-passe com uma pergunta fácil de adivinhar, um formulário sem limite de envios.

    Defesa: pergunta-te «como é que isto pode ser abusado?» logo enquanto desenhas a funcionalidade.

  7. A07

    Autenticação fracaAuthentication Failures

    Palavras-passe fracas aceites, tentativas de login ilimitadas, sessões que nunca expiram.

    Defesa: 2FA, limite de tentativas, cookies de sessão HttpOnly e Secure.

  8. A08

    Integridade de software e dadosSoftware or Data Integrity Failures

    Atualizações, scripts ou dados aceites sem verificar a origem, como um script de uma CDN que pode mudar sem saberes.

    Defesa: assinaturas e checksums, atributo integrity nos scripts externos.

  9. A09

    Logs e alertas insuficientesSecurity Logging and Alerting Failures

    Um ataque dura semanas e ninguém dá por isso, porque ninguém regista nem olha.

    Defesa: regista logins falhados e ações sensíveis, com alertas. E nunca palavras-passe nos logs.

  10. A10

    Gestão incorreta de exceçõesMishandling of Exceptional Conditions

    Novidade de 2025: um erro inesperado deixa o sistema num estado inseguro, por exemplo uma verificação de permissões que, se falhar, deixa passar.

    Defesa: em caso de erro, nega o acesso («fail closed»), trata todas as exceções, mostra mensagens genéricas.

Experimenta: como nasce um ataque XSS

O Cross-Site Scripting acontece quando um texto escrito por um utilizador é tratado como HTML. Escreve um comentário aqui em baixo e vê o que o browser faria com innerHTML e com textContent.

Exemplos:

el.innerHTML = comentario

    el.textContent = comentario

    Seguro: o browser mostra o texto tal como está.

    Sem risco: a demo não executa nada. Analisa o texto dentro de um <template>, um contentor inerte onde os scripts não correm e as imagens não carregam. Se precisares mesmo de aceitar HTML dos utilizadores, passa-o primeiro por um sanitizador como o DOMPurify. O browser está a aprender a fazê-lo sozinho com setHTML(), já no Chrome e no Firefox, mas ainda não no Safari.

    O perigo nas dependências

    Um projeto JavaScript médio instala centenas de pacotes escritos por pessoas que não conheces. Desde 2025, este risco tornou-se muito concreto.

    1. O maintainer do chalk, debug e de outros 17 pacotes cai num email de phishing que imita o suporte do npm. Durante poucos minutos saem versões que substituem os endereços das carteiras de cripto, em pacotes que, juntos, somam entre 2 e 3 mil milhões de downloads por semana. Socket

    2. Shai-Hulud, um worm que se replica sozinho: rouba os tokens do npm e do GitHub e as chaves de cloud e, depois, publica versões infetadas de outros pacotes. Mais de 500 pacotes afetados, com um alerta da CISA norte-americana. CISA

    3. O Shai-Hulud 2.0 ativa-se logo durante a instalação, no script preinstall: 796 pacotes com mais de 20 milhões de downloads semanais. Bastava um npm install. Datadog Security Labs

    4. A resposta do npm: os antigos tokens «classic» foram revogados e o login cria agora sessões de duas horas. Em 2026 chegaram a publicação com aprovação por 2FA (staged publishing) e a análise antimalware no momento da publicação. GitHub

    Como te proteger

    Segredos em segurança, headers no sítio certo

    As chaves não se põem no código

    As palavras-passe e as chaves de API ficam nas variáveis de ambiente, num ficheiro .env excluído do Git. E lembra-te: tudo o que acaba no JavaScript do frontend é público, mesmo que se chame SECRET_KEY. É por isso que o Vite só passa ao browser as variáveis que começam por VITE_.

    .gitignore

    .env
    .env.*
    !.env.example

    Se uma chave foi parar a um commit, apagá-la não chega: o histórico do Git guarda-a. Tem de ser revogada e gerada de novo imediatamente.

    Headers de segurança

    Poucas linhas na configuração do servidor fecham famílias inteiras de ataques. Exemplo no formato _headers da Netlify e do Cloudflare Pages:

    _headers

    /*
      Content-Security-Policy: default-src 'self'; object-src 'none'; frame-ancestors 'none'
      Strict-Transport-Security: max-age=31536000; includeSubDomains
      X-Content-Type-Options: nosniff
      Referrer-Policy: strict-origin-when-cross-origin
      Permissions-Policy: camera=(), microphone=(), geolocation=()
    • Content-Security-Policy: decide de onde podem vir scripts e recursos. É a segunda linha de defesa contra os XSS.
    • Strict-Transport-Security: o browser usa sempre HTTPS, mesmo que alguém escreva http://.
    • frame-ancestors 'none': nenhum outro site pode incorporar o teu num iframe (clickjacking).

    Checklist antes do deploy

    Vai marcando à medida que avanças. O progresso fica guardado neste browser.

    Concluídos: 0 de 12