Ficha 02 · Autenticação, secrets, dependências
- Hashear senhas correctamente
- Configurar MFA
- Gerir secrets
- Auditar dependências
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:
-
HTTPONLY— cookie não é acessível viadocument.cookieem JS. Mitiga XSS (script malicioso não rouba cookies). -
SECURE— cookie só transmitido em HTTPS. Impede interceptação em rede insegura. -
SAMESITE=Lax— cookie não enviado em requests cross-site (excepto navegação top-level). Mitiga CSRF. -
PERMANENT_SESSION_LIFETIME— sessão expira após 2 horas. Reduz janela de ataque se cookie for roubado. -
SESSION_COOKIE_NAME— não usa o default que revela framework.
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:
- Mudar a senha da BD (não basta apagar do Git).
- Verificar logs da BD para acessos suspeitos desde commit.
- Limpar do histórico Git com
git filter-repoou BFG. - Force-push (avisar a equipa).
- Considerar todas as outras credenciais nesse commit comprometidas.
b) Prevenção:
.envno.gitignore(e.env.local, etc.)..env.examplecom placeholders para mostrar estrutura.- Pre-commit hooks (
detect-secrets,gitleaks) que bloqueiam commits com padrões de secret. - Code review — segundo par de olhos.
- 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:
- Avaliar exposição — usamos a funcionalidade vulnerável?
- Verificar fix — versão patched disponível?
- Se sim: actualizar, testar, deploy.
- Se não: mitigação temporária (workaround, WAF rule).
- Comunicar à equipa e, se aplicável, utilizadores afectados.
- Post-mortem se chegou a produção.