Pular para conteúdo

Segurança

Princípios fundamentais: a tríade CIA

Antes de qualquer mecanismo específico, três princípios orientam qualquer decisão de segurança de sistemas:

Definição: Confidencialidade, Integridade e Disponibilidade (tríade CIA)

  • Confidencialidade — dados sensíveis (senhas, dados de cartão) só podem ser lidos por quem tem permissão. Resolvida principalmente com criptografia, protegendo o dado tanto persistido quanto em trânsito pela rede (SSL/TLS).
  • Integridade — garantir que os dados não sofram alteração não autorizada. Resolvida com assinatura: gerar um hash combinando uma chave e a mensagem; quem recebe reconstrói o hash com a mesma chave e compara com o recebido — se baterem, a mensagem não foi alterada no caminho. É o mesmo princípio usado pelo TLS para garantir integridade durante o tráfego.
  • Disponibilidade — o sistema continuar acessível, mesmo sob ataque (ex.: ataques que visam deixar o sistema fora do ar) ou sob demanda alta. Estratégias comuns: rate limit (limitar quantas requisições um cliente pode fazer num intervalo), redundância de infraestrutura, atualização do sistema operacional.

Definição: Função hash

Uma cadeia de caracteres gerada a partir de outra cadeia (a entrada), através de um algoritmo determinístico — a mesma entrada sempre produz o mesmo hash, e o resultado costuma ter tamanho fixo, independente do tamanho da entrada.

Criptografia simétrica x assimétrica

Definição: Criptografia simétrica x assimétrica

Na criptografia simétrica, a mesma chave é usada tanto para criptografar quanto para descriptografar os dados — mais rápida, mas exige que quem criptografa também tenha acesso à chave usada para reverter a operação. Na criptografia assimétrica, um par de chaves é usado: uma pública (para criptografar) e uma privada (para descriptografar) — quem criptografa nunca precisa ter acesso à chave que reverte a operação, útil quando o lado que criptografa não é totalmente confiável (ex.: um frontend web).

AES (Advanced Encryption Standard)

Definição: AES

Um dos algoritmos de criptografia simétrica mais utilizados no mundo, estabelecido como padrão pelo NIST (Instituto Nacional de Padrões e Tecnologia dos EUA) em 2001, substituindo o DES (Data Encryption Standard), que se tornou obsoleto por vulnerabilidades. É um cifrador de blocos: divide os dados em blocos de tamanho fixo (128 bits) e aplica várias rodadas de transformações matemáticas (SubBytes, ShiftRows, MixColumns, AddRoundKey) que tornam os dados irreversíveis sem a chave correta — o número de rodadas varia com o tamanho da chave (10 para AES-128, 12 para AES-192, 14 para AES-256).

Definição: Tamanho de chave — segurança x performance

O AES suporta chaves de 128, 192 e 256 bits. Quanto maior a chave, mais segura, porém mais lenta a operação. AES-128 é preferível quando a performance é crucial (dispositivos com recursos de hardware limitados); AES-192/256 oferecem maior segurança, mais indicados para aplicações de alta sensibilidade. Um ataque de força bruta contra uma chave de 128 bits precisaria testar 2¹²⁸ combinações — considerado inviável na prática, mesmo com o algoritmo sendo público.

Definição: Modos de operação do AES

Como o AES cifra em blocos fixos, os modos de operação definem como blocos sucessivos se relacionam entre si:

  • ECB (Electronic Codebook) — trata cada bloco de forma independente; inseguro, pois blocos de dados iguais produzem blocos criptografados iguais, revelando padrões repetitivos. Deve ser evitado.
  • CBC (Cipher Block Chaining) — encadeia os blocos com o auxílio de um vetor de inicialização (IV), tornando a criptografia mais robusta.
  • CFB/OFB (Cipher/Output Feedback) — transformam o AES num cifrador de fluxo.
  • GCM (Galois/Counter Mode) — amplamente usado em comunicações seguras, por combinar criptografia e autenticação (integridade) no mesmo mecanismo.
public class CryptoUtil {
    private static final String ALGORITHM = "AES";

    public static String encrypt(String data, SecretKey key) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(data.getBytes());
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            return null;
        }
    }

    public static String decrypt(String encryptedData, SecretKey key) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
            return new String(decryptedBytes);
        } catch (Exception e) {
            return null;
        }
    }
}

Definição: AES x RSA — quando usar cada um

O RSA (Rivest, Shamir e Adleman) é um algoritmo de criptografia assimétrica ainda mais seguro que o AES para o armazenamento da chave em si — como usa uma chave pública para criptografar e uma chave privada (diferente) para descriptografar, mesmo que alguém obtenha acesso à chave pública, não consegue reverter a criptografia. Prefira AES para criptografar valores transitando entre serviços de backend numa rede interna, onde as duas pontas já são confiáveis; opte por RSA quando a criptografia ocorre no lado do cliente (aplicações client-side, como um frontend web), já que ele nunca precisa ter acesso à chave que descriptografa os dados. Usar RSA implica maior complexidade de implementação.

Definição: Gestão de chaves e segredos

A segurança do AES depende inteiramente de suas chaves serem grandes e protegidas — se a chave for comprometida, a segurança se perde por completo, não importa quão forte seja o algoritmo. Nunca deixar a chave hardcoded no código-fonte ou em texto claro num arquivo de propriedades versionado: o valor deve vir de uma variável de ambiente, idealmente injetada a partir de um gerenciador de segredos dedicado (AWS Secrets Manager, HashiCorp Vault) — acessível só a pessoas autorizadas (analistas de segurança, lideranças do projeto), nunca a todo o time de desenvolvimento.

Criptografia em trânsito x em repouso

Definição: Criptografia em trânsito x em repouso

Em repouso (at rest) protege os dados enquanto persistidos — num banco de dados, num tópico Kafka, num arquivo em disco — tipicamente aplicada pela própria aplicação (ex.: com AES, antes de gravar). Em trânsito (in transit) protege os dados enquanto trafegam pela rede, entre cliente e servidor ou entre serviços — responsabilidade do protocolo de transporte (HTTPS/TLS), não da aplicação. As duas são complementares e nenhuma substitui a outra: um dado pode estar protegido em repouso, mas se a conexão que o transporta não for HTTPS, ele ainda trafega exposto (ou vice-versa).

Definição: Como o HTTPS garante a criptografia em trânsito

Ao estabelecer uma conexão HTTPS, um processo (simplificado) acontece antes de qualquer dado de negócio trafegar:

  1. Uma conexão TCP é estabelecida entre cliente e servidor.
  2. O servidor envia seu certificado (contendo sua chave pública).
  3. O cliente valida que o certificado pertence de fato ao servidor, consultando entidades certificadoras.
  4. O cliente gera uma chave de sessão e a criptografa usando a chave pública do servidor.
  5. O cliente envia a chave de sessão criptografada para o servidor.
  6. A conexão HTTPS é estabelecida — todos os dados a partir daqui trafegam criptografados com a chave de sessão.
  7. A segurança é garantida porque só cliente e servidor têm acesso ao valor da chave de sessão.

Definição: Por que isso protege contra ataques MITM (Man-In-The-Middle)

Um ataque MITM tenta interceptar a comunicação entre cliente e servidor (por exemplo, por meio de um proxy malicioso posicionado no meio do caminho). Como toda a comunicação a partir do handshake HTTPS trafega criptografada com uma chave de sessão que só cliente e servidor conhecem, um atacante que intercepta os pacotes não consegue decifrar o conteúdo — mesmo tendo acesso físico ao tráfego de rede.

Definição: HTTPS não substitui a criptografia da aplicação

Mesmo com HTTPS garantindo a criptografia em trânsito, os dados costumam trafegar em texto claro dentro do corpo da requisição (o HTTPS criptografa o canal, não exige que a aplicação também criptografe o payload) — por isso, dados sensíveis (como os de pagamento) costumam ser criptografados duas vezes: uma vez pela própria aplicação antes de persistir (repouso) e outra pelo HTTPS enquanto trafegam (trânsito), cada uma resolvendo uma preocupação diferente.

O problema que o OAuth resolve: repassar credenciais é anti-pattern

Imagine um sistema de livros ("bookserver") que precisa exibir, numa rede social, os livros que o usuário já leu. A solução ingênua — o usuário informar à rede social o mesmo login e senha que usa no bookserver, para que a rede social acesse a API em nome dele — tem um problema sério:

Definição: Anti-pattern de repasse de credenciais

Ao repassar login e senha de um sistema para outro, o segundo sistema passa a ter os mesmos acessos do usuário — sem nenhum controle sobre o que pode e o que não pode fazer em nome dele (poderia cadastrar, alterar ou até excluir dados, não só ler). Além disso, o usuário perde o controle de revogar esse acesso sem trocar sua própria senha em todo lugar onde ela é usada.

