Aprende SQL

Usuarios, permisos y privilegios: CREATE USER y GRANT

Lección 04 · Motores y ecosistema

Usuarios, permisos y privilegios: CREATE USER y GRANT

Hasta ahora hemos hablado con la base de datos como si fuéramos su único usuario. Pero en la vida real, contra un mismo servidor conectan muchas personas y muchas aplicaciones: la app web, el script de analítica, el equipo de soporte, el backup nocturno… y no todos deberían poder hacer lo mismo. A esta capa de SQL se la llama DCL (Data Control Language): el lenguaje que controla quién puede entrar y qué puede hacer.

Nota: esta lección es teórica a propósito. El playground del curso usa SQLite, que no tiene usuarios ni permisos: es una base embebida, sin servidor, y quien abre el archivo es el “dueño”. CREATE USER y GRANT existen en motores cliente-servidor como MySQL y PostgreSQL. Puedes practicarlos gratis instalando MySQL con Docker, tal y como vimos en la lección anterior.

Crear un usuario con contraseña

En MySQL, los usuarios se identifican con la forma 'nombre'@'desde_dónde': no solo importa quién eres, sino desde qué equipo te conectas.

CREATE USER 'analista'@'localhost' IDENTIFIED BY 'MiContraseña#2024';
CREATE USER 'app_web'@'10.0.0.%' IDENTIFIED BY 'OtraClave!Segura1';
  • 'analista'@'localhost' solo puede conectarse desde la propia máquina del servidor.
  • 'app_web'@'10.0.0.%' puede conectarse desde cualquier equipo de la red 10.0.0.x (el % es el comodín, como en LIKE).

Para cambiar una contraseña:

ALTER USER 'analista'@'localhost' IDENTIFIED BY 'NuevaClave#2025';

Las contraseñas nunca viajan en claro. Cuando ejecutas IDENTIFIED BY '...', el motor guarda un hash, no el texto. Y cuando tu aplicación se conecta, la contraseña no debe estar escrita en el código: va en variables de entorno o en un gestor de secretos.

Asignar privilegios con GRANT

Crear el usuario solo le da permiso para conectarse. Lo que puede hacer una vez dentro se define con GRANT:

GRANT SELECT ON tienda.products TO 'analista'@'localhost';

Con esto, analista puede leer la tabla products de la base de datos tienda… y nada más. No puede insertar, ni borrar, ni tocar otras tablas.

Los privilegios se pueden dar a distintos niveles de alcance:

Sintaxis Alcance
GRANT ... ON *.* TO ... Todo el servidor (todas las bases, todas las tablas)
GRANT ... ON tienda.* TO ... Una base de datos completa
GRANT ... ON tienda.products TO ... Una tabla concreta

Y los privilegios más comunes son los que ya conoces del curso:

Privilegio Qué permite
SELECT Leer datos
INSERT Añadir filas
UPDATE Modificar filas
DELETE Eliminar filas
CREATE / DROP Crear o eliminar tablas y bases
ALTER Cambiar estructuras
ALL PRIVILEGES Todo (reservado para administradores)

Se pueden combinar varios en una sola sentencia:

GRANT SELECT, INSERT, UPDATE ON tienda.* TO 'app_web'@'10.0.0.%';

Fíjate en el patrón: la aplicación web puede escribir datos, pero el analista solo puede leerlos. Cada quien tiene lo suyo.

Quitar privilegios con REVOKE

REVOKE es la operación inversa de GRANT:

REVOKE INSERT, UPDATE ON tienda.* FROM 'app_web'@'10.0.0.%';

Y para eliminar un usuario por completo:

DROP USER 'analista'@'localhost';

Para auditar qué puede hacer un usuario:

SHOW GRANTS FOR 'analista'@'localhost';

Roles: privilegios empaquetados

Asignar privilegios uno a uno se vuelve inmanejable en cuanto tienes varios usuarios con el mismo perfil. La solución son los roles: un paquete de privilegios con nombre, que luego se asigna a los usuarios.

-- MySQL 8+ y PostgreSQL
CREATE ROLE 'lector_ventas';

GRANT SELECT ON ventas.* TO 'lector_ventas';

GRANT 'lector_ventas' TO 'analista'@'localhost';
GRANT 'lector_ventas' TO 'becario'@'localhost';

Si mañana el perfil cambia, actualizas el rol una vez y afecta a todos sus miembros. En PostgreSQL esto está integrado desde hace años (de hecho, todo es un rol allí, incluso los usuarios); en MySQL llegó con la versión 8.

Qué soporta cada motor

Capacidad MySQL PostgreSQL SQLite
Usuarios con contraseña ✅ CREATE USER ... IDENTIFIED BY ✅ CREATE USER ... PASSWORD ❌ No existen
GRANT / REVOKE ✅ ✅ ❌
Roles ✅ (desde la v8) ✅ (todo es un rol) ❌
Modelo Cliente-servidor Cliente-servidor Archivo embebido

En SQLite la seguridad vive fuera de la base de datos: en los permisos del archivo y del proceso que lo abre. Por eso el playground del curso puede ejecutar tu SQL sin cuentas ni contraseñas.

Buenas prácticas: el principio de mínimo privilegio

  1. Cada aplicación, su usuario. Nada de que la app web se conecte como root. Si la comprometen, el atacante hereda todos los privilegios.
  2. Mínimo privilegio. Dale solo lo que necesita: si una parte del código solo lee, que su usuario solo tenga SELECT.
  3. Contraseñas fuera del código. Variables de entorno o gestor de secretos, nunca password: "..." en el repositorio.
  4. Distinto usuario para desarrollo y producción. Y rota las contraseñas cuando alguien deja el equipo (o mejor: usa roles y solo reasignas).
  5. Audita de vez en cuando. SHOW GRANTS te dice en segundos si un usuario acumuló permisos que ya no necesita.

En resumen

  • La capa de seguridad de SQL se llama DCL: CREATE USER, GRANT, REVOKE, DROP USER.
  • En MySQL los usuarios son 'nombre'@'host'; la contraseña se guarda como hash, nunca en claro.
  • GRANT asigna privilegios (SELECT, INSERT, …) a niveles de servidor, base o tabla; REVOKE los quita.
  • Los roles empaquetan privilegios para asignarlos a muchos usuarios de una vez.
  • SQLite no tiene usuarios ni permisos: es embebida, y la seguridad vive fuera de la base.
  • La regla de oro es el principio de mínimo privilegio: cada quien, solo lo que necesita.