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

Como proteger o painel do seu servidor de MU Online contra ataques CSRF

Entenda como ataques CSRF podem ser usados contra o painel do seu servidor de MU Online e implemente tokens, cookies SameSite e validação de origem para bloquear ações forjadas em nome do jogador.

RO Rodrigo · Atualizado em 13 ago 2020 · ⏱ 14 min de leitura
Resposta rápida

O painel web é a porta de entrada mais exposta do seu servidor de MU Online: é ali que o jogador troca senha, resgata itens promocionais, vincula personagem à conta e, em muitos casos, movimenta Zen ou WCoin. Se esse painel não valida a origem de cada requisição, um atacante pode montar uma página f

O painel web é a porta de entrada mais exposta do seu servidor de MU Online: é ali que o jogador troca senha, resgata itens promocionais, vincula personagem à conta e, em muitos casos, movimenta Zen ou WCoin. Se esse painel não valida a origem de cada requisição, um atacante pode montar uma página fora do seu site que, aproveitando a sessão já autenticada do jogador, dispara ações em nome dele sem que ele perceba — isso é um ataque CSRF (Cross-Site Request Forgery). Este tutorial explica o mecanismo do ataque, mostra onde ele costuma aparecer em painéis de MU (baseados em PHP com MySQL, na maioria dos casos) e apresenta as três camadas de defesa que você deve implementar: token sincronizado, cookies SameSite e validação de Origin/Referer.

Por que painéis de MU são alvo fácil

A maioria dos painéis de servidores privados foi construída a partir de scripts de código aberto (CMS antigos, forks de painéis de outros servidores) que priorizam funcionalidade sobre segurança. É comum encontrar formulários de "resgatar código promocional", "trocar senha do jogo" ou "transferir Zen entre personagens" que apenas verificam se existe uma sessão PHP válida, sem nenhum token adicional. Como o cookie de sessão é enviado automaticamente pelo navegador em qualquer requisição para o domínio, mesmo uma requisição disparada por outro site carrega esse cookie — e o servidor não tem como distinguir, só pelo cookie, se o pedido veio de uma ação real do jogador ou de um site forjado.

Como o ataque acontece na prática

Um atacante cria uma página em outro domínio com um formulário oculto apontando para painel.seusite.com/trocar-email.php, com os campos já preenchidos com o e-mail dele. Ele distribui o link em um fórum de MU, Discord ou anúncio, disfarçado de "calculadora de build" ou "ranking ao vivo". Quando um jogador logado no seu painel clica no link, o formulário é enviado automaticamente via JavaScript, o navegador anexa o cookie de sessão válido, e o servidor processa a troca de e-mail como se fosse uma ação legítima. A partir daí o atacante pede "esqueci minha senha", recebe o link no e-mail dele e assume a conta.

Camada 1 — Token CSRF sincronizado

A defesa mais robusta é gerar um token único por sessão (ou por formulário) e exigi-lo em toda requisição que altera estado.

<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<form method="post" action="trocar-email.php">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    <input type="email" name="novo_email">
    <button type="submit">Salvar</button>
</form>

No processamento da ação, valide antes de qualquer efeito colateral:

