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.
-
Nomes que dizem a verdade
Quando um valor vem de fora, o nome deve lembrá-lo:
rawEmailouuntrustedHtmlavisam-te,datanão. -
Uma função, uma tarefa
Uma função que só valida testa-se num minuto. Uma de duzentas linhas que valida, guarda e envia emails esconde as verificações que ficaram por fazer.
-
Erros tratados, não ignorados
Um
catchvazio é um alarme desligado. Regista o erro sem dados sensíveis, mostra ao utilizador uma mensagem genérica e não continues como se nada fosse.
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.
-
A01
Controlo de acessos violadoBroken Access Control
Um utilizador vê ou altera dados que não são seus, por exemplo mudando
/encomendas/1042para/encomendas/1043no endereço.Defesa: verifica as permissões no servidor em cada pedido e nega tudo por omissão.
-
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.
-
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).
-
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.
-
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,
textContentem vez deinnerHTML, validação no servidor. -
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.
-
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
HttpOnlyeSecure. -
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
integritynos scripts externos. -
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.
-
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.
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.
-
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
-
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
-
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 umnpm install. Datadog Security Labs -
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
- Faz commit do lockfile e, na CI, instala com
npm ci: instalas exatamente as versões que verificaste. - Ativa a verificação em dois passos no npm, no GitHub e no painel do alojamento. Uma conta sem 2FA é a porta mais fácil.
- Atualiza com método. O
npm audit, o Dependabot ou o Renovate avisam-te; lê o que muda antes de fazer merge. - Menos dependências. Antes de instalar um pacote, pergunta-te se precisas mesmo dele. Este site não tem nenhuma dependência em runtime: as fontes e os ícones são incluídos durante a build.
- Atenção aos nomes.
reacctnão éreact: quem publica malware escolhe nomes a uma letra de distância dos mais famosos. - Scripts de instalação sob controlo. Com
npm install --ignore-scripts, os pacotes não executam código durante a instalação.
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