Partilhar: WhatsApp
aulify
UC UC02836 · T. Desenv. Software

Ficha 02 · Autenticação, secrets, dependências

Hashing, MFA, .env, vulnerabilidades em libs
Versão · Aluno
Tempo · 45 minutos
Cotação · 100 pontos
Aluno(a)
Turma
Data
Objectivos da ficha

Parte I · Autenticação

Exercício 1 · Hashing (15 pts)

Para cada algoritmo, indica se é adequado para senhas em 2026 e porquê:

a) MD5 ___

b) SHA-256 ___

c) bcrypt ___

d) Argon2 ___

e) SHA-1 com salt ___

a) Não. MD5 está quebrado (colisões); GPUs fazem mil milhões de hashes/segundo.

b) Não. SHA-256 é rápido demais. Para integridade de ficheiros é OK; para senhas é vulnerável a brute-force.

c) Sim. Lento intencionalmente, salt automático, factor de trabalho ajustável.

d) Sim. Argon2 é o vencedor da Password Hashing Competition 2015; recomendado pelo OWASP.

e) Não. SHA-1 com salt apenas evita rainbow tables, mas continua rápido demais.

Exercício 2 · Implementar (15 pts)

Escreve as funções Python para:

a) Hashear senha com bcrypt no registo.

b) Verificar senha no login.

import bcrypt

def hash_password(plain: str) -> str:
    """Hash de senha. Devolve string para guardar na BD."""
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(plain.encode("utf-8"), salt).decode("utf-8")

def verify_password(plain: str, hashed: str) -> bool:
    """Verifica se senha plain corresponde ao hashed."""
    try:
        return bcrypt.checkpw(plain.encode("utf-8"), hashed.encode("utf-8"))
    except (ValueError, TypeError):
        return False

# No registo
user.pwd_hash = hash_password(form.password)

# No login
if verify_password(form.password, user.pwd_hash):
    # OK
    pass

Exercício 3 · Sessão segura (15 pts)

Configura cookies de sessão Flask com flags adequadas e explica cada uma:

from datetime import timedelta

app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_SAMESITE="Lax",
    PERMANENT_SESSION_LIFETIME=timedelta(hours=2),
    SESSION_COOKIE_NAME="aulify_session",
)

Explicações:

Parte II · MFA

Exercício 4 · TOTP (15 pts)

Escreve código Python que:

a) Gera novo secret para utilizador.

b) Devolve URL/QR para Google Authenticator.

c) Verifica código introduzido.

import pyotp
import qrcode
import io
import base64

def gerar_mfa_secret() -> str:
    """Gera secret para guardar na BD."""
    return pyotp.random_base32()

def url_para_qr(secret: str, email: str, app_name: str = "Aulify") -> str:
    """Devolve URL otpauth:// que Google Authenticator entende."""
    totp = pyotp.TOTP(secret)
    return totp.provisioning_uri(name=email, issuer_name=app_name)

def qr_code_base64(uri: str) -> str:
    """Devolve QR code como imagem base64 para mostrar na página."""
    img = qrcode.make(uri)
    buf = io.BytesIO()
    img.save(buf, format="PNG")
    return base64.b64encode(buf.getvalue()).decode()

def verificar_codigo(secret: str, codigo: str) -> bool:
    """Verifica código de 6 dígitos."""
    totp = pyotp.TOTP(secret)
    return totp.verify(codigo, valid_window=1)  # tolera ±30s

# Uso
secret = gerar_mfa_secret()
# Guardar secret na BD
uri = url_para_qr(secret, "ana@aulify.pt")
qr_img = qr_code_base64(uri)
# Mostrar no template: <img src="data:image/png;base64,{{qr_img}}">

# No login
if verificar_codigo(user.mfa_secret, request.form["codigo"]):
    # MFA OK
    ...

Parte III · Secrets

Exercício 5 · .env (10 pts)

A Joana comitou por engano o ficheiro .env com DATABASE_PASSWORD=Secreta123! para o repositório público.

a) Que acções imediatas tomar? (5 pts)

b) Como prevenir no futuro? (5 pts)

a) Imediato:

  1. Mudar a senha da BD (não basta apagar do Git).
  2. Verificar logs da BD para acessos suspeitos desde commit.
  3. Limpar do histórico Git com git filter-repo ou BFG.
  4. Force-push (avisar a equipa).
  5. Considerar todas as outras credenciais nesse commit comprometidas.

b) Prevenção:

  1. .env no .gitignore (e .env.local, etc.).
  2. .env.example com placeholders para mostrar estrutura.
  3. Pre-commit hooks (detect-secrets, gitleaks) que bloqueiam commits com padrões de secret.
  4. Code review — segundo par de olhos.
  5. GitHub secret scanning activado.

Exercício 6 · Variáveis de ambiente (10 pts)

Escreve código que lê configuração de variáveis de ambiente com defaults seguros:

import os
from dotenv import load_dotenv

load_dotenv()  # carrega .env localmente; em produção usa env vars do sistema

class Config:
    # Obrigatórios: sem default, lança erro se faltar
    SECRET_KEY = os.environ["SECRET_KEY"]
    DATABASE_URL = os.environ["DATABASE_URL"]

    # Opcionais: com defaults razoáveis
    DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
    LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
    SMTP_HOST = os.environ.get("SMTP_HOST", "localhost")
    SMTP_PORT = int(os.environ.get("SMTP_PORT", "587"))

    # Nunca hardcode
    # SECRET_KEY = "abc123"   # ERRADO

config = Config()

Em produção, o sistema (Heroku, AWS, Docker) injecta variáveis de ambiente. Em dev, .env simula isso.

Parte IV · Dependências

Exercício 7 · Auditar (20 pts)

A escola tem várias apps Python e quer estabelecer rotina de auditoria de dependências.

a) Que ferramentas usar? (5 pts)

b) Com que frequência rever? (5 pts)

c) Como integrar no fluxo de CI/CD? (5 pts)

d) Que fazer ao descobrir CVE crítico numa dependência? (5 pts)

a) Ferramentas: - pip-audit (oficial Python). - Snyk — comercial, plano gratuito. - GitHub Dependabot — integrado, alerta + PRs automáticos. - OWASP Dependency-Check.

b) Frequência: - Diário — Dependabot alerta automaticamente. - Semanal — revisão manual de alertas pendentes. - A cada release — auditoria completa antes de deploy.

c) Integração CI/CD:

# GitHub Actions
- name: Audit dependencies
  run: |
    pip install pip-audit
    pip-audit --requirement requirements.txt

Falhar build se houver CVE crítico (ou warn em caso menor).

d) Ao descobrir CVE crítico:

  1. Avaliar exposição — usamos a funcionalidade vulnerável?
  2. Verificar fix — versão patched disponível?
  3. Se sim: actualizar, testar, deploy.
  4. Se não: mitigação temporária (workaround, WAF rule).
  5. Comunicar à equipa e, se aplicável, utilizadores afectados.
  6. Post-mortem se chegou a produção.