<?php
session_start();
if (empty($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    http_response_code(403);
    die('Requisição inválida.');
}
// só chega aqui se o token bater — prossiga com a troca de e-mail

Use sempre hash_equals() em vez de == para evitar comparação vulnerável a timing attack. Regenere o token após cada login e, idealmente, após cada ação sensível bem-sucedida.

Camada 2 — Cookies SameSite

O atributo SameSite do cookie de sessão instrui o navegador a não enviar o cookie em requisições originadas de outro domínio, mesmo que o formulário aponte para o seu painel.

ValorComportamentoQuando usar
StrictCookie nunca enviado em navegação cross-sitePainéis sem necessidade de links externos entrando logado
LaxCookie enviado em navegação de topo (clicar em link), bloqueado em POST cross-sitePadrão recomendado para a maioria dos painéis de MU
NoneCookie sempre enviado, mesmo cross-siteEvite — exige Secure e reabre a superfície de ataque

Configure no php.ini ou via session_set_cookie_params():

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => 'seusite.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
session_start();

HttpOnly impede que o cookie seja lido por JavaScript (mitiga XSS combinado com CSRF), e Secure garante que o cookie só trafega em HTTPS.

Camada 3 — Validação de Origin e Referer

Como defesa extra, especialmente em endpoints de API/AJAX do painel, valide o cabeçalho Origin (ou Referer como fallback) e rejeite requisições vindas de domínios não autorizados.

<?php
$origem = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_REFERER'] ?? '';
$permitido = 'https://seusite.com';
if (strpos($origem, $permitido) !== 0) {
    http_response_code(403);
    die('Origem não autorizada.');
}

Essa camada não substitui o token, porque alguns proxies e extensões removem esses cabeçalhos legitimamente, mas serve como rede de segurança adicional contra automações simples.

Quais ações do painel exigem proteção prioritária

Nem toda página precisa do mesmo nível de rigor. Priorize pelo impacto de um ataque bem-sucedido:

Ação do painelRisco se exploradaPrioridade de proteção
Trocar senha do jogoPerda total da contaCrítica
Trocar e-mail cadastradoSequestro de conta via recuperação de senhaCrítica
Resgatar código promocionalUso indevido de recompensas do jogadorAlta
Transferir Zen/WCoin entre personagensPerda de moeda in-gameAlta
Vincular/desvincular personagemPerda de acesso a personagemAlta
Alterar preferências de exibição no rankingBaixo impacto financeiroBaixa

Proteção específica para o login do jogo (GameServer/AccountServer)

O ataque CSRF é um problema de navegador e afeta apenas o painel web, não o protocolo de login do cliente do jogo (que usa sockets TCP próprios, não HTTP). Ainda assim, se o painel tem uma função de "login automático no jogo" via token gerado no navegador, garanta que esse token também siga o padrão de expiração curta (poucos minutos) e uso único, para que ele não vire um vetor equivalente.

Testando a proteção com um PoC controlado

Antes de declarar o painel seguro, monte um teste de prova de conceito isolado, fora do domínio de produção:

<!-- teste-csrf.html, hospedado em outro domínio/localhost -->
<form id="f" action="https://seusite.com/trocar-email.php" method="POST">
  <input type="hidden" name="novo_email" value="[email protected]">
</form>
<script>document.getElementById('f').submit();</script>

Abra o painel real em uma aba, faça login, e em outra aba abra o teste-csrf.html. Se a troca de e-mail for aceita sem o token, a vulnerabilidade está confirmada e você deve aplicar as camadas acima antes de qualquer coisa.

Frameworks e bibliotecas que já resolvem isso

Se o painel foi migrado ou reescrito com um framework moderno (Laravel, Symfony, CodeIgniter), a proteção CSRF geralmente já vem embutida via middleware — no Laravel, por exemplo, o token é injetado automaticamente com @csrf nos formulários Blade e validado pelo middleware VerifyCsrfToken. Nesses casos, o trabalho principal é garantir que nenhuma rota sensível esteja na lista de exceções (except) do middleware, o que é um erro comum ao copiar configurações de exemplo da internet.

Logs e monitoramento de tentativas bloqueadas

Registre toda rejeição de token CSRF ou de origem inválida em um log dedicado, com IP, user-agent e timestamp. Um pico repentino de rejeições no mesmo endpoint é um indicador forte de que alguém está testando um ataque direcionado ao seu painel, e permite que você reaja (bloqueio de IP, aviso à comunidade) antes que a exploração tenha sucesso contra um jogador real.

Erros comuns e soluções

SintomaCausa provávelSolução
Token CSRF "quebra" formulários AJAXToken não incluído no cabeçalho da requisição JSEnvie o token via header customizado (X-CSRF-Token) em toda chamada fetch/AJAX
Usuários deslogados com frequênciaSameSite=Strict bloqueando redirecionamentos legítimos de pagamento externoUse SameSite=Lax para permitir navegação de topo entrando de fora
Ataque ainda funciona mesmo com tokenToken fixo por aplicação em vez de por sessãoGere token por sessão e regenere após login
Validação de Origin bloqueia usuários legítimosProxy/CDN removendo o cabeçalho OriginUse o token CSRF como defesa primária; trate Origin como camada auxiliar
Painel antigo sem sessão PHP nativaAutenticação via cookie customizado sem suporte a SameSiteMigre para session_set_cookie_params() nativo ou implemente o atributo manualmente no cookie

Checklist de proteção contra CSRF

  • Token CSRF gerado por sessão e validado com hash_equals() em toda ação que altera estado.
  • Cookies de sessão configurados com SameSite=Lax, Secure e HttpOnly.
  • Validação de Origin/Referer como camada auxiliar nos endpoints de API/AJAX.
  • Ações críticas (senha, e-mail, Zen, vínculo de personagem) mapeadas e todas protegidas.
  • Teste de PoC realizado fora do domínio de produção antes do lançamento.
  • Logs de rejeição de token/origem monitorados.
  • Rotas sensíveis conferidas manualmente na lista de exceções do framework, se aplicável.

Com o painel protegido contra CSRF, o próximo passo natural é revisar as demais camadas de segurança web do servidor — validação de entrada, rate limiting e proteção contra XSS —, pois essas defesas se reforçam mutuamente. Se você ainda está estruturando a infraestrutura do zero, vale revisar o guia completo de criação de servidor de MU Online para garantir que a base já nasça com essas práticas. </content>

Perguntas frequentes

O que exatamente é um ataque CSRF?

CSRF (Cross-Site Request Forgery) é quando um site malicioso engana o navegador do jogador para enviar uma requisição ao seu painel usando a sessão já autenticada dele, sem que ele perceba. O atacante não rouba a senha; ele abusa da confiança que o painel deposita no cookie de sessão do navegador.

Meu painel usa apenas GET para as ações, isso é perigoso?

Sim, é ainda mais perigoso. Requisições GET podem ser disparadas por uma simples tag <img> ou link em um fórum, sem nenhuma interação do jogador além de abrir a página. Ações que alteram estado (trocar senha, resgatar código, mover Zen) nunca devem usar GET.

Token CSRF resolve sozinho ou preciso de mais alguma coisa?

O token resolve a maior parte, mas deve ser combinado com cookies SameSite=Lax ou Strict e validação de cabeçalho Origin/Referer como camadas extras. Nenhuma defesa isolada é suficiente contra todas as variações do ataque.

Isso afeta o desempenho do painel?

O impacto é desprezível. Gerar e validar um token é uma operação de milissegundos comparada ao tempo de resposta típico de uma página PHP com banco de dados. A resistência que existe é quase sempre resistência a mudar código legado, não custo técnico.

Como testo se meu painel está vulnerável?

Monte um HTML simples fora do domínio do painel com um formulário apontando para uma ação sensível (ex.: trocar e-mail) e envie automaticamente via JavaScript enquanto está logado no painel em outra aba. Se a ação for executada sem erro, o painel está vulnerável.

RO
Fundador e editor-chefe

Rodrigo mantém o ViciadosMU desde os primórdios do portal. Especialista em criação e administração de servidores de MU Online, história do jogo e a evolução das seasons — escreveu boa parte do acervo antes de 2024.

Continue lendo

Artigos relacionados