Como proteger o painel administrativo do seu servidor de MU Online com autenticação em duas etapas (2FA)
Implemente autenticação em duas etapas (2FA) baseada em TOTP no painel administrativo PHP do seu servidor de MU Online, com geração de segredo, QR Code, verificação de código e plano de recuperação de acesso caso o jogador perca o dispositivo.
O painel administrativo de um servidor de MU Online é o alvo mais valioso para um atacante: uma única conta comprometida com nível de Administrador pode gerar itens, alterar saldos, banir jogadores ou derrubar o banco de dados inteiro. Proteger esse acesso apenas com senha — mesmo senha forte — deix
O painel administrativo de um servidor de MU Online é o alvo mais valioso para um atacante: uma única conta comprometida com nível de Administrador pode gerar itens, alterar saldos, banir jogadores ou derrubar o banco de dados inteiro. Proteger esse acesso apenas com senha — mesmo senha forte — deixa o servidor vulnerável a phishing, reuso de senha vazada em outro site e keyloggers. A autenticação em duas etapas (2FA) baseada em TOTP adiciona uma segunda barreira que um atacante não consegue reproduzir só com a senha roubada. Este tutorial mostra como implementar 2FA no painel PHP legado do servidor, do lado do banco de dados até a tela de verificação do jogador.
Por que senha sozinha não é suficiente
Senhas vazam por vários caminhos que independem da força da senha em si: reuso da mesma senha em outro site que sofreu vazamento, phishing disfarçado de página de login idêntica, ou malware tipo keylogger na máquina do administrador. Em qualquer um desses casos, o atacante obtém a senha correta e passa pela autenticação normalmente. O 2FA quebra esse cenário porque exige um segundo fator — algo que o administrador tem (o código gerado no celular), não apenas algo que ele sabe (a senha).
Como funciona o TOTP (Time-based One-Time Password)
O TOTP gera um código numérico de 6 dígitos que muda a cada 30 segundos, calculado a partir de um segredo compartilhado entre o servidor e o aplicativo autenticador (Google Authenticator, Authy, Microsoft Authenticator) e do horário atual. O servidor e o celular nunca trocam esse código pela rede no momento da geração — ambos calculam o mesmo valor de forma independente, o que torna o método resistente a interceptação de rede.
| Componente | Papel |
|---|---|
| Segredo compartilhado (secret) | Gerado uma vez, armazenado no banco e no app do usuário |
| Timestamp atual | Sincronizado entre servidor e dispositivo (janela de 30s) |
| Algoritmo HMAC-SHA1 (RFC 6238) | Combina segredo + timestamp para gerar o código de 6 dígitos |
| Janela de tolerância | Aceita o código do intervalo anterior/seguinte para tolerar pequena diferença de relógio |
Passo 1 — Preparar a tabela de segredos no banco
Adicione uma coluna para armazenar o segredo TOTP de cada conta administrativa, e uma tabela separada para os códigos de recuperação:
ALTER TABLE admin_accounts ADD COLUMN totp_secret VARCHAR(64) NULL;
ALTER TABLE admin_accounts ADD COLUMN totp_enabled TINYINT(1) NOT NULL DEFAULT 0;
CREATE TABLE admin_recovery_codes (
id INT AUTO_INCREMENT PRIMARY KEY,
admin_id INT NOT NULL,
code_hash VARCHAR(255) NOT NULL,
used TINYINT(1) NOT NULL DEFAULT 0,
FOREIGN KEY (admin_id) REFERENCES admin_accounts(id)
);
Nunca armazene o segredo TOTP em texto simples acessível publicamente, e nunca armazene os códigos de recuperação sem hash — trate-os como senhas de uso único.
Passo 2 — Instalar uma biblioteca TOTP em PHP
Use uma biblioteca open source consolidada em vez de implementar o algoritmo do zero:
composer require spomky-labs/otphp
Isso evita erros sutis de implementação do RFC 6238 que poderiam gerar códigos incompatíveis com os aplicativos autenticadores padrão do mercado.
Passo 3 — Gerar o segredo e o QR Code de ativação
use OTPHP\TOTP;
$totp = TOTP::generate();
$totp->setLabel('PainelAdmin-ViciadosMU');
$totp->setIssuer('ViciadosMU');
$secret = $totp->getSecret();
// Salvar $secret (criptografado) na coluna totp_secret do admin logado
$qrCodeUrl = $totp->getQrCodeUri(
'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=[DATA]',
'[DATA]'
);
// Exibir $qrCodeUrl na tela para o admin escanear com o app autenticador
Exiba o QR Code apenas uma vez, na tela de ativação, e nunca registre o segredo em log de aplicação — ele é equivalente a uma senha mestra do segundo fator.
Passo 4 — Verificar o código na ativação antes de confirmar
Antes de marcar totp_enabled = 1, exija que o administrador digite o código gerado pelo app, confirmando que o QR Code foi escaneado corretamente:
$codigoDigitado = $_POST['codigo'];
if ($totp->verify($codigoDigitado)) {
// Ativar totp_enabled = 1 no banco
// Gerar e exibir códigos de recuperação (uma única vez)
} else {
// Erro: código inválido, não ativar ainda
}
Esse passo evita o cenário em que o administrador acha que ativou o 2FA, mas o app não foi configurado corretamente, e ele fica bloqueado no próximo login.
Passo 5 — Gerar e exibir códigos de recuperação
No momento da ativação bem-sucedida, gere de 8 a 10 códigos de uso único e exiba-os apenas uma vez, orientando o administrador a guardá-los offline:
function gerarCodigosRecuperacao(int $quantidade = 10): array {
$codigos = [];
for ($i = 0; $i < $quantidade; $i++) {
$codigos[] = strtoupper(bin2hex(random_bytes(4))); // ex: A1B2C3D4
}
return $codigos;
}
// Salvar apenas o hash de cada código na tabela admin_recovery_codes
foreach ($codigos as $codigo) {
$hash = password_hash($codigo, PASSWORD_BCRYPT);
// INSERT INTO admin_recovery_codes (admin_id, code_hash) VALUES (?, ?)
}
Passo 6 — Exigir o segundo fator no login
No fluxo de login, depois da senha validada corretamente, exiba a tela de código TOTP antes de conceder a sessão administrativa completa:
if ($senhaValida) {
if ($admin['totp_enabled']) {
// Redirecionar para tela de verificação de código,
// sem ainda conceder sessão de nível administrativo
$_SESSION['pending_2fa_admin_id'] = $admin['id'];
} else {
// Conceder sessão normalmente (ou forçar ativação do 2FA, se obrigatório)
}
}
// Na tela de verificação
$codigo = $_POST['codigo'];
$totp = TOTP::createFromSecret($segredoDoBanco);
if ($totp->verify($codigo)) {
// Conceder sessão administrativa completa
} else {
// Verificar se é um código de recuperação válido (hash match e used = 0)
// Caso contrário, negar acesso e logar tentativa
}
Passo 7 — Definir a política de obrigatoriedade por nível de conta
Nem toda conta precisa do mesmo rigor. Defina a política conforme o risco do nível de acesso:
| Nível de conta | 2FA obrigatório? | Justificativa |
|---|---|---|
| Owner | Sim | Acesso total, maior dano possível se comprometido |
| Administrador | Sim | Comandos sensíveis (additem, setmoney, ban) |
| Moderador de evento | Recomendado | Acesso limitado, mas ainda distribui itens |
| Suporte júnior | Opcional | Comandos de menor impacto econômico |
| Conta de jogador comum | Opcional/incentivado | Proteção de conta pessoal, não administrativa |
Passo 8 — Plano de recuperação sem comprometer a segurança
Quando um administrador perde o dispositivo e não tem os códigos de recuperação, defina um processo manual e documentado em vez de simplesmente desativar o 2FA por pedido informal no Discord:
- Exija verificação de identidade fora de banda (chamada de vídeo com outro administrador de confiança, ou confirmação por e-mail cadastrado previamente).
- Um segundo administrador (nunca o próprio solicitante) reseta
totp_enabled = 0diretamente no banco. - Force a reativação do 2FA imediatamente no próximo login.
- Registre o incidente em log interno, incluindo quem autorizou o reset.
Erros comuns e soluções
| Sintoma | Causa provável | Solução |
|---|---|---|
| Código do app não bate com o servidor | Relógio do celular ou servidor dessincronizado | Sincronizar NTP no servidor e conferir hora do celular |
| Admin perdeu acesso e não tem códigos de recuperação | Códigos não gerados ou não guardados na ativação | Implementar processo manual de reset com verificação cruzada |
| Segredo TOTP exposto em log de aplicação | Debug/log registrando variável sensível | Remover log de variáveis de segredo e rotacionar segredos expostos |
| 2FA ativado mas sessão nunca pede o segundo fator | Fluxo de login não verifica totp_enabled corretamente | Revisar lógica de sessão pendente antes de conceder acesso completo |
| Reset de 2FA feito sem verificação de identidade | Falta de processo documentado de recuperação | Formalizar processo com segundo administrador obrigatório |
Checklist de implementação de 2FA no painel
- Tabela de segredo TOTP e códigos de recuperação criada no banco.
- Biblioteca TOTP confiável integrada (ex.: spomky-labs/otphp).
- Fluxo de ativação com QR Code e verificação de código antes de confirmar.
- Códigos de recuperação gerados, hasheados e exibidos uma única vez.
- Login exigindo segundo fator antes de conceder sessão administrativa completa.
- Política de obrigatoriedade definida por nível de conta.
- Processo documentado de recuperação de acesso com verificação cruzada.
Com o painel protegido por 2FA, revise também os demais controles de acesso da equipe administrativa — o guia de criação de servidor de MU Online cobre a base de infraestrutura sobre a qual essa camada extra de segurança deve ser construída.
Perguntas frequentes
2FA por SMS é seguro o suficiente para o painel de admin?
É melhor que nada, mas SMS é vulnerável a SIM swap e interceptação. Para contas de nível Administrador e Owner, prefira TOTP (Google Authenticator, Authy) ou uma chave física (FIDO2/WebAuthn), reservando SMS no máximo como método secundário de recuperação.
O que acontece se o administrador perder o celular com o autenticador?
Por isso é essencial gerar e armazenar códigos de recuperação (backup codes) no momento da ativação do 2FA, guardados em local seguro offline. Sem isso, a única saída é um processo manual de verificação de identidade para resetar o 2FA diretamente no banco de dados.
2FA deve ser obrigatório para todos os níveis de conta do painel?
Recomenda-se obrigatório para os níveis com comandos administrativos sensíveis (Admin, Owner) e opcional, mas incentivado, para contas comuns de jogador. Tornar obrigatório para todos de uma vez pode gerar atrito de suporte se a comunidade não estiver preparada.
TOTP funciona sem internet no celular do administrador?
Sim. O algoritmo TOTP gera o código localmente a partir do segredo compartilhado e do horário do dispositivo, sem precisar de conexão à internet no momento da geração — só o relógio do celular precisa estar sincronizado corretamente.
Preciso de uma biblioteca paga para implementar TOTP em PHP?
Não. Existem bibliotecas open source como spomky-labs/otphp ou implementações simples do algoritmo RFC 6238 que podem ser integradas gratuitamente ao painel PHP existente, sem depender de serviço externo pago.