Definição: Delegação de acesso

Em vez de repassar a credencial em si, o sistema original gera uma credencial temporária — um token de acesso — com escopo limitado (só o que for necessário para aquela integração específica), que pode ser revogado independentemente da senha do usuário. É a mesma lógica de um cartão de visitante num condomínio: o morador não entrega a chave do próprio apartamento a um prestador de serviço — a administração emite um cartão à parte, com acesso restrito (só a portaria, não a piscina) e validade limitada.

sequenceDiagram
    participant U as Usuário
    participant RS as Rede Social
    participant AL as Aplicação de Livros
    U->>AL: 1. solicita um token de acesso
    AL-->>U: token de acesso
    U->>RS: 2. repassa o token
    RS->>AL: 3. usa o token para consultar livros

Ao entregar o token (em vez da senha) para a rede social, o usuário está delegando o acesso aos seus próprios recursos para um terceiro, sem nunca expor a credencial principal.

Do OAuth 1.0 ao OAuth 2.0

Antes de existir um padrão, cada grande empresa resolvia esse problema à sua própria maneira — o Google tinha ClientLogin e AuthSub, o Yahoo! tinha o BBAuth. A falta de um padrão comum levou o Twitter e outras empresas a criarem, em conjunto com a comunidade, o protocolo OAuth 1.0.

Definição: Por que o OAuth evoluiu para 2.0

O OAuth 1.0 resolvia bem o caso de aplicações web (o usuário é redirecionado de um sistema a outro), mas não previa cenários que se tornaram comuns depois: apps nativos (mobile), aplicações JavaScript rodando direto no navegador, e o caso de uma aplicação acessar dados em benefício próprio (não em nome de um usuário). O OAuth 2.0 foi desenhado para cobrir esses cenários, com três eixos de evolução: suporte a diferentes tipos de aplicação, facilidade para quem desenvolve, e extensibilidade.

Definição: Cliente confidencial x cliente público

O OAuth 2.0 classifica cada aplicação que consome a API (client) conforme sua capacidade de proteger uma credencial: um cliente confidencial roda no lado do servidor (o código não é exposto a quem usa a aplicação) e consegue manter segredos protegidos com segurança; um cliente público roda no dispositivo do usuário (app mobile, aplicação JavaScript no navegador) e não tem como guardar um segredo de forma confiável — qualquer coisa embutida no app pode, em princípio, ser extraída por quem tem acesso ao dispositivo. Essa diferença é o que leva o OAuth 2.0 a oferecer fluxos diferentes (grant types) para cada tipo de cliente — ver a seguir.

Definição: Os três client profiles do OAuth 2.0

  • Aplicação web — servidor processando os dados e a navegação; confidencial (o servidor mantém credenciais e tokens protegidos).
  • Aplicação baseada em browser — executa direto no navegador (JavaScript); não tem como esconder credenciais do usuário final, então é tratada como pública.
  • Aplicação nativa — instalada no dispositivo (mobile/desktop); também pública (um app pode ser descompilado), mas com algum nível de proteção a mais que uma aplicação em browser — outros apps do mesmo sistema não conseguem acessar seus dados, já que cada um roda em processo isolado.

Definição: TLS x SSL

TLS (Transport Layer Security) é a evolução do SSL (Secure Socket Layer) — ambos protegem requisições feitas pela internet, e é isso que o HTTPS usa por trás dos panos. A diferença prática: TLS permite autenticação mútua (não só o servidor prova sua identidade ao cliente, o cliente também pode provar a sua ao servidor).

Definição: Por que o OAuth 2.0 abandonou a assinatura de requisições

O OAuth 1.0 exigia que toda requisição fosse assinada (montar uma string a partir dos campos da requisição e assiná-la com HMAC-SHA1 ou RSA-SHA1) — um processo trabalhoso de implementar corretamente. O OAuth 2.0 dispensa esse trabalho, exigindo em troca que toda comunicação passe por uma camada de transporte segura (TLS/SSL). Essa mudança foi (e ainda é) controversa: exigir assinatura tornava cada requisição verificável independentemente do canal; depender só do TLS significa que, se a camada de transporte for comprometida, não há segunda camada de proteção. O próprio ex-líder da especificação do OAuth 2.0 (Eran Hammer) criticou publicamente essa decisão, citando também a necessidade de gerenciar refresh tokens (ver mais adiante) que a expiração mais agressiva de tokens no OAuth 2.0 trouxe como consequência.

Definição: OAuth 2.0 é um framework, não um protocolo

A própria RFC 6749 descreve o OAuth 2.0 como um "framework de autorização", não um protocolo. A diferença: um protocolo é um conjunto fechado de normas e regras; um framework é uma solução genérica, extensível, que serve de guia para construir algo (aqui, soluções de delegação de acesso). É por isso que duas implementações diferentes de OAuth 2.0 podem não ser diretamente interoperáveis entre si — a especificação deixa dois pontos de extensão em aberto por design: grant types (as formas de uma aplicação cliente obter autorização, escolhidas conforme o tipo de cliente) e token types (os tipos de token de acesso e como trabalhar com cada um — ex.: tokens assinados x tokens autocontidos, ver JWT).

Os quatro papéis do OAuth 2.0

graph LR
    RO["Resource Owner<br/>(usuário)"] -->|autoriza| C[Client]
    C -->|solicita token| AS[Authorization Server]
    AS -->|token de acesso| C
    C -->|acessa recurso com token| RS[Resource Server]

Definição: Resource Owner

Dono dos recursos — geralmente o usuário final, mas também pode ser a própria aplicação (quando ela acessa dados em benefício próprio, não em nome de um usuário — ver Client Credentials, adiante). Recurso, no contexto de OAuth, é qualquer coleção de dados de um usuário que o Resource Owner controla o acesso — numa API REST, tipicamente representado por uma URI.

Definição: Client

A aplicação para a qual o Resource Owner concede permissão — o app que acessa recursos em nome do usuário (ou em nome próprio). Interage com todos os outros papéis: com o Resource Server para acessar um recurso, com o Authorization Server para solicitar autorização.

Definição: Authorization Server e Resource Server

Juntos, formam o que a especificação chama de OAuth Provider. O Authorization Server é responsável por emitir tokens de acesso ao Client — só depois de o Resource Owner se autenticar e autorizar aquele acesso. O Resource Server é quem efetivamente guarda os recursos protegidos e valida se o token apresentado pelo Client é válido (podendo consultar o próprio Authorization Server para isso, ou validar um token autocontido sem precisar dessa comunicação extra — ver JWT, mais adiante).

Definição: Separação lógica x física entre Authorization Server e Resource Server

Os dois papéis podem viver na mesma aplicação (separação lógica — funciona bem quando só um sistema precisa de proteção) ou em aplicações distintas (separação física — permite um único Authorization Server servir vários Resource Servers, e reduz a superfície de ataque: comprometer um não compromete automaticamente o outro, "não colocando todos os ovos na mesma cesta").

Registrando o Client

Antes de qualquer fluxo de autorização acontecer, o Client precisa ser reconhecido pelo Authorization Server através de um registro prévio — como o cadastro que se faz ao integrar um aplicativo com o login do Facebook. A especificação não define como esse registro deve funcionar (fica a critério de cada Authorization Server), mas recomenda informar dois dados centrais:

Definição: client_id / client_secret

No registro, o Authorization Server gera um identificador único (client_id, não sigiloso) e, opcionalmente, uma chave secreta (client_secret) para o Client se autenticar sempre que houver comunicação com o Authorization Server. Um Client confidencial recebe e usa client_secret; um Client público, por não conseguir proteger segredos, tipicamente não.

Definição: Redirect URI — por que registrá-la importa

A URI de redirecionamento indica para onde o Authorization Server deve devolver o usuário (com um código de autorização) depois que ele concede permissão. Registrar essa URI antecipadamente evita um ataque real e documentado: sem essa validação, uma URI maliciosa poderia ser injetada no fluxo, fazendo o Authorization Server entregar o token de acesso para a aplicação errada — um caso assim foi identificado e corrigido no próprio PayPal em 2016.

O fluxo básico de obtenção do access token

Apesar de variar em detalhe entre os quatro grant types (Authorization Code, Resource Owner Password Credentials, Implicit, Client Credentials), o fluxo mais completo (e o que deu origem ao protocolo, o Authorization Code) segue três fases:

  1. Autorização — o usuário se autentica diretamente no Authorization Server (nunca informando login/senha ao Client) e autoriza o acesso.
  2. Solicitação do token — o Client troca o que recebeu na autorização por um token de acesso junto ao Authorization Server.
  3. Uso do token — o Client apresenta o token ao Resource Server para acessar o recurso.

Definição: authorization_code

