Adicionar um Segundo Fator a um Script de Login

CibersegurançaBeginner
Pratique Agora

Introdução

A autenticação multifator reduz o risco de comprometimento de contas ao exigir mais de um tipo de prova. Uma senha é algo que o usuário sabe. Um segundo fator pode ser um código proveniente de um dispositivo, aplicativo, arquivo ou token de hardware separado.

Neste laboratório, você começará com um script de login em Python que utiliza apenas senha e, em seguida, adicionará um segundo fator simples. Para manter o laboratório acessível a iniciantes e determinístico, o segundo fator será um código baseado em HMAC gerado a partir de um arquivo secreto protegido.

Testar o Login Apenas com Senha

Nesta etapa, você executará o script de login inicial. Ele verifica apenas um nome de usuário e uma senha, o que constitui uma autenticação de fator único, pois a única prova é algo que o usuário sabe.

Entre no diretório do projeto:

cd /home/labex/project

Inspecione os arquivos preparados:

ls -l

Visualize o banco de dados de usuários:

cat users.json

Execute um login bem-sucedido apenas com senha:

python3 login.py alice wonderland

A saída deve ser:

login ok

Agora, tente uma senha incorreta:

python3 login.py alice wrong

Este comando deve imprimir login failed. Um login com falha é encerrado com um status diferente de zero, o que é normal para este teste.

Execute o verificador para o estado inicial:

python3 mfa-check.py password-only

O resultado esperado é:

password-only: complete

Criar o Segredo do Segundo Fator

Nesta etapa, você criará um segredo separado para o segundo fator. Em um sistema de MFA real, esse segredo poderia residir em um aplicativo autenticador ou em um token de hardware. Neste laboratório, ele é um arquivo local para que o comportamento seja fácil de inspecionar e verificar.

Crie um valor secreto:

printf 'labex-mfa-secret-2026\n' > mfa_secret.txt

Restrinja o arquivo para que apenas o proprietário possa ler e gravar nele:

chmod 600 mfa_secret.txt

Verifique as permissões:

ls -l mfa_secret.txt

Os bits de permissão devem começar com -rw-------:

-rw------- ... mfa_secret.txt

Execute o verificador para o segredo:

python3 mfa-check.py secret

O resultado esperado é:

secret: complete

Gerar um Código de Login

Nesta etapa, você criará um pequeno gerador de códigos. Ele usa HMAC-SHA256 para combinar o arquivo secreto com o nome de usuário. O HMAC é útil aqui porque a mesma entrada e o mesmo segredo produzem o mesmo código curto, mas alguém sem o segredo não consegue criar o código facilmente.

Crie o arquivo generate-code.py:

cat > generate-code.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import sys
from pathlib import Path

if len(sys.argv) != 2:
    print("usage: generate-code.py USERNAME")
    raise SystemExit(2)

username = sys.argv[1]
secret = Path("mfa_secret.txt").read_text().strip().encode()
digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
print(digest[:6])
PY

Torne-o executável:

chmod +x generate-code.py

Gere um código para alice:

python3 generate-code.py alice

Você deverá ver este código determinístico de seis caracteres:

21f18f

Execute o verificador para o gerador:

python3 mfa-check.py generator

O resultado esperado é:

generator: complete

Aplicar o Segundo Fator

Nesta etapa, você substituirá o script de login para que ele exija tanto a senha quanto o código gerado. Esta é a principal mudança de MFA: a senha sozinha não é mais suficiente.

Substitua o login.py por esta versão compatível com MFA:

cat > login.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import json
import sys
from pathlib import Path

USERS = Path("users.json")
SECRET = Path("mfa_secret.txt")

def expected_code(username):
    secret = SECRET.read_text().strip().encode()
    digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
    return digest[:6]

def main():
    if len(sys.argv) != 4:
        print("usage: login.py USERNAME PASSWORD MFA_CODE")
        return 2

    username, password, code = sys.argv[1], sys.argv[2], sys.argv[3]
    users = json.loads(USERS.read_text())
    if users.get(username, {}).get("password") != password:
        print("login failed")
        return 1

    if not hmac.compare_digest(code, expected_code(username)):
        print("mfa failed")
        return 1

    print("login ok")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())
PY

Certifique-se de que o script seja executável:

chmod +x login.py

Gere um novo código e salve-o em uma variável de shell:

CODE="$(python3 generate-code.py alice)"

Use a senha correta e o código juntos:

python3 login.py alice wonderland "$CODE"

A saída deve ser:

login ok

Execute o verificador para a aplicação do MFA:

python3 mfa-check.py enforced

O resultado esperado é:

enforced: complete

Testar Falha e Sucesso do MFA

Nesta etapa, você testará o comportamento final do login. Uma senha correta sem o segundo fator deve falhar, um código incorreto deve falhar, e a senha correta mais o código correto devem ter sucesso.

Primeiro, tente o comando antigo de apenas senha:

python3 login.py alice wonderland || true

A saída deve mostrar a nova mensagem de uso, pois o código MFA está faltando:

usage: login.py USERNAME PASSWORD MFA_CODE

Tente um código incorreto:

python3 login.py alice wonderland 000000 || true

A saída deve ser:

mfa failed

Gere o código correto e faça login:

CODE="$(python3 generate-code.py alice)"
python3 login.py alice wonderland "$CODE"

A saída deve ser:

login ok

Execute o verificador completo:

python3 mfa-check.py all

A saída final deve mostrar que todos os componentes de MFA foram concluídos:

secret: complete
generator: complete
enforced: complete

Resumo

Você transformou um script de login de apenas senha em um fluxo de login multifator simples. Você criou um segredo de MFA separado, gerou um código baseado em HMAC, exigiu esse código no script de login e testou casos de falha e sucesso.

O principal conceito de segurança é que uma senha roubada não deve ser suficiente por si só quando uma segunda prova independente é exigida.