Skip to content
This page has been auto-translated and may contain errors.View in English

Introdução à Cibersegurança

A maioria dos bugs de segurança em aplicações web são erros de programação comuns com uma consequência incomum.

Uma query é montada colando strings umas nas outras. Um template escreve o nome de um visitante diretamente na página. Uma rota lê um id da URL e retorna o registro sem verificar quem fez o pedido.

Cada um desses casos parece razoável durante a revisão, até o momento em que você vê a requisição que se aproveita dele.

Trabalhar com segurança é, na maior parte do tempo, o hábito de se perguntar o que uma versão hostil desse input faria.

Cada um desses bugs se encontra em uma fronteira de confiança, que é a ideia mais útil deste manual.

O que é uma fronteira de confiança?

Uma fronteira de confiança é a linha entre uma parte do seu sistema que você controla e uma parte que você não controla: o navegador, uma API de terceiros, um arquivo enviado por upload. Tudo que atravessa essa linha precisa ser verificado do outro lado.

Como pensar sobre segurança mostra onde traçar essas linhas em uma aplicação web típica.

O que este manual cobre

Isto é segurança de aplicações para desenvolvedores: a segurança do código que você escreve, revisa e coloca em produção.

Não cobre segurança de redes, análise de malware, monitoramento de blue team, operações de red team ou frameworks de conformidade. Esses são campos extensos, com literatura própria, e quem procura por eles vai ser mais bem atendido em outro lugar.

Todos os exemplos de código são em Node e Express, com Zod para descrever o formato dos dados que você espera, de modo que os exemplos se pareçam com um serviço que você já poderia estar mantendo.

Os capítulos seguem em quatro partes, pensadas para serem lidas em ordem na primeira leitura, porque as últimas se apoiam no vocabulário que a primeira estabelece.

ParteO que cobreComece em
1 · Pensando como um atacanteIdentificar por onde dados externos entram, STRIDE para prever ameaças, OWASP para nomear as que já foram encontradas, e como redigir um relatório de bugComo pensar sobre segurança
2 · Segurança de entrada e dadosOs ataques famosos, que acabam sendo o mesmo erro com fantasias diferentes, e depois a validação que os fecha de uma só vezNunca confie no input do usuário
3 · Autenticação e identidadeO HTTP esquece você entre requisições, então todo app escolhe uma forma de lembrar. Três formas, comparadasAutenticação vs autorização
4 · Limitação e controle de taxaManter o serviço respondendo quando o tráfego não coopera. Cinco algoritmos e duas respostasFundamentos de rate limiting
JunoO que este manual cobre Ficamos em um canto específico da segurança: o código que você escreve e revisa, em Node e Express com o Zod verificando o que chega.

Quatro partes, começando em como pensar e terminando em manter um serviço de pé. Eu ainda volto para a primeira parte mais do que para qualquer outra.

JunoO que este manual cobre O escopo é segurança de aplicações em Node, Express e Zod. Redes, malware, blue team e conformidade ficam de fora.

Leia as partes em ordem na primeira vez. A parte 3 assume o vocabulário de input da parte 2, e a parte 4 assume que você já sabe identificar um cliente, o que a parte 3 estabelece.

JunoO que este manual cobre O escopo é estreito de propósito: o código pelo qual você responde, e nenhum dos outros campos arquivados sob a mesma palavra.

Vale saber o que essa restrição custa. Armazenamento de senhas, padrões de controle de acesso, CSRF, cabeçalhos de resposta e risco de cadeia de suprimentos são mencionados onde um capítulo precisa deles, mas nenhum tem um capítulo próprio.

Quando você encontrar um desses de verdade, vá até o cheat sheet correspondente da OWASP. Este manual vai ter te dado o vocabulário para lê-lo.

O que você precisa saber antes

Você vai aproveitar mais este material se souber escrever um pouco de JavaScript e já tiver visto um pequeno servidor Node e Express. O suficiente para reconhecer uma rota e uma função de middleware quando aparecerem.

Uma ideia faz mais trabalho aqui do que qualquer outra. Quando alguém visita seu site, o navegador dela envia uma requisição: o endereço que ela pediu, alguns cabeçalhos e, muitas vezes, um corpo carregando o que quer que ela tenha digitado.

Cada uma dessas partes foi escrita por essa pessoa, incluindo as que sua própria página preencheu.

Nada impede alguém de ignorar sua página e enviar o que bem entender.

Um detalhe do Express merece destaque logo de início, já que a maioria das correções depende dele. O middleware roda na ordem em que você o registra, então uma verificação precisa vir antes da rota que confia no resultado dela. Uma registrada depois não protege nada.

O manual de JavaScript cobre o lado da linguagem, e o capítulo sobre o DOM prepara o terreno para os capítulos daqui que tratam de escapar o que você escreve em uma página.

JunoO que você precisa saber antes JavaScript e um pouco de Node já bastam para começar. A ideia para levar com você é que toda parte de uma requisição foi digitada por quem a enviou, incluindo os campos que sua própria página preencheu.

Levei um tempo para parar de tratar meu próprio front-end como uma fonte confiável, e quase tudo aqui fica mais fácil assim que você faz o mesmo. Também não se assume nenhum conhecimento prévio de segurança.

JunoO que você precisa saber antes Trate todo campo de req como controlado pelo atacante. A requisição chega por várias propriedades, e uma verificação em uma delas não diz nada sobre as outras: req.params guarda os segmentos do caminho, req.query a query string, req.body o corpo já processado, req.headers o resto.

Então um schema aplicado em req.body deixa sem nenhuma verificação um handler que depois lê req.query.sort.

JunoO que você precisa saber antes O modelo de ameaça é a requisição inteira: cabeçalhos, cookies, corpo, caminho, todo campo que seu próprio cliente preenche.