Código de curta duração (recomendado no máximo 10 minutos) que o Authorization Server entrega ao Client, via redirecionamento, depois que o Resource Owner autoriza o acesso — token intermediário, trocado na fase seguinte por um token de acesso de verdade. Não confundir com o grant type "Authorization Code" (o nome do fluxo inteiro) — authorization_code é só o parâmetro devolvido numa das etapas dele.

Na fase de autorização, o Client também deveria enviar um parâmetro state na URI de redirecionamento — uma medida de segurança contra um tipo de ataque específico (ver considerações de segurança, mais abaixo).

Na fase de solicitação do token, se o Client for confidencial, ele precisa se autenticar perante o Authorization Server (tipicamente via HTTP Basic Authentication, usando client_id/client_secret) — sem essa etapa, qualquer aplicação que roubasse um authorization_code válido poderia trocá-lo por um token de acesso se passando pelo Client verdadeiro.

Enviando o token de acesso numa requisição

Definição: Três formas de enviar um Bearer Token

  • Header Authorization (Authorization: Bearer <token>) — forma recomendada: mais simples e mais segura que as outras duas.
  • Parâmetro de formulário (Content-Type: application/x-www-form-urlencoded, corpo access_token=...) — só funciona com POST, então limita endpoints que deveriam usar outros métodos HTTP (ex.: GET para listar recursos).
  • Parâmetro de URI (GET /livros?access_token=...) — a mais arriscada: o token fica registrado em logs de servidor (qualquer um com acesso aos logs consegue lê-lo depois). Só deveria ser usada quando as outras duas não são possíveis.

Definição: Bearer Token — a analogia do dinheiro

Um Bearer Token ("token de portador") pode ser usado por qualquer um que o possua — como uma nota de dinheiro: quem a encontra pode gastá-la, sem ninguém perguntar sua origem. É diferente de um cartão de débito, que exige uma senha adicional para ser usado por outra pessoa. Existe também o MAC Token, uma alternativa que embute dados adicionais codificados — mas Bearer é, de longe, o tipo de token mais comum em implementações reais.

Definição: Refresh token

Tokens de acesso costumam ter vida curta — reduz o estrago se um token vazar, mas obrigaria o usuário a reautorizar o Client toda vez que o token expirasse. O refresh token resolve isso: o Client troca o refresh token por um novo token de acesso diretamente com o Authorization Server, sem precisar incomodar o Resource Owner de novo.

Por que OAuth 2.0 não é autenticação

Definição: Autenticação x autorização (revisitado)

Autenticação confirma que um usuário é mesmo quem diz ser (login/senha, biometria, ...). Autorização confirma se um usuário tem permissão para acessar ou modificar algo.

Definição: Por que usar OAuth 2.0 para autenticar é um erro comum

Um token de acesso não identifica quem está de fato o utilizando — o próprio Client nunca precisa saber quem é o Resource Owner para acessar o Resource Server com um token válido. Mesmo mantendo uma associação entre o token e um dono conhecido, nada garante que o token realmente chegou às mãos do dono verdadeiro (ele pode ter sido roubado ou repassado). Usar OAuth 2.0 sozinho como solução de autenticação é, por definição, incorreto — para isso existe uma extensão própria, construída sobre o OAuth 2.0: o OpenID Connect (ver mais adiante).

Os quatro grant types

Cada grant type resolve a obtenção do token de acesso de uma forma diferente, apropriada a um perfil de cliente e cenário específicos.

Resource Owner Password Credentials

Definição: Password Credentials grant type

O Client coleta login e senha do Resource Owner diretamente (numa tela própria do Client, não do Authorization Server) e os troca por um token de acesso — sem guardar login/senha no banco do Client, evitando a duplicação de credenciais e o anti-pattern do capítulo 1. Só deve ser usado quando o Client é altamente confiável (tipicamente first-party, do mesmo dono do Authorization Server) — justamente porque as credenciais do usuário passam diretamente por ele.

Authorization Code

Definição: Authorization Code grant type

O fluxo mais completo do OAuth 2.0 (e o que deu origem ao protocolo): o usuário se autentica direto no Authorization Server (nunca no Client), que devolve um authorization_code de curta duração via redirecionamento; o Client troca esse código por um token de acesso, autenticando-se com client_id/client_secret. Uso de authorization_code mais de uma vez é tratado como sinal de roubo — a especificação recomenda que o Authorization Server cancele todos os tokens gerados a partir daquele código, como defesa contra reuso indevido.

Indicado para aplicações confidenciais (web com backend) que conseguem redirecionar o usuário e proteger o token recebido. Também serve para aplicações nativas com suporte a URL scheme customizado, que intercepta a URL de callback. Não deve ser usado quando o Client não pode redirecionar o usuário entre Authorization Server e Client.

Definição: Authorization Code + PKCE (Proof Key for Code Exchange) — o padrão atual

Extensão do Authorization Code, hoje considerada o padrão recomendado para SPAs (aplicações React/Angular/Vue rodando só no browser) e apps mobile — cenários em que não existe backend confiável para guardar um client_secret. Antes de redirecionar o usuário, o Client gera um code verifier (uma string aleatória) e deriva dele um code challenge (hash SHA-256 do verifier), enviando só o challenge no redirecionamento inicial. Na troca do authorization_code por tokens, o Client também envia o code verifier original — o Authorization Server recalcula o hash e confere se bate com o challenge recebido antes. Isso garante que, mesmo que alguém intercepte o authorization_code (ex.: pelo histórico do navegador), não consegue trocá-lo por tokens sem conhecer o code verifier, que nunca trafega na URL. Não exige client_secret nenhum — dispensável, já que a prova de posse é o próprio code verifier.

Implicit

Definição: Implicit grant type

