MFA no back-end: visão geral e integração segura

Capítulo 11

Tempo estimado de leitura: 10 minutos

+ Exercício

O que é MFA e onde entra no fluxo de login

MFA (Multi-Factor Authentication) adiciona uma etapa extra de verificação além do fator primário (geralmente senha). No back-end, isso significa que o login passa a ter pelo menos dois estágios: (1) validar credenciais primárias e (2) validar um desafio de segundo fator. O ponto central é modelar esse “meio-login” com segurança, sem emitir uma sessão/token final antes do MFA ser concluído.

Um fluxo típico fica assim:

  • Etapa 1 (primary): usuário envia identificador + senha.
  • Decisão: se o usuário tem MFA habilitado e o dispositivo não é confiável, o back-end cria um desafio pendente e responde que MFA é necessário.
  • Etapa 2 (MFA): usuário prova posse do segundo fator (TOTP, WebAuthn/passkey, SMS em último caso, ou código de recuperação).
  • Finalização: somente após sucesso no MFA o back-end emite o estado autenticado final (sessão/cookie ou token).

Fatores e métodos principais (e quando usar)

TOTP (Time-based One-Time Password)

TOTP é o código de 6–8 dígitos gerado por um app autenticador a partir de um segredo compartilhado e do tempo. É amplamente suportado e funciona offline.

  • Prós: simples, barato, offline.
  • Contras: vulnerável a phishing e “real-time relay” (o atacante captura e usa o código imediatamente).
  • Onde entra: após a senha, o usuário informa o código TOTP para concluir o login.

WebAuthn / Passkeys

WebAuthn (passkeys) usa criptografia assimétrica e vincula a autenticação ao domínio (origin), reduzindo drasticamente phishing. Em vez de “código”, o usuário aprova com biometria/PIN no dispositivo, e o navegador/app envia uma asserção assinada.

  • Prós: forte contra phishing, sem segredos compartilhados no servidor, melhor UX.
  • Contras: integração mais complexa; requer suporte do cliente (browser/OS).
  • Onde entra: pode ser usado como segundo fator após senha, ou como fator primário (passwordless). Neste capítulo, foque como segundo fator.

SMS (último recurso)

SMS entrega um código por mensagem. Deve ser tratado como opção de contingência, não como padrão, por riscos como SIM swap, interceptação e engenharia social.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Prós: acessível para usuários sem app autenticador.
  • Contras: segurança inferior; dependência de operadora; suscetível a sequestro de número.
  • Onde entra: segundo fator quando não há TOTP/WebAuthn disponível.

Códigos de recuperação

Códigos de recuperação são “senhas de uso único” geradas quando o usuário habilita MFA. Servem para recuperar acesso quando o segundo fator não está disponível.

  • Boas práticas: gerar 8–12 códigos, exibir uma única vez, armazenar apenas hash no servidor, invalidar após uso.

Modelagem de estados no back-end

Para MFA ser robusto, o back-end precisa representar explicitamente estados intermediários e artefatos relacionados. Abaixo está um modelo prático (independente de framework) para orientar tabelas/coleções e lógica.

Estado do usuário: MFA habilitado

Campos típicos no registro do usuário:

  • mfa_enabled (boolean)
  • mfa_methods (lista: webauthn, totp, sms)
  • mfa_preferred_method (opcional)
  • mfa_enrolled_at, mfa_last_verified_at (auditoria e políticas)

Desafio pendente (MFA challenge)

Após validar a senha, não emita autenticação final. Em vez disso, crie um registro de desafio pendente com TTL curto (ex.: 5 minutos). Esse desafio é o “ticket” que permite tentar MFA.

Campos recomendados:

  • challenge_id (UUID aleatório)
  • user_id
  • status (pending, verified, expired, locked)
  • created_at, expires_at
  • attempt_count, last_attempt_at
  • ip, user_agent (para detecção e auditoria)
  • allowed_methods (ex.: ["webauthn","totp","recovery"])
  • context (ex.: login, step_up)

O cliente recebe challenge_id e sabe que precisa concluir MFA. Esse challenge_id não deve ser previsível e deve ser tratado como segredo temporário.

Dispositivos confiáveis (trusted devices)

“Confiar neste dispositivo por X dias” reduz fricção sem remover MFA globalmente. Modele isso como um token de confiança por dispositivo, com revogação e expiração.

Estratégia comum:

  • Após MFA bem-sucedido, se o usuário optar por confiar, o servidor cria um trusted_device e emite um cookie/identificador persistente.
  • Em logins futuros, se o cookie for válido e não revogado, o servidor pode pular o MFA (ou exigir “step-up” apenas para ações sensíveis).

Campos recomendados para trusted_devices:

  • device_id (UUID)
  • user_id
  • device_fingerprint_hint (opcional; não confie apenas nisso)
  • token_hash (hash do token persistente)
  • created_at, expires_at, revoked_at
  • last_used_at, ip_last

