← Voltar ao blog
MySQL 09 de novembro de 2026 10 min de leitura

Como criar usuário no MySQL e dar permissões do jeito certo (8.4 e 9.x)

Como criar usuário no MySQL e dar permissões do jeito certo (8.4 e 9.x)

Para criar usuário no MySQL você usa dois comandos separados: CREATE USER cria a conta (nome, host de origem e senha) e GRANT dá as permissões. Parece simples, e é. O problema é que muita gente ainda segue tutorial de MySQL 5.x, faz tudo num GRANT ... IDENTIFIED BY, recebe erro de sintaxe e acaba resolvendo do pior jeito possível: GRANT ALL ON *.* para a aplicação. Vamos fazer direito.

Escrevo isto depois de mais de 40 anos em tecnologia, boa parte deles mexendo com MySQL, inclusive como embaixador do MySQL no Brasil. E se tem uma coisa que eu vejo repetida em ambiente de produção é a conta da aplicação com poder de DBA. Não é preguiça de ninguém. É falta de um roteiro claro. Então aqui vai o meu.

Qual é o comando para criar usuário no MySQL?

O padrão documentado no manual oficial do MySQL 8.4 é este:

CREATE USER 'app_loja'@'10.0.0.%' IDENTIFIED BY 'uma-senha-forte-aqui';
GRANT SELECT, INSERT, UPDATE, DELETE ON loja.* TO 'app_loja'@'10.0.0.%';

A seção Adding Accounts, Assigning Privileges, and Dropping Accounts do manual segue exatamente essa ordem: primeiro a conta, depois os privilégios. Desde o MySQL 8.0, o GRANT não cria mais conta implicitamente e não aceita mais IDENTIFIED BY. Se o seu script antigo faz isso, ele vai quebrar no 8.4 e no 9.x. Não é bug, é o comportamento correto.

Um detalhe que eu gosto de reforçar com quem está começando: não precisa rodar FLUSH PRIVILEGES depois de CREATE USER e GRANT. Esses comandos já atualizam as tabelas de permissão em memória. O FLUSH só faz sentido se alguém mexeu direto nas tabelas do schema mysql, coisa que você não deveria fazer.

O que significa o 'usuario'@'host'?

No MySQL, uma conta não é só um nome: é o par usuário + host de origem. 'app_loja'@'localhost' e 'app_loja'@'10.0.0.%' são duas contas diferentes, com senhas e permissões que podem ser diferentes.

  • 'localhost': só conexões da própria máquina.
  • '10.0.0.%': qualquer IP da faixa 10.0.0.x. O % funciona como curinga.
  • '%': de qualquer lugar. Use com muita parcimônia.

Se você omitir o host, o manual do CREATE USER diz que ele assume '%'. Minha opinião: sempre escreva o host, mesmo quando for %. Deixa explícito que alguém pensou nisso.

Quais permissões dar para a aplicação?

Aqui está a parte que mais importa, e é onde eu vejo o maior estrago. O GRANT trabalha em níveis:

Nível Sintaxe Quando usar
Global ON *.* Contas de administração e ferramentas de monitoramento
Banco ON loja.* O caso normal de uma aplicação
Tabela ON loja.pedidos Integrações e relatórios que leem pouca coisa
Rotina ON PROCEDURE loja.fechar_caixa Quando você expõe só procedures

A regra que eu uso, de DBA para dev: a conta que a aplicação usa em produção precisa de SELECT, INSERT, UPDATE e DELETE no banco dela. E só. Ela não precisa de DROP, não precisa de ALTER, e definitivamente não precisa de nada em *.*.

"Mas o ORM roda migration na subida da aplicação." Então crie uma segunda conta, de migração, com CREATE, ALTER, INDEX, DROP e REFERENCES no banco, usada só no pipeline de deploy. Uma senha a mais para gerenciar custa muito menos do que um DROP TABLE vindo de uma falha de SQL injection na conta da aplicação.

Exemplos práticos:

-- conta de leitura para BI/relatórios
CREATE USER 'bi_leitura'@'10.0.5.%' IDENTIFIED BY 'outra-senha-forte';
GRANT SELECT ON loja.* TO 'bi_leitura'@'10.0.5.%';