Uma etapa a menos que o Authorization Code: o token de acesso vem direto no fragmento da URI de redirecionamento (#access_token=...), sem uma troca posterior por authorization_code. Pensado para aplicações que executam inteiramente no browser (JavaScript puro, sem backend). Se a aplicação web tiver um servidor por trás, o Authorization Code é preferível por questões de segurança — o token nunca fica exposto na URL/histórico do navegador.

Definição: Implicit Flow foi oficialmente desaconselhado

Com o suporte moderno a fetch e CORS, até SPAs conseguem fazer chamadas seguras ao endpoint de token, com todo o tráfego (e a resposta contendo os tokens) no corpo da requisição, protegido pelo HTTPS — eliminando a necessidade de expor o token na URL. Por isso, o Implicit Flow foi formalmente desaconselhado pela IETF para a maioria dos usos, substituído pelo Authorization Code + PKCE (ver acima). Ainda é suportado por praticamente todos os provedores (Keycloak, Auth0, Google), mas seu uso é fortemente desencorajado em aplicações novas.

Client Credentials

Definição: Client Credentials grant type

O mais simples dos quatro: o Client solicita um token usando só suas próprias credenciais (client_id/client_secret) — sem vínculo com nenhum Resource Owner. Usado quando o Client acessa recursos em benefício próprio, não em nome de um usuário (ex.: comunicação servidor-a-servidor entre microsserviços). Só faz sentido para clientes confidenciais — nunca usar num app mobile, cujas credenciais não podem ser protegidas no dispositivo.

Definição: Client Credentials como alternativa a compartilhar banco de dados

Numa arquitetura de microsserviços, um padrão comum é proteger a comunicação interna entre serviços (numa sub-rede privada) com o mesmo mecanismo OAuth 2.0 já usado nas APIs públicas, em vez de recorrer a um banco de dados compartilhado entre aplicações — compartilhar banco cria acoplamento indesejado (uma mudança de schema afeta várias aplicações, dificultando deploys independentes).

Definição: OAuth 2.0 x HTTP Basic Authentication para comunicação servidor-a-servidor

Se o ecossistema de APIs já usa OAuth 2.0 (Resource Servers já preparados para validar tokens), reaproveitar Client Credentials aproveita a infraestrutura existente e ainda ganha expiração de token. Mas se o sistema em questão não usa OAuth 2.0 em lugar nenhum, adicionar a complexidade do protocolo só para uma comunicação pontual pode não compensar — nesse caso, HTTP Basic Authentication direto é uma escolha mais simples e igualmente razoável.

Device Authorization Flow

Definição: Device Authorization Flow

Fluxo pensado para dispositivos sem navegador disponível (ou com entrada de texto limitada) — o exemplo clássico é autenticar um aplicativo de streaming numa Smart TV. A aplicação exibe um código curto (user code) e uma URL de verificação; o usuário acessa essa URL em outro dispositivo (o celular, por exemplo), faz login e digita o código — enquanto isso, a aplicação na TV fica em polling, checando periodicamente se a autorização já foi concedida, até receber os tokens.

Comparando os quatro

Grant type Client Envolve Resource Owner? Uso típico
Password Credentials Confidencial, alta confiança Sim (credenciais diretas) App first-party do mesmo dono do provider
Authorization Code Confidencial (ou nativo com URL scheme) Sim (via redirecionamento) Aplicação web com backend
Implicit Público (browser) Sim (via redirecionamento) SPA sem backend
Client Credentials Confidencial Não Comunicação servidor-a-servidor

Escopos e roles

Além do próprio token de acesso, o OAuth 2.0 oferece dois mecanismos complementares para restringir o que uma integração pode fazer.

Definição: Refresh tokens — quando fazem sentido

Só valem a pena para grant types onde o Resource Owner precisa estar presente para reautorizar (Password Credentials e Authorization Code) — recorrer a um refresh token evita incomodar o usuário de novo quando o token expira. Não fazem sentido para Client Credentials (não há Resource Owner envolvido — o Client pode simplesmente solicitar um novo token com suas próprias credenciais) nem, tipicamente, para Implicit (onde o token já fica exposto na URL, e a especificação recomenda manter o tempo de expiração mais curto em vez de usar refresh token).

Definição: Escopo (scope)

Delimita o que o Client pode fazer com o token de acesso (ex.: read, write) — apresentado ao Resource Owner na tela de autorização, para que ele aprove ou negue cada escopo individualmente. O escopo efetivamente concedido é a interseção entre o que o Client solicitou, o que o Client está autorizado a pedir (configurado no Authorization Server) e o que o Resource Owner de fato aprovou — pedir um escopo fora dessa interseção resulta em erro (error=invalid_scope, na etapa de autorização, ou access_denied, ao tentar acessar um recurso com escopo insuficiente).

Definição: Escopo x role — não confundir

Escopo restringe o que o Client (a aplicação) pode fazer — é concedido pelo Resource Owner durante a autorização, por integração. Role restringe o que o usuário (Resource Owner) pode fazer no sistema como um todo — é definida pelo Authorization Server, independente de qual Client está sendo usado (ex.: ROLE_ADMIN x ROLE_USUARIO_COMUM). As duas camadas são independentes e compõem juntas: mesmo que o escopo autorizado permita uma ação, a role do usuário ainda pode negá-la (e vice-versa).

Como o Resource Server valida um token

Quando o Authorization Server e o Resource Server são componentes separados (recomendado por segurança — reduz a superfície de ataque ao servidor OAuth), o Resource Server não tem acesso direto ao banco de dados onde os tokens são gerados e armazenados. Como, então, ele confirma que um token apresentado é válido?

Introspecção de tokens

Definição: Introspecção de tokens (RFC 7662)

Mecanismo em que o Resource Server consulta um endpoint dedicado do Authorization Server, enviando o token como parâmetro, e recebe de volta um JSON com os metadados do token (incluindo, obrigatoriamente, o atributo active, indicando se ainda é válido). Pode ser usado para validar qualquer tipo de token OAuth 2.0, inclusive refresh tokens. A comunicação com esse endpoint também deve passar por TLS/SSL, e o Resource Server precisa se autenticar para usá-lo.

Definição: Quando vale a pena usar validação remota (introspecção)

Separar Authorization Server e Resource Server via introspecção reduz o acoplamento entre eles (o Resource Server não precisa mais acessar o banco de dados de tokens diretamente) — mas troca esse acoplamento por uma dependência de rede: cada requisição ao Resource Server agora depende de uma chamada extra ao Authorization Server. Em cenários de alto volume, isso pode comprometer a disponibilidade do sistema (o mesmo princípio da tríade CIA discutido no capítulo 1). Um cache no endpoint de introspecção, com tempo de validade curto (menor que o do próprio token), reduz esse custo sem abrir mão da validação.

Tokens autocontidos com JWT

Definição: Por que JWT existe

Tanto acessar o banco de dados diretamente quanto fazer introspecção remota têm um custo (banco ou rede, respectivamente). O JWT (JSON Web Token) resolve isso de outra forma: o próprio token carrega, de forma compacta e verificável, todos os dados necessários para validá-lo — o Resource Server não precisa perguntar nada a ninguém.

Definição: Estrutura de um JWT — header.payload.signature

Um JWT é uma string composta por três seções separadas por ponto, cada uma codificada em Base64: header (algoritmo usado para assinar/criptografar o token), payload (os dados do token, chamados de claims) e signature (garante a integridade do conteúdo). A família de especificações que cobre essas partes é conhecida como JOSE (JavaScript Object Signing & Encryption): JWS (JSON Web Signature, para tokens assinados), JWE (JSON Web Encryption, para tokens criptografados), JWA e JWK.

Definição: Assinar x criptografar um JWT

Assinar (JWS) gera um hash a partir do conteúdo e de uma chave, garantindo integridade — qualquer um pode ler o conteúdo do token (é só Base64, não criptografia), mas não pode alterá-lo sem invalidar a assinatura. Criptografar (JWE) protege o conteúdo para que só quem tem a chave certa consiga ler os dados. A assinatura é gerada com um algoritmo a partir de duas entradas — a chave e o payload — e o resultado (também chamado de tag) compõe a terceira seção do token. Só o Authorization Server tem a chave de assinatura; o Client recebe e repassa o token como uma string opaca, sem nunca precisar entender sua estrutura interna.

Definição: Claim

Uma afirmação que o token faz sobre si mesmo ou sobre alguma entidade (usuário, sistema). A especificação JWT classifica claims em três tipos: registered (definidas pela própria especificação, com nomes padronizados — iss/issuer quem gerou o token, sub/subject de quem é o token, aud/audience para qual Resource Server ele vale, exp/expiration validade, nbf/not before, iat/ issued at, jti/JWT ID; nenhuma é obrigatória, mas ajudam a manter interoperabilidade entre sistemas), public (definidas livremente por uma aplicação, registradas num processo formal para evitar colisão de nomes) e private (nomes combinados só entre Authorization Server e Resource Server de um sistema específico, sem preocupação com outras aplicações — ex.: o e-mail do Resource Owner).

OAuth 2.0 como base para autenticação: OpenID Connect

Definição: Por que um access token não serve para autenticação

Um access token é, por design, opaco para o Client — ele nunca sabe quem autorizou a geração daquele token (nem o Resource Server, mesmo usando introspecção ou JWT, tem essa garantia — só sabe que o token é válido). Além disso, um access token pode ser renovado via refresh token sem o usuário estar presente, e sua audiência nunca é definida para o Client (só para o Resource Server) — nada nele garante que "quem apresentou este token é, de fato, este usuário, agora".

Definição: OpenID Connect

Especificação construída sobre o OAuth 2.0, especificamente para resolver autenticação — usando o mesmo fluxo de autorização (tipicamente Implicit ou Authorization Code) como base, mas devolvendo, além do access token, um ID Token: um JWT com claims específicas para identificar o usuário. No vocabulário do OpenID Connect, o Authorization Server que também emite o ID Token é chamado de Identity Provider; o Client é chamado de Relying Party.

Definição: Claims do ID Token

Além das claims JWT já conhecidas (iss, sub, aud, exp, iat), o ID Token adiciona claims específicas de autenticação: auth_time (quando o usuário se autenticou), nonce (valor associado ao Client, usado para mitigar um replay attack — reaproveitar um ID Token roubado em uma sessão diferente), acr (mecanismo usado para autenticar o usuário) e azp (a quem o token foi autorizado). O aud do ID Token deve ser o client_id do Relying Party — ao contrário do access token, cuja audiência é o Resource Server.

O fluxo de autenticação segue as mesmas quatro primeiras etapas de um grant type comum (acessar app → redirecionar para autenticação → usuário se autentica e autoriza → Client recebe os tokens), mas devolve dois tokens: access_token (para acessar recursos, se necessário) e id_token (para identificar o usuário). O Client pode ainda consultar um endpoint userInfo no Identity Provider, usando o access token, para obter mais dados do usuário além dos que já vieram no ID Token.

Definição: 'Login com Google/Facebook' é OpenID Connect

Qualquer botão de "entrar com sua conta Google" que um site oferece é, por trás dos panos, uma implementação de OpenID Connect — o site (Relying Party) delega a autenticação para o Google (Identity Provider), recebe um ID Token confirmando quem é o usuário, e associa esses dados de autenticação a uma entidade própria no seu sistema (o mesmo padrão de vincular um id_token/sub a um usuário já cadastrado na aplicação).

Keycloak: um Identity Provider de código aberto

Definição: Identity Provider (IDP) e Keycloak

Um Identity Provider (IDP) é um serviço especializado em gerenciar credenciais, sessões e permissões, eliminando a responsabilidade de cada aplicação reimplementar login e controle de acesso do zero. Keycloak é um IDP completo e de código aberto, mantido pela Red Hat, que implementa tanto OAuth 2.0 quanto OpenID Connect.

Definição: Realm, Client, Roles e Usuários (terminologia do Keycloak)

  • Realm — espaço isolado que agrupa um conjunto de usuários, credenciais, roles e configurações de segurança; realms diferentes não interferem entre si (útil para separar ambientes — desenvolvimento, teste, produção — ou clientes distintos de uma mesma organização).
  • Client — identificador de uma aplicação ou serviço que autentica usuários e acessa recursos protegidos dentro de um realm; pode ser configurado como público ou confidencial, com o fluxo de autenticação que fizer sentido (Authorization Code, Client Credentials, ...).
  • Roles — conjuntos de permissões atribuídos a usuários ou grupos dentro de um realm, no nível global (aplicável a todo o realm) ou específicas de um client.
  • Usuários — entidades que se autenticam e interagem com os clients, cada uma com suas próprias credenciais e roles associadas; podem ser gerenciados diretamente no Keycloak, incluindo configurações adicionais como autenticação multifator (MFA).

Definição: Extraindo roles do JWT emitido pelo Keycloak

Diferente de um JWT genérico com uma claim roles simples, o Keycloak emite as roles específicas de cada client dentro de uma claim aninhada chamada resource_access — é preciso extrair esse valor explicitamente ao configurar o Resource Server, associando-o às autoridades que o Spring Security reconhece.

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
    converter.setJwtGrantedAuthoritiesConverter(jwt -> {
        Collection<GrantedAuthority> authorities = new ArrayList<>();

        // Roles padrão do Spring (scope)
        JwtGrantedAuthoritiesConverter defaultConverter = new JwtGrantedAuthoritiesConverter();
        authorities.addAll(defaultConverter.convert(jwt));

        // Obtém o client que emitiu o token (azp)
        String azp = jwt.getClaimAsString("azp");

        // Só considera tokens emitidos pelos clients permitidos
        List<String> allowedClients = Arrays.asList("book-customer-client", "book-admin-client");
        if (azp != null && allowedClients.contains(azp)) {
            Map<String, Object> resourceAccess = jwt.getClaim("resource_access");
            if (resourceAccess != null && resourceAccess.containsKey(azp)) {
                Map<String, Object> clientRoles = (Map<String, Object>) resourceAccess.get(azp);
                List<String> roles = (List<String>) clientRoles.get("roles");
                roles.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
            }
        }
        return authorities;
    });
    return converter;
}
@PostMapping("/api/book")
@PreAuthorize("hasRole('admin-operations')")
public ResponseEntity<Integer> create(@RequestBody BookRequestDto bookDto) {
    Integer createdId = createBookInputBoundary.execute(mapper.bookRequestDtoToBook(bookDto));
    return new ResponseEntity<>(createdId, HttpStatus.CREATED);
}