Boas práticas: o token do dispositivo deve ser aleatório, armazenado como hash no servidor, e rotacionado periodicamente (por exemplo, a cada uso ou a cada N dias).

Segredos TOTP

Para TOTP, o servidor precisa armazenar o segredo compartilhado. Isso exige proteção extra:

  • Criptografia em repouso: armazene o segredo cifrado (ex.: AES-GCM) com uma chave gerenciada fora do banco (KMS/HSM quando possível).
  • Separação de chaves: não deixe a chave de criptografia no mesmo lugar que o banco.
  • Controle de acesso: limite quem/qual serviço pode descriptografar.
  • Rotação: planeje rotação de chaves de criptografia (envelope encryption ajuda).

Campos sugeridos:

  • totp_secret_ciphertext
  • totp_secret_kid (identificador da chave usada)
  • totp_confirmed (somente após o usuário validar um código durante o cadastro)

Credenciais WebAuthn

Para WebAuthn, você armazena a credencial pública e metadados, não um segredo reutilizável:

  • credential_id (bytes/base64url)
  • public_key
  • sign_count (para detecção de clonagem em alguns cenários)
  • transports (opcional)
  • created_at, last_used_at

Códigos de recuperação

Armazene apenas hash (como senhas), com marcação de uso:

  • code_hash
  • used_at (nulo até ser usado)
  • created_at

Riscos e boas práticas específicas de MFA

Rate limit e bloqueio progressivo em desafios

Mesmo com MFA, um atacante pode tentar adivinhar TOTP/SMS ou abusar do endpoint de desafio. Aplique controles:

  • Rate limit por challenge_id, user_id, IP e faixa de IP.
  • Limite de tentativas: ex.: 5 tentativas por desafio; após isso, marque como locked e exija novo login.
  • Backoff: aumente o tempo entre tentativas (ex.: 1s, 2s, 4s...).
  • TTL curto: desafios expiram rápido (ex.: 5 min).

Proteção contra phishing: preferir WebAuthn

TOTP e SMS podem ser capturados por páginas falsas e usados em tempo real. WebAuthn reduz esse risco porque a asserção é vinculada ao origin (domínio) e não pode ser “reaproveitada” em outro site. Boas práticas:

  • Ofereça WebAuthn como método preferencial quando disponível.
  • Step-up para ações sensíveis: mesmo em sessão ativa, peça WebAuthn para trocar e-mail, sacar valores, alterar MFA etc.
  • Evite “fallback” automático para SMS; exija confirmação explícita e monitore.

Armazenamento seguro de segredos TOTP

Além de criptografia em repouso, considere:

  • Minimização: só descriptografar o segredo no momento de validar o código.
  • Auditoria: registre eventos de leitura/uso (sem logar o segredo).
  • Proteção de backup: backups do banco devem estar cifrados e com acesso restrito.

Auditoria de eventos (observabilidade de segurança)

Registre eventos para investigação e detecção:

  • mfa.enroll.started, mfa.enroll.completed
  • mfa.challenge.created, mfa.challenge.succeeded, mfa.challenge.failed, mfa.challenge.locked
  • mfa.method.added, mfa.method.removed
  • mfa.trusted_device.added, mfa.trusted_device.revoked
  • mfa.recovery_code.used, mfa.recovery_codes.regenerated

Inclua user_id, IP, user-agent, timestamps e um request_id para correlação. Evite registrar códigos, segredos, ou payloads sensíveis.

Passo a passo prático: habilitar MFA (TOTP e WebAuthn)

Cadastro TOTP (enrollment)

Objetivo: o usuário escaneia um QR code e confirma com um código válido antes de marcar TOTP como ativo.

  1. Iniciar: servidor gera um segredo TOTP aleatório e cria um registro de enrollment pendente.

  2. Exibir QR: servidor retorna um otpauth:// (ou QR) para o app autenticador.

  3. Confirmar: usuário envia um código TOTP; servidor valida e então marca totp_confirmed=true e mfa_enabled=true.

// Exemplo de payloads (simplificado) POST /mfa/totp/enroll/start Response { "otpauth_uri": "otpauth://totp/MinhaApp:email?secret=...&issuer=MinhaApp", "enroll_id": "..." } POST /mfa/totp/enroll/confirm { "enroll_id": "...", "code": "123456" }

Boas práticas: exija reautenticação recente (ou step-up) para iniciar enrollment; gere e entregue códigos de recuperação ao final.

Cadastro WebAuthn (passkey) (enrollment)

Objetivo: registrar uma credencial WebAuthn para o usuário.

  1. Iniciar: servidor cria um challenge WebAuthn (bytes aleatórios) e retorna opções de criação (RP ID, user handle, etc.).

  2. Criar credencial no cliente: o navegador/app chama a API WebAuthn e retorna a attestation.

  3. Finalizar: servidor valida a attestation, extrai credential_id e public_key, e salva.