-- conta de migração, usada só no deploy
CREATE USER 'migracao_loja'@'10.0.0.%' IDENTIFIED BY 'mais-uma-senha';
GRANT CREATE, ALTER, INDEX, DROP, REFERENCES, SELECT, INSERT, UPDATE, DELETE
  ON loja.* TO 'migracao_loja'@'10.0.0.%';

Um ponto que pega muita gente: GRANT ALL não inclui GRANT OPTION. Isso está no manual. Então, se você vir WITH GRANT OPTION numa conta de aplicação, alguém colocou de propósito, e a conta pode distribuir os próprios privilégios para outras. Retire.

Como conferir e remover permissões?

Para ver o que uma conta pode fazer:

SHOW GRANTS FOR 'app_loja'@'10.0.0.%';

Para tirar uma permissão:

REVOKE DELETE ON loja.* FROM 'app_loja'@'10.0.0.%';

E para apagar a conta inteira:

DROP USER 'app_loja'@'10.0.0.%';

Se a ideia é só suspender o acesso (alguém saiu da equipe, um fornecedor encerrou o contrato), prefiro ALTER USER ... ACCOUNT LOCK. A conta continua lá, com o histórico de permissões, mas ninguém entra. Revisar periodicamente quem tem acesso a quê é um dos itens que eu olho primeiro num health check de MySQL.

Roles: vale a pena usar no MySQL?

Vale, e pouca gente usa. Desde o 8.0 o MySQL tem roles, que são conjuntos nomeados de privilégios. Em vez de repetir o mesmo GRANT para cada pessoa da equipe, você define o perfil uma vez. O exemplo da seção Using Roles do manual é praticamente o que eu recomendo:

CREATE ROLE 'app_read', 'app_write';
GRANT SELECT ON loja.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON loja.* TO 'app_write';

CREATE USER 'ana'@'%' IDENTIFIED BY 'senha-da-ana';
GRANT 'app_read' TO 'ana'@'%';
SET DEFAULT ROLE ALL TO 'ana'@'%';

Repare no SET DEFAULT ROLE. O manual é claro: conceder uma role não a ativa automaticamente. Sem essa linha (ou sem a variável activate_all_roles_on_login ligada), o usuário conecta, tenta um SELECT e toma erro de acesso negado, e você perde meia hora achando que o GRANT não funcionou. Para ver os privilégios que vêm da role, use SHOW GRANTS FOR 'ana'@'%' USING 'app_read';.

Por que aparece erro de mysql_native_password?

Esta é provavelmente a dúvida que mais traz gente ao Google depois de uma atualização. O resumo, conferido na documentação oficial:

  • Desde o MySQL 8.0, o plugin de autenticação padrão é o caching_sha2_password.
  • No MySQL 8.4 LTS, o mysql_native_password está desabilitado por padrão. Dá para religar com mysql_native_password=ON na seção [mysqld] do arquivo de configuração. A variável default_authentication_plugin foi removida no 8.4; quem controla isso agora é authentication_policy.
  • No MySQL 9.0, segundo as notas de versão do 9.0.0, o plugin mysql_native_password foi removido do servidor, junto com a opção --mysql-native-password.

Na prática: se você criou usuários num 5.7 ou num 8.0 antigo com mysql_native_password e subiu para o 8.4, essas contas param de autenticar até você religar o plugin ou migrar as contas. No 9.x não há o que religar. A saída definitiva é migrar:

ALTER USER 'app_loja'@'10.0.0.%' IDENTIFIED WITH caching_sha2_password BY 'nova-senha';

E, junto com isso, conferir se o driver da sua aplicação suporta caching_sha2_password. Para achar as contas que ainda estão no plugin antigo:

SELECT user, host, plugin FROM mysql.user WHERE plugin = 'mysql_native_password';

Minha opinião, de CTO para CTO: não trate o mysql_native_password=ON como solução. Ele é uma ponte para você atravessar o upgrade, não um lugar para morar. Planeje a troca das contas antes de pensar em 9.x.