Com o JwtAuthenticationConverter configurado, @PreAuthorize("hasRole('...')") nos métodos do REST controller valida a role extraída do token — sem que o endpoint precise conhecer nenhum detalhe de como o Keycloak estrutura suas claims.

Definição: Rede externa x rede interna — nem todo microsserviço deveria ser público

Numa arquitetura de microsserviços madura, só os serviços que atuam como BFF (porta de entrada de uma jornada, ver Microsserviços) deveriam ficar expostos em rede externa — os demais (que só recebem chamadas de outros microsserviços já autenticados e autorizados) deveriam ficar restritos à rede interna, inacessíveis diretamente pela internet. Isso reduz a superfície de ataque do sistema: mesmo que um serviço interno tenha uma falha de segurança, ela só é explorável por quem já está dentro da rede privada.

Considerações de segurança

O OAuth 2.0 é, por design, flexível o suficiente para deixar várias decisões de implementação em aberto — o que também significa que uma implementação descuidada pode introduzir falhas reais, mesmo seguindo a especificação à risca. A RFC 6819 documenta um modelo de ameaças de segurança dedicado ao protocolo; a OWASP (Open Web Application Security Project, organização sem fins lucrativos focada em segurança de software, famosa por sua lista Top 10 de vulnerabilidades mais críticas em sistemas web) é outra referência recomendada para quem quer se aprofundar em segurança de forma mais ampla.

Roubo de token via URI de redirecionamento não validada

Definição: Ataque de troca de redirect_uri

Sem exigir o registro (e a validação completa) da URI de redirecionamento, um atacante pode induzir a vítima a clicar num link de autorização forjado, cujo redirect_uri aponta para um domínio controlado pelo atacante. A vítima, já autenticada e confiando na aplicação legítima, autoriza o acesso normalmente — mas o token de acesso (ou, no grant type Implicit, o próprio token no fragmento da URL) acaba sendo entregue ao atacante, não ao Client verdadeiro. É especialmente grave no grant type Implicit, onde o Client nem se autentica junto ao Authorization Server (é público) — a URI de redirecionamento é a única garantia de que o token vai parar no lugar certo.

Definição: Mitigação — validação completa, não parcial, da URI

A defesa central é exigir o registro da URI de redirecionamento e fazer um match exato dela contra o valor recebido na requisição — validar só o domínio (ou um prefixo) não é suficiente: um atacante com qualquer forma de injetar conteúdo dentro do próprio domínio legítimo (uma página de blog editável, por exemplo) poderia se passar por uma URI "parecida o bastante" para passar numa validação parcial.

Sequestro de identidade via authorization_code (CSRF)

Definição: Ataque de fixação de sessão via authorization_code

Diferente do roubo de token, este ataque troca a identidade por trás de uma ação: o atacante inicia seu próprio fluxo de autorização (com sua própria conta), intercepta o authorization_code gerado para ele, e induz a vítima — via um link de CSRF (Cross-Site Request Forgery) — a completar o callback do Client usando esse código. O Client, sem verificar se o código pertence à sessão atual, associa as ações da vítima à conta do atacante — por exemplo, fazendo com que tudo que a vítima cadastre passe a contar como se fosse do atacante.

Definição: Mitigação — o parâmetro state

O Client gera um valor aleatório (state) antes de redirecionar o usuário para a etapa de autorização, guarda esse valor associado à sessão atual, e o envia junto na URL de autorização. O Authorization Server devolve o mesmo state na URL de callback — o Client então só aceita o authorization_code se o state recebido bater com o que foi guardado na sessão. Isso garante que o código sendo processado pertence à mesma sessão que iniciou o fluxo, fechando a janela para o ataque de CSRF descrito acima. Usar state corretamente deveria ser considerado obrigatório em qualquer implementação de Authorization Server, mesmo que a especificação o trate como opcional.

Outros cuidados para o Client

Definição: Nunca exponha tokens em canais logáveis

Assim como uma senha, um token de acesso nunca deve trafegar por um canal que possa ficar gravado em log — enviar o token como parâmetro de URI (em vez do header Authorization) é arriscado justamente por isso: URLs completas costumam parar em access_logs de servidor e em proxies intermediários, tornando o token recuperável por qualquer um com acesso a esses registros (ver também as três formas de enviar um Bearer Token, acima). O mesmo vale para o client_secret — precisa ficar fora do código-fonte de aplicações nativas, cujo binário pode ser descompilado; a RFC 7591 define um padrão de registro dinâmico de Client, evitando embutir esse segredo diretamente no app distribuído.

Assinatura digital, certificados e PKI

A certificação digital garante quatro propriedades na troca de mensagens entre partes que não se conhecem:

Propriedade Significado
Autenticidade A mensagem veio mesmo de quem diz ter enviado
Integridade O conteúdo não foi alterado no caminho
Privacidade (confidencialidade) Só o destinatário consegue lê-la
Não repúdio O remetente não pode negar que enviou

Ela combina três ferramentas: criptografia (veja simétrica x assimétrica), funções de hash e certificados.

Assinatura digital

Definição: assinatura digital

O remetente calcula o hash da mensagem e o criptografa com a sua chave privada; o resultado, anexado à mensagem, é a assinatura. Quem recebe recalcula o hash da mensagem e decifra a assinatura com a chave pública do remetente: se os dois valores coincidem, a mensagem é íntegra e a autoria é autêntica (só quem tem a chave privada poderia ter assinado — o que também sustenta o não repúdio). A assinatura não cifra a mensagem; para sigilo, cifra-se à parte (com a chave pública do destinatário).

sequenceDiagram
    participant R as Remetente
    participant D as Destinatário
    R->>R: hash(mensagem)
    R->>R: assinatura = cifrar(hash, chave privada)
    R->>D: mensagem + assinatura (+ certificado)
    D->>D: hash(mensagem)
    D->>D: decifrar(assinatura, chave pública)
    D->>D: hashes iguais? → íntegra e autêntica

Certificado digital e autoridades certificadoras

