Como configurar SMTP para recuperação de conta no MU Online
Configure o envio de e-mail por SMTP no site do seu servidor de MU Online e implemente um fluxo seguro de recuperação de senha por token, com PHPMailer, expiração e proteção contra abuso.
Nada frustra mais um jogador do que perder acesso à conta e não ter como recuperá-la. Em servidores de MU Online, a recuperação de senha por e-mail é a solução padrão — mas ela só funciona se o site conseguir enviar e-mails de forma confiável, e isso depende de um SMTP bem configurado. Este tutorial
Nada frustra mais um jogador do que perder acesso à conta e não ter como recuperá-la. Em servidores de MU Online, a recuperação de senha por e-mail é a solução padrão — mas ela só funciona se o site conseguir enviar e-mails de forma confiável, e isso depende de um SMTP bem configurado. Este tutorial cobre as duas metades do problema: primeiro fazer o servidor enviar e-mails que realmente chegam na caixa de entrada (não no spam), e depois construir um fluxo de recuperação de senha seguro, com token de uso único, expiração e proteção contra abuso. Os nomes de tabelas, colunas e o formato de hash de senha aparecem como exemplo e variam por versão do seu MuServer, então confirme cada um no seu banco. Se você ainda está montando a base, veja o guia de como criar um servidor de MU Online.
Pré-requisitos
O envio de e-mail junta três coisas que precisam estar alinhadas: o site, um provedor SMTP e o DNS do seu domínio.
- Site do servidor em PHP funcionando com acesso ao banco onde ficam as contas (
MEMB_INFO, por exemplo). - PHP 7.4 ou superior com
opensslhabilitado (necessário para TLS no SMTP). - Composer instalado, para trazer a biblioteca PHPMailer.
- Uma conta em um provedor SMTP — pode ser Gmail (testes), ou um serviço transacional como Brevo, Mailgun ou Amazon SES (produção).
- Acesso ao painel de DNS do domínio, para criar os registros SPF, DKIM e DMARC.
- Um endereço remetente dedicado, como
[email protected].
| Provedor SMTP | Indicado para | Limite típico (varia) |
|---|---|---|
| Gmail (senha de app) | Testes, servidor pequeno | ~500 e-mails/dia |
| Brevo / Sendinblue | Produção pequena/média | Plano grátis ~300/dia |
| Mailgun | Produção com volume | Pago por milheiro |
| Amazon SES | Alto volume, baixo custo | Escala sob demanda |
Por que não usar a função mail() nativa
O primeiro impulso é usar a função mail() do PHP, mas ela é a causa número um de e-mails na pasta de spam em servidores privados. Ela envia direto pelo servidor local, quase sempre sem autenticação SMTP, sem SPF alinhado e a partir de um IP com reputação ruim. O resultado é previsível: o Gmail e o Outlook rejeitam ou marcam como lixo. A solução é enviar através de um servidor SMTP autenticado usando uma biblioteca dedicada — PHPMailer é a mais consolidada. Ela cuida da negociação TLS, da autenticação e da formatação correta do cabeçalho.
Instale via Composer:
composer require phpmailer/phpmailer
Configurando as credenciais SMTP
Mantenha as credenciais fora do código versionado, em um arquivo de configuração separado com permissão restrita. Nunca commite senhas de SMTP em repositório.
<?php
// config/smtp.php (fora da raiz pública se possível)
return [
'host' => 'smtp-relay.brevo.com', // varia por provedor
'porta' => 587, // 587 STARTTLS ou 465 SSL
'seguranca' => 'tls', // 'tls' ou 'ssl'
'usuario' => 'seu-usuario-smtp',
'senha' => 'SUA_SENHA_OU_API_KEY',
'remetente' => '[email protected]',
'nome_de' => 'Servidor ViciadosMU',
];
Função de envio com PHPMailer
Centralize o envio numa única função reutilizável. Assim, tanto a recuperação de senha quanto qualquer outro e-mail (boas-vindas, aviso de VIP) usam o mesmo caminho testado.
<?php
// src/Mailer.php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require __DIR__ . '/../vendor/autoload.php';
function enviarEmail(string $para, string $assunto, string $corpoHtml): bool
{
$cfg = require __DIR__ . '/../config/smtp.php';
$mail = new PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = $cfg['host'];
$mail->SMTPAuth = true;
$mail->Username = $cfg['usuario'];
$mail->Password = $cfg['senha'];
$mail->SMTPSecure = $cfg['seguranca']; // tls | ssl
$mail->Port = $cfg['porta'];
$mail->CharSet = 'UTF-8';
$mail->setFrom($cfg['remetente'], $cfg['nome_de']);
$mail->addAddress($para);
$mail->isHTML(true);
$mail->Subject = $assunto;
$mail->Body = $corpoHtml;
// Versão texto para clientes sem HTML
$mail->AltBody = strip_tags($corpoHtml);
$mail->send();
return true;
} catch (Exception $e) {
error_log('[SMTP] Falha ao enviar: ' . $mail->ErrorInfo);
return false;
}
}
Teste isoladamente antes de integrar qualquer coisa. Crie um script rápido que chama enviarEmail('[email protected]', 'Teste SMTP', '<b>Funcionou!</b>') e confirme o recebimento. Se cair no spam já nesse teste, o problema está no DNS, não no código.
Configurando SPF, DKIM e DMARC
Esta é a etapa que mais impacta a entrega e a que mais é ignorada. Esses três registros DNS dizem aos servidores de e-mail que o seu domínio autoriza aquele SMTP a enviar em seu nome.
- SPF — um registro
TXTque lista quem pode enviar pelo seu domínio. O provedor SMTP fornece o valor exato; algo comov=spf1 include:spf.brevo.com ~all. - DKIM — uma assinatura criptográfica. O provedor gera um par de chaves e você publica a chave pública em um registro
TXT(ouCNAME) no subdomínio indicado. - DMARC — uma política que diz o que fazer com e-mails que falham SPF/DKIM. Comece brando:
v=DMARC1; p=none; rua=mailto:[email protected], e depois endureça parap=quarantine.
Depois de publicar, aguarde a propagação (pode levar horas) e valide com ferramentas de checagem de SPF/DKIM. Só siga para produção quando os três passarem.
Preparando o banco para o token de recuperação
O fluxo de recuperação precisa de um lugar para guardar os tokens temporários. Crie uma tabela dedicada — não misture com a tabela de contas do jogo.
-- Exemplo (varia por versão): tabela de tokens de recuperação
CREATE TABLE WEB_PASSWORD_RESET (
id INT IDENTITY(1,1) PRIMARY KEY,
conta VARCHAR(10) NOT NULL,
token_hash CHAR(64) NOT NULL, -- sha256 do token
expira_em DATETIME NOT NULL,
usado BIT NOT NULL DEFAULT 0,
ip_origem VARCHAR(45) NULL,
criado_em DATETIME NOT NULL DEFAULT GETDATE()
);
Guardar apenas o token_hash (e não o token puro) é uma decisão de segurança deliberada: mesmo com acesso ao banco, ninguém consegue montar o link de recuperação.
Etapa 1: solicitar recuperação
O jogador informa o e-mail. O sistema procura a conta associada, gera um token aleatório forte, guarda o hash e envia o link por e-mail. Um detalhe importante: a resposta ao usuário deve ser a mesma exista ou não a conta, para não revelar quais e-mails estão cadastrados.
<?php
// solicitar-recuperacao.php
require 'src/Mailer.php';
require 'src/db.php';
function solicitarRecuperacao(string $email): void
{
$pdo = getPDO();
// Procurar conta pelo e-mail (coluna varia por versão)
$stmt = $pdo->prepare("SELECT memb_id FROM MEMB_INFO WHERE mail_addr = ?");
$stmt->execute([$email]);
$conta = $stmt->fetchColumn();
// Sempre responda igual, exista ou não a conta
if ($conta) {
$token = bin2hex(random_bytes(32)); // token puro (só no e-mail)
$tokenHash = hash('sha256', $token);
$expira = date('Y-m-d H:i:s', time() + 1800); // 30 min
$ins = $pdo->prepare(
"INSERT INTO WEB_PASSWORD_RESET (conta, token_hash, expira_em, ip_origem)
VALUES (?, ?, ?, ?)"
);
$ins->execute([$conta, $tokenHash, $expira, $_SERVER['REMOTE_ADDR'] ?? null]);
$link = "https://seuserver.com/redefinir?conta=" . urlencode($conta)
. "&token=" . $token;
$html = "<p>Recebemos um pedido para redefinir sua senha.</p>
<p><a href=\"$link\">Clique aqui para criar uma nova senha</a>.</p>
<p>O link expira em 30 minutos. Se não foi você, ignore este e-mail.</p>";
enviarEmail($email, 'Recuperação de senha - ViciadosMU', $html);
}
// Mensagem neutra para o usuário, sempre
echo 'Se este e-mail estiver cadastrado, você receberá as instruções em instantes.';
}
Etapa 2: validar o token e redefinir a senha
Quando o jogador clica no link, o sistema valida o token contra o hash, checa expiração e uso, e só então permite definir a nova senha. Ao final, o token é marcado como usado — nunca reaproveitável.
<?php
// redefinir.php
require 'src/db.php';
function redefinirSenha(string $conta, string $token, string $novaSenha): array
{
$pdo = getPDO();
if (strlen($novaSenha) < 6) {
return ['ok' => false, 'msg' => 'A senha deve ter ao menos 6 caracteres'];
}
$stmt = $pdo->prepare(
"SELECT id, expira_em, usado FROM WEB_PASSWORD_RESET
WHERE conta = ? AND token_hash = ?"
);
$stmt->execute([$conta, hash('sha256', $token)]);
$reg = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$reg) {
return ['ok' => false, 'msg' => 'Link inválido'];
}
if ($reg['usado']) {
return ['ok' => false, 'msg' => 'Este link já foi utilizado'];
}
if (strtotime($reg['expira_em']) < time()) {
return ['ok' => false, 'msg' => 'Este link expirou. Solicite novamente.'];
}
// Formato de senha VARIA POR VERSÃO — confira o do seu MuServer
// Exemplo comum: MD5 maiúsculo
$senhaCript = strtoupper(md5($novaSenha));
$pdo->beginTransaction();
$pdo->prepare("UPDATE MEMB_INFO SET memb_pw = ? WHERE memb_id = ?")
->execute([$senhaCript, $conta]);
$pdo->prepare("UPDATE WEB_PASSWORD_RESET SET usado = 1 WHERE id = ?")
->execute([$reg['id']]);
$pdo->commit();
return ['ok' => true, 'msg' => 'Senha alterada com sucesso! Já pode entrar no jogo.'];
}
Usar uma transação garante que a senha só é considerada trocada se o token também for invalidado no mesmo movimento — sem janela para reuso.
Protegendo contra abuso
Um formulário de recuperação aberto é um convite a abuso: alguém pode disparar centenas de e-mails ou tentar adivinhar tokens. Algumas defesas simples resolvem a maior parte:
- Limite de solicitações por IP e por conta — no máximo, por exemplo, 3 pedidos a cada 15 minutos.
- Token longo e aleatório — 32 bytes de
random_bytessão impossíveis de adivinhar por força bruta. - CAPTCHA no formulário de solicitação, para conter bots.
- Expiração curta — 15 a 30 minutos é suficiente para o jogador agir.
- Invalidação de tokens antigos — ao gerar um novo, marque os anteriores da mesma conta como usados.
- Log de tentativas, guardando IP e horário, para investigar picos suspeitos.
Erros comuns e soluções
| Erro | Causa provável | Solução |
|---|---|---|
| E-mail cai sempre no spam | Falta de SPF/DKIM/DMARC | Publique os três registros DNS e valide antes de produção |
SMTP connect() failed | Porta/segurança erradas ou firewall | Confira porta 587/465 e libere a saída no firewall do servidor |
Could not authenticate | Usuário ou senha SMTP incorretos | Gere senha de app (Gmail) ou API key (transacional) e reteste |
openssl ausente | Extensão desabilitada no PHP | Habilite extension=openssl no php.ini e reinicie |
| Link de recuperação não funciona | Comparando token puro com hash | Aplique hash('sha256', $token) na validação |
| Token reutilizável | Falta marcar usado = 1 | Invalide o token dentro da mesma transação da troca de senha |
| Vazamento de contas existentes | Mensagens diferentes por e-mail | Responda sempre a mesma mensagem neutra |
Testando o fluxo completo
- Configure o SMTP e envie um e-mail de teste isolado — confirme que chega na caixa de entrada, não no spam.
- Publique SPF, DKIM e DMARC e revalide o teste.
- Solicite recuperação com um e-mail cadastrado e confirme o recebimento do link.
- Solicite com um e-mail inexistente e verifique que a mensagem exibida é idêntica.
- Clique no link, defina uma nova senha e faça login no jogo com ela.
- Tente usar o mesmo link novamente — deve ser recusado como "já utilizado".
- Aguarde a expiração e teste um link vencido — deve pedir nova solicitação.
Checklist de lançamento
- PHPMailer instalado via Composer e
opensslhabilitado - Credenciais SMTP em arquivo fora do versionamento
- E-mail de teste chegando na caixa de entrada (não spam)
- SPF, DKIM e DMARC publicados e validados
- Tabela
WEB_PASSWORD_RESETcriada - Token guardado apenas como hash
sha256 - Expiração de token configurada (15-30 min)
- Token invalidado após o primeiro uso (transação)
- Mensagem neutra que não revela contas existentes
- Rate limiting e CAPTCHA no formulário de solicitação
- Formato de senha confirmado para a sua versão do MuServer
- Fluxo completo testado de ponta a ponta
Perguntas frequentes
Posso usar o Gmail como SMTP do servidor?
Pode para testes e servidores pequenos, mas o Gmail impõe limites diários e exige senha de app. Para produção, um provedor transacional (Brevo, Mailgun, Amazon SES) entrega melhor e não bloqueia sua conta.
Por que os e-mails do meu site caem no spam?
Quase sempre por falta de SPF, DKIM e DMARC no domínio, ou por enviar de um IP com má reputação. Configurar esses três registros DNS é o passo que mais melhora a entrega.
O token de recuperação precisa expirar?
Sim. Um token sem expiração vira uma chave permanente para invadir a conta. O padrão é expirar entre 15 e 60 minutos e invalidá-lo assim que for usado uma vez.
Preciso guardar o token em texto puro no banco?
Não, e não deveria. Guarde apenas o hash do token. Se o banco vazar, ninguém consegue reconstruir os links de recuperação a partir dos hashes.
SMTP com SSL ou TLS, qual escolher?
Use a porta 587 com STARTTLS na maioria dos provedores modernos, ou a porta 465 com SSL implícito quando o provedor exigir. Evite a porta 25 sem criptografia, que costuma estar bloqueada e é insegura.