Que cuidados de segurança colocar já na criação?

O CREATE USER aceita várias opções que quase ninguém usa e que resolvem problemas reais. Todas estão no manual do 8.4:

CREATE USER 'fornecedor_x'@'203.0.113.10'
  IDENTIFIED BY 'senha-temporaria'
  REQUIRE SSL
  PASSWORD EXPIRE INTERVAL 90 DAY
  FAILED_LOGIN_ATTEMPTS 4 PASSWORD_LOCK_TIME 2;
  • REQUIRE SSL: só aceita conexão criptografada. Para conta que vem de fora da sua rede, eu considero obrigatório.
  • PASSWORD EXPIRE INTERVAL 90 DAY: força troca de senha periódica para contas de pessoas. Para conta de aplicação, pense bem antes, porque senha expirada às três da manhã derruba o sistema.
  • FAILED_LOGIN_ATTEMPTS com PASSWORD_LOCK_TIME: bloqueia temporariamente a conta depois de tentativas erradas seguidas. Os dois valores precisam ser diferentes de zero para funcionar.
  • IDENTIFIED BY RANDOM PASSWORD: o próprio servidor gera a senha e a devolve uma única vez. Ótimo para automação.
  • CREATE USER IF NOT EXISTS: evita erro em scripts que rodam mais de uma vez.

Meu roteiro resumido

Se eu tivesse que caber tudo num post-it:

  1. Uma conta por aplicação, por ambiente. Nada de reaproveitar a mesma credencial em homologação e produção.
  2. Host restrito à faixa de rede de onde a aplicação realmente conecta.
  3. Só SELECT, INSERT, UPDATE, DELETE no banco da aplicação. Migração em conta separada.
  4. Pessoas recebem roles, não GRANT avulso.
  5. root não sai do localhost e não vai para arquivo de configuração de aplicação.
  6. Contas antigas em mysql_native_password entram no plano de upgrade.
  7. Revisão de SHOW GRANTS pelo menos a cada trimestre.

Nada disso é sofisticado. É disciplina. E é exatamente o tipo de coisa que aparece num assessment de MySQL quando alguém finalmente olha com calma para o ambiente. Se você quer ir além, tenho mais conteúdo sobre o banco na página MySQL.

Perguntas frequentes

Preciso rodar FLUSH PRIVILEGES depois do GRANT?

Não. CREATE USER, GRANT, REVOKE e DROP USER já atualizam as permissões em memória. FLUSH PRIVILEGES só é necessário se alguém alterou as tabelas do schema mysql diretamente com INSERT ou UPDATE.

Por que o GRANT ... IDENTIFIED BY dá erro de sintaxe?

Porque essa forma foi removida no MySQL 8.0. Hoje a senha é definida no CREATE USER ou no ALTER USER, e o GRANT cuida apenas dos privilégios.

Como liberar acesso remoto a um usuário do MySQL?

Crie a conta com o host de onde a conexão vai partir, por exemplo 'app'@'10.0.0.%', e confira se o servidor está escutando na interface de rede (variável bind_address) e se o firewall libera a porta 3306. Evite '%' sempre que conseguir.

Como ver todos os usuários do MySQL?

Consulte SELECT user, host, plugin FROM mysql.user;. A coluna plugin já mostra quais contas ainda usam mysql_native_password.

Roles funcionam no MariaDB do mesmo jeito?

O MariaDB também tem roles, mas a sintaxe e o comportamento de ativação não são idênticos aos do MySQL. Consulte a documentação do MariaDB antes de reaproveitar scripts.

Quer dominar o MySQL de verdade?

Criar usuário e dar permissão é a porta de entrada. Segurança, desempenho, backup e alta disponibilidade vêm logo atrás. Escrevi o livro Aprendendo MySQL exatamente para quem quer entender o banco por dentro, sem decoreba. E, se você prefere aprender com laboratório e alguém tirando dúvida, conheça o curso MySQL: prática, teoria e laboratórios e os demais treinamentos. Se a dúvida é do seu ambiente específico, fale comigo pela página de contato.