Para saber que uma chave pública pertence mesmo a determinada pessoa ou site, usa-se o certificado digital (padrão X.509): um documento que associa uma identidade (nome, domínio) a uma chave pública, com período de validade, e que é assinado por uma autoridade certificadora (AC) confiável.

  • Cadeia de certificação (PKI/ICP): uma AC pode ser assinada por outra, até chegar a uma AC raiz, que é autoassinada e confiada previamente (instalada nos navegadores e sistemas). No Brasil, a infraestrutura oficial é a ICP-Brasil, cuja raiz é o ITI; certificados dela têm validade jurídica.
  • Validação de um certificado: (1) data de validade (em geral de 1 a 3 anos; hoje, para sites, bem menos); (2) não revogação, consultando a CRL (lista de certificados revogados, atualizada periodicamente) ou o OCSP (consulta em tempo real do status); (3) cadeia confiável até uma raiz aceita. Falhar em algum critério só deve ser aceito "por conta e risco".
  • Usos: HTTPS/TLS, assinatura de documentos e contratos, assinatura de código (code signing), e-mail seguro (S/MIME), autenticação de clientes. No handshake TLS, o servidor apresenta o seu certificado e as partes derivam chaves de sessão simétricas (veja Criptografia em trânsito).
  • Certificado autoassinado: emitido pela própria entidade, sem AC; o navegador alerta. Serve só para desenvolvimento e testes.

No Java: JCA

A JCA (Java Cryptography Architecture) é o conjunto de APIs de criptografia da plataforma (hash, assinatura, cifras, geração e gerenciamento de chaves, certificados), projetada com independência de algoritmo e de implementação:

  • Classes engine (MessageDigest, Signature, Cipher, KeyPairGenerator, KeyStore...) representam o serviço desejado, sem expor o algoritmo concreto: MessageDigest.getInstance("SHA-256").
  • Providers (CSP) fornecem as implementações; a JCA escolhe o primeiro por ordem de preferência ou o indicado no código. Bibliotecas externas, como a Bouncy Castle, adicionam algoritmos e suporte a X.509 e ASN.1.
  • KeyStore: repositório (arquivo) de chaves e certificados, manipulado pela ferramenta keytool.

Algoritmos obsoletos

Os exemplos de livros antigos usam MD5 e SHA-1, hoje considerados quebrados para assinatura e integridade. Prefira SHA-256 ou superior (e Argon2/Bcrypt para senhas).

CAPTCHA e proteção contra automação

Muitas aplicações precisam distinguir pessoas de programas que disparam milhares de requisições (cadastros falsos, força bruta de senhas, scraping, spam). O CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart, criado em 2000) é um teste de Turing inverso: um desafio fácil para humanos e difícil para máquinas (ler letras distorcidas, identificar objetos em imagens, ouvir um áudio).

Funcionamento básico: o servidor gera um desafio e guarda a resposta esperada na sessão; a pessoa responde e o servidor compara. Em Java existiram bibliotecas como JCaptcha, SimpleCaptcha e Kaptcha; hoje usam-se serviços como reCAPTCHA, hCaptcha e Cloudflare Turnstile, que avaliam o comportamento e reduzem o atrito.

Limitações e cuidados:

  • Nenhum CAPTCHA elimina 100% da automação: há OCR e modelos de IA que resolvem imagens, e serviços pagos de resolução humana.
  • Invalide a resposta depois de validada (remova-a da sessão); caso contrário, reutilizar o mesmo identificador de sessão e a resposta já aceita permite automatizar várias requisições.
  • Use o CAPTCHA junto com outras defesas: limitação de taxa, bloqueio progressivo por tentativas, MFA, análise de risco (veja rate limiting).
  • Pense em acessibilidade (desafios em áudio) e na experiência do usuário.

Segurança em aplicações web

As vulnerabilidades mais comuns em aplicações web têm uma raiz em comum: confiar em dados que vêm do usuário. A regra de ouro é não confiar nos usuários: valide tudo o que entra, trate tudo o que sai e conceda o mínimo de privilégio. Esta seção reúne as falhas clássicas (a maioria aparece no ranking OWASP Top 10, ver adiante), como elas funcionam em linhas gerais e, principalmente, como se proteger. Visão de infraestrutura e arquitetura em System Design.

Definição: OWASP

Open Worldwide Application Security Project: comunidade aberta (desde 2001) que produz guias, ferramentas e o ranking OWASP Top 10 das falhas de segurança mais críticas em aplicações web. É a referência padrão para desenvolvedores e para quem faz testes de segurança.

SQL Injection

O que é: a aplicação monta um comando SQL concatenando texto digitado pelo usuário. Se esse texto contém SQL, ele passa a fazer parte do comando: dá para contornar um login, ler ou apagar dados. Um login clássico WHERE login = ' + entrada + ' AND senha = '...' vira sempre verdadeiro se a entrada alterar a lógica da condição.

Como se proteger:

  • Consultas parametrizadas (prepared statements): o SQL tem marcadores (?/:nome) e os valores são enviados separados — o banco nunca os interpreta como comando. É a defesa principal.
  • Validar e normalizar entradas (tipo, tamanho, formato).
  • Usar ORM não basta: frameworks protegem se usados com parâmetros, mas deixam concatenar strings (JPQL/HQL ou SQL nativo montado à mão ficam vulneráveis).
  • Menor privilégio no banco (veja "Outras falhas" abaixo): mesmo que ocorra a injeção, o dano é limitado.
# Vulnerável: concatenação
cur.execute("SELECT * FROM usuarios WHERE login = '" + login + "' AND senha = '" + senha + "'")

# Seguro: parâmetros (a senha, na prática, é comparada por hash)
cur.execute("SELECT * FROM usuarios WHERE login = %s", (login,))

Mais sobre SQL e injeção em SQL.

Cross-Site Scripting (XSS)

O que é: a aplicação exibe conteúdo fornecido por um usuário sem tratá-lo, e o navegador de outra pessoa executa como JavaScript. O script roda com os privilégios da página legítima: pode ler cookies, alterar a tela, simular formulários de login e enviar dados a um servidor do atacante. O caso mais famoso foi o worm do Myspace (2005), que se espalhou para mais de um milhão de perfis em menos de um dia.

Tipo Como chega à vítima
Armazenado (stored) Fica salvo no banco (comentário, perfil) e roda para quem abrir a página
Refletido (reflected) Vem na URL ou na busca e é ecoado na resposta; a vítima precisa clicar num link manipulado