Leve também o runtime em consideração. O raciocínio se transporta entre stacks; os padrões e o modelo de concorrência raramente. O event loop único do Node é o caso mais evidente, já que uma expressão regular construída para fazer backtracking, ou uma chamada de hash síncrona em um handler, trava todas as outras requisições em andamento.

Um bug de tamanho de input que um servidor com uma thread por requisição absorve como latência aqui se torna uma falha de disponibilidade.

Como ensinamos vulnerabilidades

Todo capítulo que cobre uma fraqueza real segue os mesmos quatro passos, então, depois de ler um, você já sabe se orientar em todos os outros.

  1. O código com falha, curto o bastante para ler de uma vez e parecido com algo que você encontraria em um projeto real.
  2. O payload, o input ou requisição real que quebra o código.
  3. Por que funciona, rastreado pelo código, porque uma correção que você não consegue explicar é uma correção que vai acabar desfazendo mais tarde.
  4. A correção, com uma nota sobre as variantes que ela cobre e as que não cobre.

No tamanho que uma introdução comporta, os quatro passos ficam assim:

js
// Vulnerável: o nome é colado diretamente no texto da query
app.get('/users', (req, res) => {
  const rows = db.query(`SELECT * FROM users WHERE name = '${req.query.name}'`)
  res.json(rows)
})

// Payload: /users?name=' OR 1=1 --
// A aspa fecha a string antes da hora, então OR 1=1 entra na query como uma
// condição própria e casa com todas as linhas da tabela.

// Corrigido: o valor viaja ao lado da query e nunca é interpretado como SQL
const rows = db.query('SELECT * FROM users WHERE name = ?', [req.query.name])

Mostrar o payload é proposital. Injeção descrita de forma abstrata passa despercebida, e o mesmo bug é difícil de notar às três da tarde no pull request de outra pessoa.

Uma fronteira vale para tudo

Esses payloads estão aqui para você conseguir encontrar essa classe de bug no código pelo qual você é responsável. Rode-os contra sistemas que você possui ou para os quais tem permissão explícita de teste, e em nenhum outro lugar.

O manual também respeita esse limite: nada de ferramentas de varredura, nada de alvos de terceiros, nada de evasão de detecção, e nenhum passo a passo de como encadear uma fraqueza em outra.

JunoComo ensinamos vulnerabilidades Ver um payload funcionando pode parecer estranho no início, como se você estivesse recebendo algo proibido. É o mesmo motivo pelo qual uma aula sobre segurança alimentar mostra como é um alimento estragado: reconhecer isso é o objetivo todo.

A regra para levar com você é rodar essas coisas apenas nos seus próprios projetos.

JunoComo ensinamos vulnerabilidades Os mesmos quatro passos sempre: código vulnerável, payload, por que funciona, a correção. Leia o payload com atenção toda vez, porque é isso que você vai reconhecer por comparação durante revisões mais tarde.

O passo que as pessoas pulam é o terceiro. Uma correção adotada sem entendimento acaba sendo removida pela próxima pessoa que a achar inconveniente.

JunoComo ensinamos vulnerabilidades A explorabilidade separa um achado que vale a pena corrigir nesta semana de uma anotação no backlog, e só dá para julgar isso com um payload concreto. É por isso que cada capítulo traz um.

O quarto passo importa tanto quanto os outros. Uma correção sem escopo declarado vira folclore: alguém a aplica em um caso que ela nunca cobriu, e a brecha sobrevive a uma revisão que parecia completa. Toda correção aqui diz o que fecha e o que não fecha.

Conheça seus guias

Três guias conduzem este manual, e você escolhe qual deles vai te acompanhar.

Todo mundo lê a mesma base, escrita de forma simples para quem está encontrando segurança pela primeira vez. Escolher um guia mais aprofundado adiciona material por cima: o padrão mais amplo, os casos que a primeira correção deixa passar, as escolhas de compromisso por trás dela. Seu guia também define o tom.

Troque de guia a qualquer momento pelo seletor no topo de qualquer página, ou abra Preferências no canto inferior direito para mudar o guia ou o tamanho da fonte.

O nível é a profundidade que você quer no momento, então mude sempre que um capítulo parecer raso ou denso demais. Subir de nível no meio do caminho adiciona o material extra no lugar, mantendo o que você já leu como estava.

JunoConheça seus guias Olá de novo, sou eu. Iniciante aqui não significa iniciante em programação, significa novo em segurança, e isso é a maioria das pessoas.

Se um capítulo parecer pesado, tente descer de nível só naquela página. Nada se perde, e a base é a mesma por baixo.

JunoConheça seus guias A página base é a mesma, não importa qual guia você escolha. O que muda é quem está ao seu lado e até onde essa pessoa aprofunda.

Vale a pena alternar de nível em um assunto que você já domina bem: é a forma mais rápida de descobrir qual nível te serve melhor para o resto da trilha.

JunoConheça seus guias A profundidade aqui é julgamento de produção, não detalhes internos por si só: o que uma correção custa, onde ela para de funcionar, e qual escolha de compromisso você está aceitando.

Escolha este nível porque é a profundidade que você quer hoje, não por causa de há quanto tempo você faz isso. Muitos desenvolvedores experientes leem o caminho base num assunto que não conhecem bem e depois voltam para cá.

Para onde isso vai a seguir

Quatro partes, trinta e dois capítulos, um exemplo funcional quebrado e consertado ao longo de todo o manual.

O que é cibersegurança? delimita a palavra antes de qualquer vocabulário chegar, e então como pensar sobre segurança inicia o hábito sobre o qual o resto do manual funciona.

Prefere aprender construindo?O curso Learn Cybersecurity da Scrimba cobre o mesmo conteúdo por meio de desafios interativos que você resolve no navegador.