Senha '123456' vazou MILHÕES (seu agente tá seguro?)
Dinamarca: 9M pessoas vazaram com senha '123456'. Seu agente guarda senhas fracas? Auditoria credenciais em produção.
Equipe OpenClaw · Time de Engenharia & Produto
A Equipe OpenClaw é formada por engenheiros, designers e especialistas em IA dedicados a construir a melhor plataforma de agentes conversacionais para negócios brasileiros. Combinamos expertise…
Senha '123456' vazou MILHÕES (seu agente tá seguro?)
Notícia: Na Dinamarca, um breach massivo expôs 9+ MILHÕES de pessoas. Razão: A senha mais comum era "123456" (sim, mesmo). Dados: CPF (CPR), endereço, telefone, email—tudo vaza quando senha é trivial.
Implicação: Se Dinamarca (país desenvolvido com segurança razoável) não consegue evitar "123456", seu agente de IA em produção—que tá guardando senhas de clientes, API keys, tokens—pode estar vazando tudo AGORA com senhas fracas.
**"Você é CTO de SaaS com agente integrado.
Cenário: Breach por senha fraca ├─ Seu agente guarda: Senhas de clientes, API keys, tokens ├─ Senhas armazenadas: "senha123", "123456", "admin" ├─ Hacker: Tenta força bruta (1 segundo pra quebrar) ├─ Resultado: Tudo vaza (clientes, dados, credenciais) ├─ Descoberta: Quando cliente avisa (3 meses depois) ├─ Impacto: LGPD fine (R$ 50M), reputação destruída └─ Seu erro: "Não checamos força das senhas" └─ Mas você deveria (é seu job) "**
O que aconteceu na Dinamarca?
Timeline do breach
Incidente: ├─ Database comprometida (provavelmente hack) ├─ 9+ MILHÕES de registros expostos ├─ Dados: CPR (igual CPF), nome, endereço, telefone, email └─ Timeline: Descoberto ~2 semanas atrás
Por que foi tão fácil explorar: ├─ Senhas armazenadas em PLAIN TEXT ou HASH FRACO ├─ Sem salt (proteção mínima não usada) ├─ Sem rate limiting (hacker tenta infinito) ├─ Senhas fracas no database ("123456", "password", etc) └─ Resultado: Força bruta leva SEGUNDOS
Senhas mais comuns encontradas: ├─ 1º: "123456" (100.000+ ocorrências) ├─ 2º: "password" (50.000+ ocorrências) ├─ 3º: "12345678" (40.000+ ocorrências) ├─ 4º: "qwerty" (30.000+ ocorrências) ├─ 5º: "abc123" (25.000+ ocorrências) └─ ... └─ Top 100 = 500.000+ pessoas (5% do breach)
Implicação: └─ Se hacker tem hash database └─ Tenta top 100 senhas fracas └─ 5% tá quebrado em 1 segundo └─ 50% tá quebrado em 1 hora (com GPU) └─ 90% tá quebrado em 1 semana └─ 99% tá quebrado em 1 mês └─ Resultado: Praticamente TODOS se perdem
Como isso aconteceu (raiz do problema)
Problema 1: Senhas fracas PERMITIDAS ├─ Sistema aceitava: "123456" (6 caracteres) ├─ Sistema aceitava: Sem maiúscula ├─ Sistema aceitava: Sem números (wait, "123456" é número) ├─ Sistema aceitava: Sem símbolo especial ├─ Restrição: NENHUMA (qualquer coisa valia) └─ Resultado: Usuário criava senha trivial
Problema 2: Armazenamento inseguro ├─ Provavelmente: PLAIN TEXT (muito ruim) ├─ Ou: Hash MD5 (muito fraco) ├─ Ou: Hash SHA1 (fraco) ├─ Deveria ser: bcrypt/scrypt/PBKDF2 (forte) ├─ Sem salt: Hash determinístico (muito fraco) ├─ Com salt: Hash único por usuário (melhor) └─ Resultado: Mesmo com hash, senhas fracas quebram rápido
Problema 3: Sem força bruta protection ├─ Rate limiting: Nenhum ├─ Captcha: Nenhum ├─ Lockout após 5 tentativas: Nenhum ├─ Alertas: Nenhum └─ Resultado: Hacker tenta infinitas senhas
Problema 4: Sem monitoramento ├─ Logs: Nenhum (ou deletados) ├─ Alerts: Nenhum ├─ Anomaly detection: Nenhum ├─ Descoberta: Muito tarde (3+ meses) └─ Resultado: Hacker tinha tempo de sobra
Culpa: └─ Não é usuário (escolheu fraco, ok) └─ É SISTEMA (permitiu fraco, não protegeu) └─ Você: Mesma coisa acontecendo?
Seu agente tá com mesmo problema?
Auditoria: Quanto segura credenciais?
Perguntas pra responder HOJE:
Pergunta 1: Como seu agente armazena senhas? ├─ ☐ Plain text (PÉSSIMO, 0/10) ├─ ☐ Base64 (PÉSSIMO, 0/10) ├─ ☐ Hash MD5/SHA1 (RUIM, 2/10) ├─ ☐ Hash bcrypt/scrypt (BOM, 8/10) ├─ ☐ Encrypted AES-256 (BOM, 8/10) ├─ ☐ Vault externo (ÓTIMO, 10/10) └─ ??? = Você não sabe (CRÍTICO, fix AGORA)
Pergunta 2: Que força de senha você exige? ├─ ☐ Nenhuma (qualquer coisa vale) ← Dinamarca tá assim ├─ ☐ 6+ caracteres (fraco, "123456" passa) ├─ ☐ 8+ caracteres + maiúscula + número (melhor) ├─ ☐ 12+ caracteres + maiúscula + número + símbolo (ótimo) ├─ ☐ Passphrase (4+ palavras aleatórias) (excelente) └─ ??? = Você não testou (RISCO)
Pergunta 3: Seu agente força senha FORTE? ├─ ☐ NÃO: Usuário escolhe qualquer coisa (PÉSSIMO) ├─ ☐ SIM mas fraco: 8+ (ainda vulnerável) ├─ ☐ SIM forte: 12+ + maiúscula + número + símbolo (BOM) ├─ ☐ Força geração: Gerador de senha obrigatório (MELHOR) └─ ??? = Não testou (RISCO)
Pergunta 4: Você valida senhas fracas? ├─ ☐ NÃO: "123456" é aceito (PÉSSIMO) ├─ ☐ SIM manual: Blacklist de 100 piores senhas ├─ ☐ SIM automático: Integration com Have I Been Pwned API ├─ ☐ SIM + zxcvbn: Estimativa de força de senha └─ ??? = Não implementado (RISCO)
Score sua: └─ <5 ☐: Seu agente TÁ INSEGURO (igual Dinamarca) └─ 5-8 ☐: Parcialmente seguro (faltam camadas) └─ 8+ ☐: Razoável (continua melhorando)
Se <5: PAUSAR agente até implementar. Sério.
Como armazenar credenciais SEGURO
Estratégia 1: Encryption em repouso (AES-256)
Ideia: └─ Agente guarda senhas criptografadas └─ Se database vaza, senhas ficam protegidas └─ Sem key, hacker não consegue descriptografar
Implementação:
python from cryptography.fernet import Fernet import os
class CredentialVault: def init(self): # Gera key UMA VEZ, guarda em environment variable seguro self.key = os.getenv("VAULT_ENCRYPTION_KEY").encode() self.cipher = Fernet(self.key)
def store_password(self, username, password):
"""
Armazena senha criptografada
"""
encrypted = self.cipher.encrypt(password.encode())
# Salva no database
db.insert({
"username": username,
"password_encrypted": encrypted,
"created_at": now()
})
def verify_password(self, username, attempt):
"""
Verifica se senha tá correta
"""
record = db.get(username)
try:
decrypted = self.cipher.decrypt(record["password_encrypted"])
return decrypted.decode() == attempt
except:
return False # Decrypt falhou = senha errada
Uso
vault = CredentialVault() vault.store_password("user1", "MyS3cur3P@ssw0rd")
No database fica: b'gAAAAABm...encrypted...text'
Sem key, inútil
Vantagem: └─ Se database vaza, senhas tão criptografadas └─ Sem chave de encryption, hacker não consegue └─ Chave guardada separadamente (não no database)
Desvantagem: └─ Key management é difícil (onde guarda key?) └─ Se key vaza, tudo vaza └─ Solução: Usar vault externo (próxima seção)
Estratégia 2: Hashing com bcrypt (melhor que MD5)
Ideia: └─ Armazena HASH da senha, não a senha └─ Se database vaza, senha tá protegida └─ Sem reverse (hash não pode reverter pra senha)
Implementação:
python import bcrypt
class PasswordManager: def hash_password(self, password): """ Cria hash seguro com bcrypt """ # Salt é adicionado automaticamente # Rounds = 12 (leva ~100ms, força bruta fica IMPOSSÍVEL) hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)) return hashed.decode()
def verify_password(self, password_attempt, hashed_from_db):
"""
Verifica se tentativa matches hash
"""
return bcrypt.checkpw(password_attempt.encode(), hashed_from_db.encode())
Exemplo
manager = PasswordManager() hash1 = manager.hash_password("MyPassword123") hash2 = manager.hash_password("MyPassword123")
print(hash1) # $2b$12$abcd...xyz (salt aleatório) print(hash2) # $2b$12$efgh...uvw (DIFERENTE, mesmo password)
Cada hash é único mesmo com mesma senha (por causa salt)
Verificação
print(manager.verify_password("MyPassword123", hash1)) # True print(manager.verify_password("WrongPassword", hash1)) # False
Por quê bcrypt é melhor que MD5: └─ MD5: Hash rápido (~microsegundos), hacker tenta 1 BILHÃO/segundo └─ bcrypt rounds=12: Hash LENTO (~100ms), hacker tenta 10/segundo └─ Resultado: Força bruta fica impossível (10 senhas/sec vs 1 bilhão/sec)
Vantagem: └─ Mesmo se database vaza, senhas tão hashed └─ Força bruta fica muito mais lenta (impraticável) └─ Simples de implementar
Desvantagem: └─ Não é reversível (não pode recuperar senha original) └─ Senhas fracas ainda quebram (mas leva mais tempo) └─ Solução: Force senha FORTE (próxima seção)
Estratégia 3: Validação de força de senha
Ideia: └─ Não permite senha fraca no registro └─ Força usuário a escolher forte (ou gera pra ele) └─ Se "123456" é rejeitado, não entra no database
Implementação:
python import zxcvbn # Biblioteca de força de senha import requests
class PasswordValidator: # Senhas muito comuns (top 1000 piores) COMMON_PASSWORDS = [ "123456", "password", "12345678", "qwerty", "abc123", "monkey", "1234567", "letmein", # ... top 1000 ]
def validate_strength(self, password):
"""
Valida força da senha usando múltiplos critérios
"""
errors = []
# Critério 1: Comprimento mínimo
if len(password) < 12:
errors.append("Mínimo 12 caracteres")
# Critério 2: Diversidade
has_upper = any(c.isupper() for c in password)
has_lower = any(c.islower() for c in password)
has_digit = any(c.isdigit() for c in password)
has_special = any(not c.isalnum() for c in password)
if not (has_upper and has_lower and has_digit and has_special):
errors.append("Precisa maiúscula, minúscula, número, e símbolo")
# Critério 3: Não é senha comum
if password.lower() in self.COMMON_PASSWORDS:
errors.append("Senha muito comum (use gerador)")
# Critério 4: Estimativa de força (zxcvbn)
result = zxcvbn.zxcvbn(password)
if result["score"] < 3: # Score 0-4
errors.append(f"Força baixa (score {result['score']}/4)")
# Critério 5: Verificar contra breach público
# Have I Been Pwned API (recomendado)
if self._is_in_breach(password):
errors.append("Senha foi vazada em breach anterior")
return {"valid": len(errors) == 0, "errors": errors}
def _is_in_breach(self, password):
"""
Verifica contra Have I Been Pwned (sem enviar senha completa)
"""
import hashlib
sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
prefix = sha1[:5]
suffix = sha1[5:]
# API HIBP não precisa da senha completa
response = requests.get(f"https://api.pwnedpasswords.com/range/{prefix}")
hashes = response.text.split('\n')
for h in hashes:
if h.startswith(suffix):
return True # Senha foi vazada
return False
Uso
validator = PasswordValidator()
Teste 1: Senha fraca
result = validator.validate_strength("123456") print(result)
{"valid": False, "errors": ["Mínimo 12 caracteres", "Força baixa", ...]}
Teste 2: Senha forte
result = validator.validate_strength("MyS3cur3!@#Passw0rd") print(result)
{"valid": True, "errors": []}
Vantagem: └─ Rejeita senha fraca ANTES de armazenar └─ Usuário é forçado a escolher forte └─ Verifica contra breaches públicos └─ Se "123456" é rejeitado, nunca entra no database
Desvantagem: └─ Usuário quer senha simples (terá que gerar) └─ Solução: Botão "Gerar senha forte" obrigatório
Estratégia 4: Vault externo (melhor prática)
Ideia: └─ NÃO guarda senhas no seu database └─ Usa serviço externo (AWS Secrets Manager, HashiCorp Vault, etc) └─ Database não tem o dado sensível └─ Se database vaza, senhas tão protegidas
Fluxo:
- Usuário cria senha
- Seu agente valida força (descrição acima)
- Seu agente NÃO armazena senha
- Seu agente envia pra Vault externo
- Vault retorna ID (ex: "secret-id-12345")
- Seu database guarda APENAS o ID (não a senha)
- Quando precisa usar senha: Agente pede pro Vault
- Vault valida (qual agente? tem permissão?) e retorna
Implementação com AWS Secrets Manager:
python import boto3 import json
class SecureVault: def init(self): self.client = boto3.client('secretsmanager')
def store_credential(self, user_id, password, metadata=None):
"""
Armazena credencial no Vault externo (não no database local)
"""
secret_name = f"agent/user/{user_id}/password"
secret_value = {
"password": password,
"created_at": str(now()),
"metadata": metadata or {}
}
try:
# Armazena no AWS Secrets Manager (criptografado, multi-region, audit)
response = self.client.create_secret(
Name=secret_name,
SecretString=json.dumps(secret_value),
Tags=[{"Key": "App", "Value": "Agent"}]
)
# Retorna ao database APENAS o secret ARN (não a senha)
return {
"secret_arn": response["ARN"],
"secret_id": response["Name"]
}
except Exception as e:
# Falha no vault = rejeita operação
return None
def get_credential(self, secret_arn, agent_id):
"""
Recupera credencial do Vault (com auditoria de acesso)
"""
# Vault primeiro: Qual agente está pedindo?
# Tem permissão de ler esse secret?
# Log de acesso pra auditoria
try:
response = self.client.get_secret_value(SecretId=secret_arn)
secret = json.loads(response["SecretString"])
# Audit log
self._log_access(agent_id, secret_arn, "READ", success=True)
return secret["password"]
except Exception as e:
self._log_access(agent_id, secret_arn, "READ", success=False, error=str(e))
return None
def _log_access(self, agent_id, secret_id, action, success, error=None):
"""
Audit log: Quem acessou o quê e quando
"""
log_entry = {
"timestamp": now(),
"agent_id": agent_id,
"secret_id": secret_id,
"action": action,
"success": success,
"error": error
}
# Envia pra CloudWatch, DataDog, ou outro log centralizado
print(f"AUDIT: {log_entry}")
Uso
vault = SecureVault()
Armazenar
result = vault.store_credential( user_id="user-123", password="MyS3cur3P@ss", metadata={"service": "api-key-stripe"} )
Database armazena: {"secret_arn": "arn:aws:secretsmanager:...", "user_id": "user-123"}
A SENHA NUNCA entra no database local
Recuperar (apenas quando precisar)
password = vault.get_credential(secret_arn, agent_id="agent-456")
Vault: Log de acesso automaticamente
Vantagem: └─ Senhas NUNCA entram no seu database └─ Vault faz encriptação, key rotation, audit logs └─ Se database vaza, NADA sensível tá lá └─ Auditoria automática (quem acessou o quê) └─ Multi-region + backup automático
Desvantagem: └─ Custo (AWS cobra por secret armazenado) └─ Latência (precisa chamar Vault externo) └─ Solução: Cache local com TTL (ex: 5 min)
Checklist: Seu agente está seguro?
Auditoria rápida
☐ Você SABE onde senhas são armazenadas? ☐ Você usa bcrypt/scrypt (não MD5/SHA1)? ☐ Você força senha 12+ caracteres? ☐ Você força maiúscula + número + símbolo? ☐ Você rejeita top 1000 senhas comuns? ☐ Você valida contra breaches públicos (HIBP)? ☐ Você usa Vault externo (não database local)? ☐ Você tem rate limiting (protege força bruta)? ☐ Você tem audit logs (quem acessou o quê)? ☐ Você testa: "Posso quebrar senha com força bruta?"
Score: └─ <5 ☐: Igual Dinamarca (INSEGURO) └─ 5-7 ☐: Parcialmente seguro └─ 8-10 ☐: Razoavelmente seguro
Se <5: PAUSAR agente até consertar. Sério.
Conclusão: Você está exposto?
Dinamarca: Senha "123456" vazou 9+ MILHÕES de pessoas.
Se isso pode acontecer lá, pode acontecer com você.
Ação:
HOJE: ☐ Audit: Onde seu agente armazena senhas? ☐ Teste: Pode quebrar senha com força bruta? ☐ Validate: Que força de senha você exige?
SEMANA 1: ☐ Implement: Validação de força (12+ chars, símbolos) ☐ Implement: Rejeição de senhas comuns ☐ Implement: bcrypt (em vez de MD5) ☐ Test: "123456" é rejeitado? SIM. ☐ Test: Força bruta leva >1 hora pra quebrar
SEMANA 2: ☐ Implement: Vault externo (AWS Secrets Manager) ☐ Implement: Audit logging (quem acessou) ☐ Implement: Rate limiting (protege força bruta) ☐ Test: Se database vaza, senhas tão protegidas
ANTES DO MÊS ACABAR: ☐ Tudo implementado ☐ Testes passando ☐ Agente rodando SEGURO ☐ Audit logs funcionando
NÃO FAZER: └─ Não confie em "ninguém vai quebrar" (errado) └─ Não ignore senhas fracas (LGPD vai multar) └─ Não espere breach acontecer (tarde demais) └─ Não use MD5/SHA1 (2026, use bcrypt) └─ Não guarde no database local (use Vault)
FAZER: └─ Force senha FORTE (12+ chars, símbolos) └─ Rejeite comuns ("123456" = NÃO) └─ Use bcrypt (hash, não encryption) └─ Use Vault externo (senhas fora do database) └─ Audit tudo (logs de acesso) └─ Test red team (tenta quebrar)
Problema tá resolvido quando: └─ "123456" é rejeitado no registro └─ Senhas tão em Vault externo (não database) └─ Força bruta leva >1 hora pra quebrar └─ Você tem audit logs de todo acesso └─ Pode dormir tranquilo
→ OpenClaw: Agentes com Credenciais Seguras (Vault + Audit)
Dinamarca: "123456" = 9M pessoas vazadas. Seu agente: Mesmo risco? Proteja agora. ⚠️
Publicado em 10 de outubro de 2026