Como se proteger:

  • Valide a entrada e faça o escape da saída (codifique <, >, ", & conforme o contexto: HTML, atributo, JS, URL). A saída é o ponto decisivo — frameworks modernos (JSF, React, Angular, templates com autoescape) já escapam por padrão; o risco volta quando se desliga o escape (innerHTML, [innerHTML], dangerouslySetInnerHTML, ${var} direto no JSP).
  • Se for preciso aceitar HTML do usuário, sanitize com biblioteca especializada (OWASP AntiSamy, DOMPurify), com lista de permissões. Filtros que procuram palavras como script falham, porque há muitas formas de escrever JavaScript (atributos de evento, codificações).
  • Adicionar uma Content Security Policy (próxima subseção) como segunda barreira.
  • Marcar cookies de sessão como HttpOnly.

Content Security Policy (CSP)

CSP é um cabeçalho HTTP (Content-Security-Policy) que diz ao navegador de onde a página pode carregar scripts, estilos, imagens, fontes e outros recursos (lista de permissões). Mesmo que um atacante consiga injetar um script, o navegador o bloqueia se a origem não está permitida.

Diretiva Controla
default-src Regra padrão para o que não tem diretiva própria ('none' bloqueia tudo)
script-src, style-src Origens de JavaScript e CSS ('self' = a própria origem; 'unsafe-inline' libera código inline, enfraquece a proteção)
img-src, font-src, media-src Imagens, fontes, áudio/vídeo
connect-src Destinos de requisições AJAX/WebSocket
form-action, base-uri, object-src Destino de formulários, URL base e plugins

Uma política restritiva típica: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' cdn.exemplo.com (depois liberar o estritamente necessário). Boa prática: começar com Content-Security-Policy-Report-Only para observar violações sem bloquear, e ajustar. Hoje, prefira nonces ou hashes a 'unsafe-inline'.

Subresource Integrity (SRI) e recursos de CDN

Aplicações costumam carregar bibliotecas (CSS, JavaScript) de uma CDN (rede de distribuição de conteúdo, que serve arquivos estáticos perto do usuário). Se a CDN for comprometida, o arquivo adulterado roda na sua página. O SRI (Subresource Integrity) protege isso: você publica o hash (SHA-256, SHA-384 ou SHA-512) do arquivo esperado e o navegador só executa se o conteúdo bater:

<script src="https://cdn.exemplo.com/lib.min.js"
        integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
        crossorigin="anonymous"></script>

Combine com a CSP (liberando só a origem da CDN) e fixe a versão da biblioteca. Complementos de cabeçalhos: X-Content-Type-Options: nosniff (impede o navegador de "adivinhar" o tipo do arquivo), Referrer-Policy (limita o que vai no cabeçalho Referer), Strict-Transport-Security (força HTTPS).

Páginas de erro customizadas

Erros acontecem (entrada inválida, página inexistente, falha interna). Mostrar o erro padrão do servidor, com stack trace, versão e caminhos de arquivo, entrega informação ao atacante. Configure páginas de erro genéricas (400, 403, 404, 500) que dizem só o necessário ao usuário, e registre os detalhes apenas nos logs do servidor (Gestão de erros).

Cross-Site Request Forgery (CSRF)

O que é: o atacante faz o navegador de um usuário já autenticado enviar uma requisição ao sistema (cancelar um processo, fazer um pagamento) — o navegador anexa o cookie de sessão automaticamente, então o servidor acha que a ação é legítima. Basta o usuário abrir uma página ou e-mail malicioso. O atacante precisa conhecer a URL, o método e os parâmetros da operação.

Como se proteger:

  • Token anti-CSRF: um valor aleatório, imprevisível e ligado à sessão, incluído em cada formulário (campo oculto ou cabeçalho) e validado no servidor. O atacante não consegue adivinhá-lo. Frameworks já trazem isso (Spring Security, JSF ≥ 2.2, Laravel csrf_field, Django).
  • Cookies SameSite (Lax/Strict): o navegador não envia o cookie em requisições originadas em outros sites.
  • Exigir reautenticação em ações críticas; não alterar estado com GET.
  • Configurar CORS corretamente (CORS). APIs que autenticam por token no cabeçalho Authorization (não por cookie) não são suscetíveis a CSRF.

Mass Assignment (atribuição em massa)

O que é: muitos frameworks preenchem automaticamente os atributos de um objeto com os parâmetros da requisição. Se o objeto tem campos que o usuário não deveria controlar (admin, saldo, preco, idDono), um atacante pode enviar esses parâmetros "a mais" e o framework os grava.

Como se proteger: liste explicitamente os campos permitidos (allowlist): setAllowedFields no @InitBinder do Spring MVC, $fillable/$guarded no Laravel Eloquent, permit no Rails — ou, melhor, receba um DTO só com os campos editáveis (veja DTOs) e copie manualmente para a entidade. Nunca vincule diretamente a entidade de domínio à entrada.

Session Hijacking (sequestro de sessão)

Contexto: HTTP é stateless; para saber quem é o usuário logado, o servidor cria uma sessão em memória e envia ao navegador um cookie com o identificador da sessão. Quem possuir esse identificador "é" o usuário. (Guardar dados sensíveis direto no cookie é pior: ele fica no navegador e pode ser adulterado.) Os servidores geram identificadores longos, aleatórios e imprevisíveis, para que ninguém consiga adivinhar o de outra pessoa.

Como o cookie é roubado: por XSS (um script lê document.cookie e o envia ao atacante) e por interceptação de rede (sniffing) em redes públicas quando o site não usa HTTPS.

Como se proteger:

  • HTTPS em todo o site (certificado de uma autoridade certificadora, como a Let's Encrypt, que é gratuita) e HSTS para impedir o retorno ao HTTP. Certificados autoassinados só servem para teste.
  • Cookie de sessão com os atributos Secure (só via HTTPS), HttpOnly (JavaScript não acessa) e SameSite.
  • Prevenir XSS (todas as medidas acima).
  • Regenerar o identificador da sessão no login (evita session fixation), expirar sessões por inatividade e invalidá-las no logout.

Exposição de dados sensíveis

Cartões, documentos e outros dados pessoais precisam ser protegidos em repouso e em trânsito:

  • Criptografar os campos sensíveis antes de gravar (por exemplo, AES), com a chave fora do código e do banco (cofre de segredos, KMS). Quem invadir o banco só vê texto cifrado. Teoria em Criptografia simétrica x assimétrica.
  • HTTPS sempre; nunca colocar dados sensíveis na URL (ela vai para logs e histórico).
  • Evitar identificadores sequenciais em links públicos (/pedido/1001, /pedido/1002): permitem enumerar registros de outras pessoas. Use identificadores aleatórios (UUID) e sempre verifique a autorização do dono do recurso.
  • Limitar o que é exibido (máscara de cartão), não registrar dados sensíveis em logs e respeitar a LGPD.

Redirecionamentos não validados (open redirect)

Após o login, é comum voltar à página que o usuário tentou acessar, guardando a URL num parâmetro (login?redirectUrl=...). Se o destino não é validado, um atacante envia um link legítimo que, depois do login, leva a um site falso idêntico ao original, para roubar credenciais.

Como se proteger: evite redirecionar com base em parâmetro; se for necessário, valide contra uma lista de destinos permitidos (whitelist) ou aceite apenas caminhos relativos/URLs da própria aplicação.

Outras falhas comuns

Falha Risco Correção
Senhas em texto puro Um vazamento do banco expõe todas as contas (e as reutilizadas em outros sites) Armazenar só hash lento com sal: Bcrypt, Scrypt, PBKDF2 ou Argon2 (key stretching). MD5 e SHA-1 estão obsoletos; mesmo o SHA-2 puro é rápido demais para senhas. O sal (valor único por usuário) evita hashes repetidos e tabelas pré-calculadas
Aplicação usando o usuário root do banco Uma injeção de SQL passa a poder apagar tabelas, criar usuários, ler tudo Criar usuário específico com privilégios mínimos (sem CREATE, DROP, ALTER, TRUNCATE; só as tabelas necessárias)
Configurações padrão Contas e senhas default (banco, painel do servidor, console de administração) são as primeiras testadas Trocar/remover credenciais padrão, desativar o que não se usa; o atacante descobre tecnologias com facilidade, então não dependa de "esconder" a versão
Componentes vulneráveis Bibliotecas e frameworks de terceiros com falhas conhecidas (CVE) Manter inventário e atualizar; automatizar com Software Composition Analysis (OWASP Dependency-Check, Dependabot, Snyk) no pipeline

Definição: criptografia x hash

Criptografia é reversível com a chave e protege a confidencialidade. Hash gera um código de tamanho fixo, não reversível, e serve para integridade e para armazenar senhas (comparando-se o hash da entrada com o guardado).

Quem encontra as falhas: ethical hackers e bug bounty

Empresas mantêm equipes de segurança, contratam especialistas para testar (pentest) e mantêm programas de recompensas por falhas (bug bounty) em plataformas como HackerOne e Bugcrowd. Ethical hacker é o especialista que procura vulnerabilidades com autorização, para corrigi-las. Veja a seção de testes de invasão abaixo.

Testes de invasão (pentest)

Teste de invasão (penetration test, pentest) é uma avaliação autorizada e controlada em que um(a) especialista simula um atacante para descobrir vulnerabilidades antes que alguém mal-intencionado o faça. Apesar de usar técnicas ofensivas, é uma atividade de defesa: identifica o que corrigir, onde estão os maiores riscos, estima o impacto de uma exploração e ajuda a evitar vazamento de dados pessoais (e as sanções da LGPD).

Autorização e ética

Só se testa o que foi formalmente autorizado, dentro do escopo e das regras de engajamento combinados por escrito (alvos, horários, técnicas permitidas, tratamento dos dados encontrados). Testar sistemas de terceiros sem autorização é crime. Para aprender, use ambientes próprios e isolados (veja abaixo). Esta seção descreve conceitos, metodologia e as classes de falhas; o foco é entender e defender.

Metodologias e referências

Todas as metodologias seguem, em essência, três grandes etapas: reconhecimento, exploração e pós-exploração (e relatório). As mais usadas:

Referência O que é
OWASP Web Security Testing Guide (WSTG) Guia detalhado de testes para aplicações web (há guias para mobile e firmware)
PTES (Penetration Testing Execution Standard) Sete fases: interações de pré-engajamento, coleta de informações, modelagem de ameaças, análise de vulnerabilidades, exploração, pós-exploração e relatório
OSSTMM Manual de metodologia aberta, com métricas de segurança operacional; apoia a ISO 27001
NIST SP 800-115 Guia técnico de testes e avaliação de segurança (revisão, identificação de alvos, validação de vulnerabilidades)
PTF (Penetration Testing Framework) Roteiro prático com ferramentas por categoria de teste

Quanto ao conhecimento prévio do alvo, o teste pode ser de caixa preta (sem informação, como um atacante externo), caixa cinza (informação parcial, por exemplo uma conta de usuário) ou caixa branca (com código-fonte e arquitetura).

Red team, blue team e programas de recompensa

Papel O que faz
Red team Simula o adversário, com um objetivo (por exemplo, acessar determinado dado), escopo amplo e liberdade de técnica, sem avisar a defesa — testa pessoas, processos e detecção
Blue team A defesa: monitora, detecta, responde e fortalece controles
Purple team Red e blue colaborando para melhorar as detecções (complemento)
Pentest Escopo limitado e metódico, com o objetivo de encontrar o maior número de vulnerabilidades, e não de cumprir uma missão

Programas de bug bounty (HackerOne, Bugcrowd, programas próprios) pagam pesquisadores que relatam falhas de forma responsável. Costumam ser adotados por organizações com segurança já madura, complementando auditorias e pentests. Para quem está começando, o conselho é treinar antes em laboratórios (Hack The Box, TryHackMe, VulnHub) em vez de testar sistemas reais.

Classificação de gravidade: CVSS e OWASP Top 10

  • CVSS (Common Vulnerability Scoring System): nota de 0 a 10 para a gravidade de uma vulnerabilidade, com três grupos de métricas — base (características intrínsecas: vetor de ataque, complexidade, privilégios, interação, impacto em CIA — o valor mais citado), temporal (maturidade do exploit, existência de correção) e ambiental (importância do ativo na organização). Faixas: baixa (0,1–3,9), média (4–6,9), alta (7–8,9), crítica (9–10).
  • OWASP Top 10: ranking das categorias de risco mais críticas. A edição de 2017, que o livro cita, lista: injeção, quebra de autenticação, exposição de dados sensíveis, XXE, quebra de controle de acesso, configuração incorreta de segurança, XSS, desserialização insegura, uso de componentes vulneráveis e registro e monitoramento insuficientes. (A edição de 2021 reorganizou a lista: quebra de controle de acesso passou ao 1º lugar, e entraram falhas criptográficas, design inseguro, falhas de integridade de software e de dados e SSRF; a injeção, que inclui o XSS, caiu para o 3º. Consulte a versão vigente no site da OWASP.)

Ferramentas e o ciclo achar, corrigir e retestar

Um pentest só vale se termina em correção. Ferramentas comuns (em ambiente autorizado, como um laboratório com uma aplicação-alvo em contêiner):

Ferramenta Para quê
DevTools do navegador (abas Elements, Network, Sources) Ver o código-fonte, formulários, campos ocultos/desabilitados, parâmetros enviados e arquivos estáticos
Burp Suite Proxy de interceptação: captura e altera requisições, faz spidering (mapeia URLs e formulários) e scanner
curl / Postman Repetir requisições à mão, sem o navegador (testar se a proteção está só no cliente)
Wireshark Captura de tráfego de rede: mostra por que HTTPS é indispensável (credenciais e cookies em texto puro em HTTP)
nmap Descoberta de hosts, portas e serviços na etapa de scanning

Do achado ao conserto (por vulnerabilidade):

Vulnerabilidade Onde procurar no código Correção
Autenticação quebrada Controle de login duplicado ou ausente em algumas funcionalidades Centralizar em filtro/interceptor executado antes de toda requisição protegida
SQL Injection Consultas montadas com concatenação ("... WHERE email = '" + email + "'") PreparedStatement/consultas parametrizadas/JPA; revisar todos os DAOs
Controle de acesso (autorização) Ação que só carrega o objeto e altera (fechar tópico, marcar solução) sem checar o dono Verificar autoria ou perfil (moderador) no servidor, por objeto
XSS Saída com ${var} sem escape Escape de saída (<c:out>, th:text) e sanitização (AntiSamy) quando se aceita HTML
Mass assignment Método que recebe a entidade inteira (inclui perfil, saldo) a partir do formulário Receber um DTO/formulário só com os campos editáveis
Validação só no cliente Regras apenas em JavaScript/HTML Revalidar no servidor sempre
CSRF Ações que mudam estado sem token Token anti-CSRF + cookies SameSite
Senhas Texto puro, MD5/SHA-1 BCrypt (ou Argon2) com salt, via Spring Security

Depois de corrigir, repita o ataque (navegador e curl) para confirmar que a falha fechou, e procure a mesma falha em outros pontos do código.

Fases de um pentest em aplicação web

  1. Preparação do ambiente — um laboratório isolado (máquinas virtuais em rede interna, sem rota para a internet), com uma máquina atacante (Kali Linux) e aplicações propositalmente vulneráveis (OWASP BWA, Mutillidae, DVWA, Juice Shop). Use snapshots para voltar ao estado limpo, pois o teste pode comprometer as próprias máquinas.
  2. Reconhecimento — passivo (fontes abertas, sem tocar no alvo) e ativo: mapear a aplicação e seus pontos de entrada, descobrir diretórios e arquivos escondidos, identificar tecnologias e versões (cabeçalhos, impressões digitais) e varrer portas/serviços (nmap e scripts). Defesa: minimizar informações expostas, remover arquivos de backup e de teste do servidor, ocultar versões e monitorar varreduras.
  3. Análise e exploração das classes de falha abaixo.
  4. Pós-exploração — avaliar o alcance do acesso obtido (dados, pivotamento) para dimensionar o impacto. Ferramentas como o Metasploit (framework com módulos de exploração, payloads e o Meterpreter) automatizam essa etapa; varreduras automáticas são superficiais, geram falsos positivos e precisam de validação manual.
  5. Relatório — a entrega que dá valor ao teste.

Classes de falha exploradas e a defesa correspondente

O livro percorre as seguintes classes; abaixo, o que são e como mitigar (complemento ao texto-fonte, que não trata de correção):

Classe Em resumo Mitigação
SQL Injection (extração de dados, leitura de arquivos, execução de comandos) Entrada vira SQL; ferramentas como o sqlmap automatizam a descoberta Consultas parametrizadas, privilégio mínimo no banco, WAF
Path traversal, LFI/RFI (inclusão de arquivos) A aplicação lê/inclui arquivo cujo caminho vem do usuário (../), expondo arquivos locais ou executando código remoto Não montar caminhos com entrada do usuário; allowlist de arquivos; canonicalizar e restringir o diretório; desativar inclusão remota
XSS (inclusive em parâmetros POST e com frameworks de exploração de navegador) Script executado no navegador da vítima Escape de saída, sanitização, CSP, HttpOnly
CSRF e SSRF CSRF: requisição forjada pelo navegador da vítima. SSRF: o servidor é induzido a requisitar recursos internos ou da nuvem em nome do atacante Token anti-CSRF e SameSite; para SSRF, allowlist de destinos, bloquear redes internas e metadados de nuvem, segmentação de rede
Falhas de autenticação, sessão e autorização Força bruta e dicionário, controle de acesso por parâmetro manipulado (IDOR), cookies previsíveis, recursos sem autenticação, ações sem autorização em APIs REST, recuperação de senha fraca Bloqueio por tentativas, MFA, sessões aleatórias e com atributos seguros, autorização verificada no servidor em toda requisição, recuperação de senha com token único e de curta duração
Injeção de comandos do SO Entrada chega a um exec/system Evitar chamadas ao shell; usar APIs e listas de argumentos; validar
XXE (XML External Entity) Processador de XML resolve entidades externas e expõe arquivos internos Desabilitar DTDs e entidades externas no parser
Desserialização insegura Objetos serializados controlados pelo usuário levam à execução remota Não desserializar dados não confiáveis; formatos simples (JSON), assinatura/integridade, listas de classes permitidas
Injeção/poluição de parâmetros HTTP, XPath injection Parâmetros duplicados ou entradas que alteram consultas XPath Validar e normalizar parâmetros; consultas parametrizadas
Clickjacking A página é embutida num iframe invisível para induzir o clique Cabeçalho X-Frame-Options / frame-ancestors da CSP
Burla de controles client-side Validações feitas só no navegador Revalidar tudo no servidor
Redirecionamento de URL (open redirect), clone de páginas Phishing a partir de links legítimos Allowlist de destinos, treinamento contra phishing, MFA resistente a phishing
Captura de informação sensível, componentes vulneráveis Dados em logs, comentários, backups, mensagens de erro detalhadas; bibliotecas com CVE Mensagens de erro genéricas, remover dados de depuração, atualizar dependências
Negação de serviço na camada HTTP Esgotar recursos com requisições lentas ou em massa Timeouts, limitação de taxa, WAF/anti-DDoS, balanceador

Relatório final

O relatório é o produto do pentest. Deve conter: resumo executivo (para a gestão, em linguagem de negócio, com impacto e prioridades), escopo e acordos (alvos, limitações), coleta de informação, vulnerabilidades (descrição, impacto, nota CVSS, evidências que permitem reproduzir e recomendação de correção) e considerações finais, com uma síntese e a classificação geral de risco. Verifique manualmente cada achado de ferramenta automatizada para evitar falsos positivos.

Um roteiro de verificações (baseado no OWASP Testing Guide v4.2) agrupa os testes em: reconhecimento; gestão de identidade; autenticação; autorização; gerenciamento de sessão (atributos de cookie, CSRF, logout, expiração); validação de entrada (XSS, SQL e outras injeções); tratamento de erros e criptografia fraca; lógica de negócio; e testes client-side.