Introdução
O portal de membros da Harbor permite recuperar uma conta após esquecer a senha. O e-mail contém um token temporário: sua posse deveria autorizar uma única troca de senha para uma única conta. O suporte descobriu que os links podem ser reutilizados e alterar a senha de outra conta.
Primeiro você recuperará uma conta fictícia normalmente, observará o comportamento inseguro e corrigirá um pequeno manipulador Python. Não é necessária experiência em Python: funções e condições são explicadas antes da edição. Você deve saber digitar um comando e alternar abas.
Use Portal Web para o aplicativo, Mailbox Web para o correio simulado e App Server para o código preparado. Tudo funciona nesta instância de treinamento independente, sem envio externo de e-mail. Tokens, comparações de expiração, transações, alterações de senha e login são reais; este pequeno aplicativo não é um sistema de identidade de produção.
Recuperar uma conta normalmente
Nesta etapa, você seguirá um e-mail de redefinição e entrará com a nova senha, estabelecendo o funcionamento que a correção deve preservar.
Uma conta identifica uma pessoa; uma senha comprova que ela pode entrar. Se for esquecida, um link autoriza temporariamente sua alteração. O token do link não é a senha. A caixa local permite agir como proprietário das contas fictícias maya e noah, ambas inicialmente com StartPass42!. Use somente essas contas de treinamento.
Em Portal Web, encontre Request a reset link. Digite maya em Recovery account e clique em Send reset link. Aparecerá Link requested. Open Local Mailbox. Atualize Mailbox Web: a mensagem mais recente fica no topo e mostra destinatário, Open reset link e a sequência do token.
Clique em Open reset link nessa mensagem. O formulário abre na mesma aba com Token e Reset account preenchidos. Não os altere; digite FirstPass42! em New password e clique em Reset password. Espere Reset accepted e HTTP 200.
HTTP é a conversa de requisições e respostas entre navegador e servidor. Aqui, 200 significa aceito, 403 indica uma requisição negada e 500 um erro do aplicativo. Um erro de conexão é diferente de uma negação de segurança.
Abra Portal Web, atualize se necessário e use Sign in: Login account maya, Password FirstPass42!, botão Sign in. Espere Login accepted. Uma redefinição funcional muda a senha real, não apenas um texto na página.
Se perder o link, solicite outro no mesmo formulário e use a mensagem mais recente. Ele dura 30 minutos. Um erro de digitação ou link expirado não exige reiniciar o ambiente.
Observar a fronteira quebrada
Nesta etapa, você fará alterações de demonstração descartáveis para distinguir proprietário, validade e permissão de uso único do token.
O navegador envia campos com nomes e valores. O usuário pode alterá-los, portanto o servidor não pode confiar em Reset account só porque o e-mail o preencheu. O servidor mantém um registro separado:
| Campo | Significado |
|---|---|
account |
Conta autorizada a usar o token |
expires_at |
Último instante permitido, em segundos desde uma origem fixa de tempo |
used |
0 antes do uso; 1 depois |
Um token novo só serve para seu proprietário antes de expirar. Uma redefinição bem-sucedida o torna usado; ele nunca deve voltar a ser utilizável. Um token não usado também pode expirar com o tempo. Nenhum desses estados autoriza outra redefinição.
Solicite um novo link para maya em Portal Web. Atualize Mailbox Web, abra a mensagem mais recente e defina DemoPass42!. Reabra o link da mesma mensagem e defina ReplayDemo42!. Ambas mostram Reset accepted. A segunda deveria ser negada: é uma repetição, reutilizar uma permissão que já deveria estar esgotada.
Abra novamente esse link. Altere Reset account para noah e New password para CrossDemo42!; clique em Reset password. A requisição é aceita. Em Portal Web, entre como noah com CrossDemo42!. Um link emitido para maya alterou noah: o servidor não vinculou token e conta.
Por fim, no formulário de Portal Web, informe Token expired-demo, Reset account maya e New password ExpiredDemo42!; clique em Reset password. Esse token preparado expirou uma hora antes da preparação e também é aceito: o servidor ignorou o relógio. Não é necessário esperar um temporizador.
Essas contas são descartáveis, e os testes seguintes usam cópias limpas. Preserve o estado observado, sem editar diretamente o banco de dados.
Vincular e consumir o token
Nesta etapa, você corrigirá a pequena função do servidor e a testará sem alterar as contas de demonstração em uso.
Alterne para App Server, que começa em /home/labex/project/reset. cat mostra um arquivo de texto; o argumento é seu nome. Leia o manipulador:
cat handler.py
A primeira linha importa duas funções auxiliares preparadas. def define uma função; os nomes entre parênteses são parâmetros, as entradas de uma chamada. eligible(row, account, now) recebe o registro salvo, a conta enviada e o horário do servidor. row["account"] lê um campo. row is None significa que nenhum token foi encontrado. O bloco indentado de if só executa se a condição for verdadeira; return False nega e return True concede permissão. Quatro espaços iniciais definem aqui um bloco Python.
A função atual retorna True para qualquer token existente, explicando as três falhas. reset também chama unsafe_reset, que muda a senha sem consumir o token.
A correção exige todas as condições juntas. == compara valores, > significa maior que e and exige ambos os lados verdadeiros. or aceitaria qualquer lado, deixando uma propriedade válida ocultar outra inválida. Uma expiração futura é maior que now; igualdade já significa expirado. used == 0 só permite token não usado.
A função preparada consume_and_reset inicia uma transação SQLite, lê o token atual, chama sua eligible e confirma juntos a mudança de senha e used = 1. Isso é atomicidade: as mudanças têm sucesso juntas. Ela bloqueia antes de ler, impedindo duas requisições concorrentes de verem o token não usado ao mesmo tempo. Você não implementa bloqueios nem hash de senhas.
Abra o pequeno arquivo com nano, editor de texto do terminal:
nano handler.py
Substitua o conteúdo por este código. Use setas e Backspace/Delete para remover o texto antigo e cole a substituição completa. Preserve espaços e pontuação; os parênteses permitem dividir a condição em linhas.
from framework import unsafe_reset, consume_and_reset
def eligible(row, account, now):
if row is None:
return False
return (row["account"] == account
and row["expires_at"] > now
and row["used"] == 0)
def reset(db, token, account, password):
return consume_and_reset(db, token, account, password, eligible)
Salve com Ctrl+O, pressione Enter para confirmar handler.py e saia com Ctrl+X. Cada requisição Web lê o arquivo salvo; não é preciso reiniciar. Se aparecer HTTP 500, reabra e compare indentação, colchetes e aspas.
python3 executa um programa Python. O verificador recebe o caminho do manipulador e inicia um aplicativo HTTP separado com dados SQLite temporários. Ele testa o código real sem corrigi-lo ou mudar as contas em uso:
python3 /opt/reset-lab/check.py handler.py
Espere onze linhas PASS, incluindo valid reset accepted, replay denied e competing use has exactly one winner. FAIL nomeia a propriedade incorreta. Negar tudo falha nas verificações legítimas de recuperação e login. Alterar campos do navegador ou o banco em uso não corrige o código testado com dados novos. Reabra nano handler.py, corrija, salve e repita o mesmo verificador quando necessário.
Provar que a recuperação continua funcionando
Nesta etapa, você usará o serviço corrigido e relacionará seus resultados às verificações isoladas de segurança.
Em Portal Web, solicite um link novo para maya. Atualize Mailbox Web, abra a última mensagem e defina ResetSafe42!. Espere Reset accepted e HTTP 200. Reabra o link da mesma mensagem e tente ReplayAgain42! para maya: espere Reset denied e HTTP 403.
Solicite outro link novo de maya. Abra-o, altere Reset account para noah e tente BlockedCross42!. Deve ser negado. Isso não deve consumir o link não usado do proprietário: reabra a mesma mensagem, mantenha maya e defina ResetSafe42!. Deve funcionar.
No formulário, tente Token expired-demo, Reset account maya e New password BlockedExpired42!: espere negação. Em Portal Web, entre como maya com ResetSafe42!. Login accepted prova que tentativas falhas não mudaram a senha. Também é possível entrar como noah com a senha de demonstração CrossDemo42!; as outras contas são preservadas.
Em App Server, repita os testes de regressão descartáveis:
python3 /opt/reset-lab/check.py handler.py
Os onze devem passar, incluindo duas requisições simultâneas com uma única redefinição aceita. Repeti-los não altera registros ativos. Recuperação permitida e abuso negado devem coexistir; desativar a página ou negar sempre não é uma correção adequada.
O aplicativo simula a entrega e simplifica a identidade. Em produção, são necessários também e-mail seguro, limites de frequência, gestão de sessões e política de senhas, fora deste exercício. O guia da OWASP amplia esse contexto.
Resumo
Você seguiu uma redefinição real, demonstrou repetição, alteração entre contas e expiração, e corrigiu um pequeno manipulador Python. Proprietário salvo, expiração futura, estado não usado e consumo atômico atuam juntos. Testes HTTP descartáveis comprovam recuperação válida, abuso negado, outras contas intactas e um único vencedor concorrente. O desafio do portal de funcionários aplica essas regras a outra condição incompleta.



