Programação Por Equipe DevBras

Capa do artigo: Mostrar o nome de quem está logado em PHP sem confiar no navegador

Mostrar o nome de quem está logado em PHP sem confiar no navegador

Guarde só o id da conta na sessão, leia o nome no banco e escape na saída. Assim a barra "Olá, Ana" não vira "Olá, administrador" com um cookie editado.

Segunda de manhã, o painel da oficina mostra "Olá, Ana" no topo. Um estagiário abre as ferramentas do navegador, troca o cookie usuario_nome para "admin" e a barra passa a dizer "Olá, admin". Pior: o menu de administração aparece, porque o mesmo código que escreve o nome decide o que mostrar. A resposta curta: no login, o servidor guarda só o id da conta na sessão; o nome é lido do banco com esse id e escapado antes de ir para o HTML. Nada que identifica a pessoa vem de cookie solto, campo oculto ou parâmetro na URL.

Este guia monta isso em PHP puro com PDO, do formulário de login até o botão de sair, usando um sistema de exemplo: o "Painel da Oficina", com uma tabela usuarios que tem id, nome, email, senha_hash e ativo.

Por que o cookie com o nome mente

Tudo que está no navegador pertence ao navegador. Cookie, localStorage, campo hidden e query string podem ser lidos e reescritos por quem está usando a máquina. Se a página lê $_COOKIE['usuario_nome'] e imprime, ela mostra o que o visitante quiser. Se usa esse valor para decidir permissão, entrega a permissão também.

A sessão do PHP funciona de outro jeito. O navegador recebe apenas um identificador aleatório (o cookie PHPSESSID, por padrão). Os dados ficam no servidor, num arquivo ou num Redis, e só o código do servidor escreve neles. O visitante pode apagar ou trocar o identificador, mas não consegue inventar o conteúdo de uma sessão que não existe. É por isso que a sessão é a fonte de verdade sobre quem está logado, e o cookie é só a chave do armário.

Como fazer o login guardar só o necessário

  1. Inicie a sessão com cookie protegido: HttpOnly, Secure e SameSite.
  2. Busque a conta pelo e-mail com consulta parametrizada e confira a senha com password_verify.
  3. Se a senha bateu, troque o id da sessão com session_regenerate_id(true), para que um id plantado antes do login não continue valendo.
  4. Guarde na sessão apenas o id numérico da conta.
  5. Redirecione para o painel e encerre o script com exit.
<?php
// bootstrap.php
session_start([
    'cookie_httponly' => true,
    'cookie_secure'   => true,
    'cookie_samesite' => 'Lax',
]);

