O maior portal de MU Online do Brasil — desde 2003
Tutorial Intermediário Website

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.

GA Gabriel · Atualizado em 12 jul 2026 · ⏱ 20 min de leitura
Resposta rápida

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 openssl habilitado (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 SMTPIndicado paraLimite típico (varia)
Gmail (senha de app)Testes, servidor pequeno~500 e-mails/dia
Brevo / SendinblueProdução pequena/médiaPlano grátis ~300/dia
MailgunProdução com volumePago por milheiro
Amazon SESAlto volume, baixo custoEscala 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.

  1. SPF — um registro TXT que lista quem pode enviar pelo seu domínio. O provedor SMTP fornece o valor exato; algo como v=spf1 include:spf.brevo.com ~all.
  2. DKIM — uma assinatura criptográfica. O provedor gera um par de chaves e você publica a chave pública em um registro TXT (ou CNAME) no subdomínio indicado.
  3. 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 para p=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_bytes sã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

ErroCausa provávelSolução
E-mail cai sempre no spamFalta de SPF/DKIM/DMARCPublique os três registros DNS e valide antes de produção
SMTP connect() failedPorta/segurança erradas ou firewallConfira porta 587/465 e libere a saída no firewall do servidor
Could not authenticateUsuário ou senha SMTP incorretosGere senha de app (Gmail) ou API key (transacional) e reteste
openssl ausenteExtensão desabilitada no PHPHabilite extension=openssl no php.ini e reinicie
Link de recuperação não funcionaComparando token puro com hashAplique hash('sha256', $token) na validação
Token reutilizávelFalta marcar usado = 1Invalide o token dentro da mesma transação da troca de senha
Vazamento de contas existentesMensagens diferentes por e-mailResponda sempre a mesma mensagem neutra

Testando o fluxo completo

  1. Configure o SMTP e envie um e-mail de teste isolado — confirme que chega na caixa de entrada, não no spam.
  2. Publique SPF, DKIM e DMARC e revalide o teste.
  3. Solicite recuperação com um e-mail cadastrado e confirme o recebimento do link.
  4. Solicite com um e-mail inexistente e verifique que a mensagem exibida é idêntica.
  5. Clique no link, defina uma nova senha e faça login no jogo com ela.
  6. Tente usar o mesmo link novamente — deve ser recusado como "já utilizado".
  7. Aguarde a expiração e teste um link vencido — deve pedir nova solicitação.

Checklist de lançamento

  • PHPMailer instalado via Composer e openssl habilitado
  • 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_RESET criada
  • 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.

GA
Editor de guias e builds

Gabriel cobre gameplay, builds de classes, PvP e progressão. Testa cada estratégia em servidor antes de publicar.

Continue lendo

Artigos relacionados