// Roteiro de endpoints (alto nível) POST /mfa/webauthn/register/options Response { publicKey: { challenge: "...", rp: {...}, user: {...}, pubKeyCredParams: [...] } } POST /mfa/webauthn/register/verify { "attestation": { ... } }

Boas práticas: valide origin/RP ID corretamente; use bibliotecas maduras para WebAuthn; registre múltiplas credenciais (ex.: celular e notebook) para reduzir lockout.

Roteiro de endpoints e estados para um fluxo MFA simples e robusto

A seguir, um roteiro de endpoints que separa claramente: autenticação primária, desafio MFA, verificação MFA e emissão do estado autenticado final. A ideia funciona tanto para sessões quanto para tokens, desde que você só “finalize” após MFA.

1) Login primário

EndpointDescriçãoResposta
POST /auth/loginValida credenciais primáriasSe não precisa MFA: emite autenticação final. Se precisa MFA: retorna mfa_required + challenge_id.
// Response quando MFA é necessário { "mfa_required": true, "challenge_id": "c_123", "allowed_methods": ["webauthn","totp","recovery"], "expires_in": 300 }

Regras: se o usuário tem dispositivo confiável válido, pode retornar autenticação final diretamente; caso contrário, crie mfa_challenge pendente.

2) Iniciar desafio por método (quando aplicável)

Alguns métodos precisam de “options”/envio antes da verificação.

EndpointQuando usarObservações
POST /auth/mfa/webauthn/optionsPara WebAuthnGera challenge WebAuthn vinculado ao challenge_id.
POST /auth/mfa/sms/sendPara SMSEnvia código; aplicar rate limit forte e antifraude.
// Exemplo: POST /auth/mfa/webauthn/options { "challenge_id": "c_123" } // Response { publicKey: { challenge: "...", allowCredentials: [...] } }

3) Verificar MFA

EndpointDescriçãoEntrada
POST /auth/mfa/verifyValida o segundo fator e finaliza o loginInclui challenge_id + prova do método

Exemplos de payload por método:

// TOTP { "challenge_id": "c_123", "method": "totp", "code": "123456", "trust_device": true } // Recovery code { "challenge_id": "c_123", "method": "recovery", "code": "RC-ABCD-EFGH" } // WebAuthn { "challenge_id": "c_123", "method": "webauthn", "assertion": { ... } }

Regras de verificação:

  • Verifique se challenge_id existe, está pending, não expirou e pertence ao usuário correto.
  • Aplique rate limit e incremente attempt_count em falhas.
  • Em sucesso: marque desafio como verified, registre auditoria e emita autenticação final.
  • Se trust_device=true: crie trusted_device e emita cookie/token persistente do dispositivo (hash no servidor).

4) Gerenciar dispositivos confiáveis

EndpointDescrição
GET /mfa/trusted-devicesLista dispositivos confiáveis do usuário
DELETE /mfa/trusted-devices/:device_idRevoga um dispositivo

Boas práticas: exija step-up (MFA) para revogar/adicionar dispositivos confiáveis, pois isso afeta diretamente a postura de segurança.

5) Gerenciar métodos e códigos de recuperação

EndpointDescrição
POST /mfa/recovery-codes/regenerateGera novos códigos (revoga os antigos)
POST /mfa/methods/disableDesabilita MFA (exigir step-up forte)

Regras: regenerar códigos invalida todos os anteriores; desabilitar MFA deve exigir verificação recente e registrar auditoria.

Checklist de validações críticas no back-end

  • Nunca emita autenticação final antes do MFA ser concluído quando mfa_enabled e dispositivo não confiável.
  • Desafios com TTL curto, estado persistido e limite de tentativas.
  • Rate limit por usuário/IP/desafio, com bloqueio progressivo.
  • WebAuthn preferencial para reduzir phishing; evite SMS como padrão.
  • TOTP secret cifrado em repouso, com chaves fora do banco e auditoria de acesso.
  • Recovery codes armazenados como hash e de uso único.
  • Auditoria completa de eventos de MFA, sem dados sensíveis em logs.

Agora responda o exercício sobre o conteúdo:

Em um fluxo de login com MFA habilitado e sem dispositivo confiável, qual prática no back-end garante que o usuário só receba o estado autenticado final após concluir o segundo fator?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

O back-end deve modelar um “meio-login”: validar credenciais primárias, criar um desafio pendente com TTL e limites de tentativa e só então, após a verificação do MFA, emitir a sessão/cookie ou token final.

Próximo capitúlo

Autorização por papéis (RBAC) em APIs back-end

Arrow Right Icon
Capa do Ebook gratuito Autenticação e Autorização no Back-end: Sessões, JWT e Boas Práticas
61%

Autenticação e Autorização no Back-end: Sessões, JWT e Boas Práticas

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.