$pdo = new PDO('mysql:host=localhost;dbname=oficina;charset=utf8mb4', 'oficina_app', getenv('DB_SENHA'), [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

function e(string $texto): string
{
    return htmlspecialchars($texto, ENT_QUOTES, 'UTF-8');
}
<?php
// entrar.php (trecho que trata o POST)
require __DIR__ . '/bootstrap.php';

$email = trim((string) ($_POST['email'] ?? ''));
$senha = (string) ($_POST['senha'] ?? '');

$stmt = $pdo->prepare('SELECT id, senha_hash FROM usuarios WHERE email = ? AND ativo = 1');
$stmt->execute([$email]);
$conta = $stmt->fetch(PDO::FETCH_ASSOC);

if ($conta && password_verify($senha, $conta['senha_hash'])) {
    session_regenerate_id(true);
    $_SESSION['usuario_id'] = (int) $conta['id'];
    header('Location: /painel.php');
    exit;
}

$erro = 'E-mail ou senha incorretos.';

Repare no que não entrou na sessão: nem o nome, nem o perfil, nem o e-mail. O id basta para buscar o resto. A mensagem de erro é a mesma para e-mail inexistente e senha errada, para não ensinar a quem testa quais contas existem. Se a sua tabela ainda guarda senha sem hash, resolva isso antes de qualquer outra coisa deste guia; o passo a passo está em login em PHP e MySQL com senha protegida.

Como mostrar o nome na barra do topo

Crie uma função que devolve a conta logada ou null. Ela lê o id da sessão, consulta o banco e confere se a conta continua ativa. Assim, se alguém desativar a Ana no meio do expediente, a próxima página que ela abrir já não a reconhece.

<?php
// conta.php
function contaLogada(PDO $pdo): ?array
{
    if (empty($_SESSION['usuario_id'])) {
        return null;
    }
    $stmt = $pdo->prepare('SELECT id, nome, perfil FROM usuarios WHERE id = ? AND ativo = 1');
    $stmt->execute([$_SESSION['usuario_id']]);
    return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
<?php $conta = contaLogada($pdo); ?>
<?php if ($conta): ?>
  <nav>
    Olá, <?= e($conta['nome']) ?>
    <?php if ($conta['perfil'] === 'admin'): ?>
      <a href="/admin/">Administração</a>
    <?php endif; ?>
    <a href="/sair.php">Sair</a>
  </nav>
<?php else: ?>
  <a href="/entrar.php">Entrar</a>
<?php endif; ?>

Três detalhes fazem diferença aqui. Primeiro, o e() em volta do nome: se alguém se cadastrar como <script>alert(1)</script>, a barra mostra o texto em vez de executá-lo. Segundo, o link de administração nem é gerado para quem não é admin. Esconder com CSS deixaria o HTML do painel visível no código-fonte. Terceiro, o perfil também vem do banco, não da sessão nem de um campo do formulário.

Ler o banco a cada página ou guardar o nome na sessão

Uma consulta por id na chave primária custa quase nada, e é a opção mais simples de manter correta. Se o painel tiver tráfego alto e você quiser evitá-la, guarde o nome na sessão logo depois de lê-lo do banco, nunca a partir do que o usuário digitou. O preço é atualizar a sessão quando a pessoa editar o próprio perfil e aceitar que uma desativação só vale no próximo login. Para permissão, prefira sempre a leitura no banco.

Como proteger as páginas da área logada

Toda página do painel começa com a mesma verificação. Sem conta, redireciona para o login e para. Sem o exit, o PHP continua executando depois do header e o HTML do painel vai junto na resposta, mesmo que o navegador siga o redirecionamento.

<?php
// painel.php
require __DIR__ . '/bootstrap.php';
require __DIR__ . '/conta.php';

$conta = contaLogada($pdo);
if (!$conta) {
    header('Location: /entrar.php');
    exit;
}

Como sair de verdade

Sair não é apagar o nome da tela. É destruir a sessão no servidor e expirar o cookie no navegador. Se você só redirecionar para a home, o botão "voltar" e um F5 ainda trazem o painel.

<?php
// sair.php
require __DIR__ . '/bootstrap.php';

$_SESSION = [];
$p = session_get_cookie_params();
setcookie(session_name(), '', [
    'expires'  => time() - 3600,
    'path'     => $p['path'],
    'domain'   => $p['domain'],
    'secure'   => $p['secure'],
    'httponly' => $p['httponly'],
    'samesite' => $p['samesite'],
]);
session_destroy();

header('Location: /entrar.php');
exit;

Isso encerra a sessão do aparelho atual. "Sair de todos os aparelhos" exige outra peça, um registro das sessões por conta, e está em sair de todos os aparelhos invalidando a sessão no servidor. Os atributos do cookie de sessão estão detalhados em cookie de sessão com HttpOnly, Secure e SameSite.

Erros comuns

  • Gravar o nome ou o perfil num cookie comum. Qualquer pessoa edita o valor e passa a ser "admin" na tela e, se o código usar esse valor para permissão, no sistema.
  • Imprimir o nome sem htmlspecialchars. Um cadastro com HTML no nome executa script no navegador de todo mundo que vê a lista de usuários.
  • Esquecer o exit depois do redirecionamento. A página protegida é gerada inteira e enviada; quem usa curl lê tudo.
  • Não regenerar o id da sessão no login. Um id que o atacante conseguiu plantar antes passa a pertencer à conta autenticada.
  • Esconder o menu de admin com CSS. O link continua no HTML e a página de destino, se não verificar o perfil por conta própria, abre para qualquer um.
  • Copiar um tutorial antigo de fórum. Muitos usam mysql_query, senha em MD5 e nome na URL; os motivos estão em por que tutorial de login antigo não serve de base.

Checklist

  • A sessão guarda só o id da conta, e ele é gravado depois de password_verify dar certo.
  • session_regenerate_id(true) roda logo após o login.
  • O cookie de sessão sai com HttpOnly, Secure e SameSite.
  • Nome e perfil vêm do banco, consultados pelo id da sessão.
  • Todo texto do usuário passa por htmlspecialchars com ENT_QUOTES e UTF-8.
  • Cada página protegida verifica a conta e termina com exit após o redirecionamento.
  • O botão de sair destrói a sessão e expira o cookie.
  • Uma conta desativada deixa de entrar na próxima página carregada.

Faça o teste agora no seu sistema: entre com uma conta, abra as ferramentas do navegador e procure, entre os cookies do site, qualquer um que tenha o nome, o e-mail ou o perfil escrito em texto legível. Se encontrar, qual página do seu código está lendo esse valor em vez da sessão?

Avalie este artigo

Referência: http://forum.imasters.com.br/topic/466064-exibir-nome-do-usuario-logado/