DBA Junior Ciberecus MX
Temas · Capítulo 3 · PF-018

Tema 18: Usuarios, roles y permisos — el gobierno del altépetl

#PF-018 | XP: 260 | Insignia: Calpixqui de Permisos | Tier: Senior | Tiempo: 3.5 h

Visión mexica — calpixqui repartiendo tributos según el rango de cada calpulli

Objetivo

Dominarás la gestión de usuarios, roles y permisos en MySQL y PostgreSQL. Aplicarás el principio de menor privilegio, usarás roles para agrupar permisos, configurarás autenticación segura con hashes fuertes, y aprenderás a auditar quién hizo qué.

Mapa del tema

    Principio de menor privilegio Usuarios en MySQL: `CREATE USER`, `GRANT`, `REVOKE` Usuarios y roles en PostgreSQL: `CREATE ROLE`, `GRANT` Roles vs usuarios: cuándo agrupar Autenticación con hashes seguros (caching_sha2, scram-sha-256) LAB: roles para app, reportes, backup y DBA

Visión mexica — Por Citlalli

En el sistema tributario mexica, el calpixqui (recaudador) no entregaba tributos al templo mayor sin antes verificar tres veces el registro: una contra el padrón del calpulli, otra contra la lista del tlatoani, y la tercera contra los libros del calmécac. Cada persona con acceso a los tributos tenía un permiso específico: los macehuales entregaban, los pipiltin verificaban, los calpixqui transportaban, los sacerdotes archivaban. Nadie hacía todo.

Esa es la esencia del principio de menor privilegio en bases de datos modernas: cada usuario tiene solo los permisos que necesita para su función, ni uno más. La app web no necesita DROP TABLE. El usuario de reportes no necesita DELETE. El de respaldo no necesita INSERT. Y así.

Un DBA que da `GRANT ALL` a todos los usuarios está renunciando a su trabajo: cualquier fuga, error o ataque se magnifica porque cada cuenta tiene poder ilimitado.


Bloque 1 — Principio de menor privilegio (PoLP)

El principio de menor privilegio dice: cada usuario, programa o proceso tiene solo los permisos estrictamente necesarios para realizar su tarea, y nada más.

Aplicado a bases de datos significa:

  • La aplicación web: `SELECT, INSERT, UPDATE, DELETE` en tablas específicas. Nunca `DROP`, `CREATE`, `ALTER`, ni acceso a `mysql.` o `pg_catalog`.
  • El usuario de reportes: `SELECT` en todo. Nunca escritura.
  • El usuario de respaldo: `SELECT, LOCK TABLES, RELOAD, SHOW VIEW, EVENT, TRIGGER`. Nunca escritura directa.
  • El DBA: todo. Pero con cuenta personal, no compartida, y con 2FA.
Beneficios del PoLP:

    Reduce la superficie de ataque: si la app es comprometida, el atacante no puede borrar tablas. Limita errores humanos: el usuario de reportes no puede borrar accidentalmente. Facilita la auditoría: sabes exactamente qué puede hacer cada cuenta. Cumple normativas: PCI-DSS, HIPAA, ISO 27001, LFPDPPP mexicana lo requieren.

Bloque 2 — Usuarios en MySQL: `CREATE USER`, `GRANT`, `REVOKE`

Crear usuario dedicado para la app

-- Crear usuario con autenticación fuerte
CREATE USER 'app_tienda'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'ClaveFuerte_2026!';

-- Otorgar permisos mínimos sobre la base tienda_tlalli GRANT SELECT, INSERT, UPDATE, DELETE ON tienda_tlalli. TO 'app_tienda'@'localhost';

-- Aplicar cambios FLUSH PRIVILEGES;

Verificar permisos otorgados

SHOW GRANTS FOR 'app_tienda'@'localhost';
-- Esperado:
-- GRANT USAGE ON . TO `app_tienda`@`localhost`
-- GRANT SELECT, INSERT, UPDATE, DELETE ON `tienda_tlalli`. TO `app_tienda`@`localhost`

Permisos comunes en MySQL

| Privilegio | Uso típico | Riesgo si se abusa | |---|---|---| | `SELECT` | Lectura de datos | Bajo. Permite exfiltrar datos si la tabla es sensible. | | `INSERT` | Insertar filas | Medio. Permite inyectar datos falsos. | | `UPDATE` | Modificar filas | Medio. Permite modificar precios, contraseñas, etc. | | `DELETE` | Borrar filas | Alto. Un `DELETE` sin WHERE puede tirar la tabla. | | `DROP` | Borrar objetos | Crítico. Borra tablas, bases, índices. | | `CREATE` | Crear objetos | Alto. Puede crear bases o tablas no autorizadas. | | `ALTER` | Modificar estructura | Alto. Puede cambiar tipos, romper FK. | | `GRANT OPTION` | Otorgar permisos | Crítico. Permite escalar privilegios. | | `FILE` | Leer/escribir archivos del SO | Crítico. Permite leer `/etc/passwd` o escribir archivos arbitrarios. | | `SUPER` | Operaciones de admin | Crítico. Bypasa muchas restricciones. |

Ejemplo de usuario de reportes (solo lectura)

CREATE USER 'reportes'@'%' IDENTIFIED WITH caching_sha2_password BY 'Reportes2026!';
GRANT SELECT ON tienda_tlalli. TO 'reportes'@'%';

Ejemplo de usuario de backup (Tema 16)

CREATE USER 'backup_user'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'Backup2026!';
GRANT SELECT, LOCK TABLES, RELOAD, SHOW VIEW, EVENT, TRIGGER ON tienda_tlalli. TO 'backup_user'@'localhost';

Revocar permisos

REVOKE DELETE ON tienda_tlalli. FROM 'app_tienda'@'localhost';
FLUSH PRIVILEGES;

Cambiar contraseña

ALTER USER 'app_tienda'@'localhost' IDENTIFIED BY 'NuevaClave_2026!';

Eliminar usuario

DROP USER 'app_tienda'@'localhost';

Bloque 3 — Usuarios y roles en PostgreSQL: `CREATE ROLE`, `GRANT`

PostgreSQL unifica los conceptos de usuario y rol: un rol con `LOGIN` puede conectarse, sin `LOGIN` es un grupo.

Crear roles (grupos de permisos)

-- Rol para la aplicación
CREATE ROLE app_tienda WITH LOGIN PASSWORD 'ClaveFuerte_2026!';

-- Rol de lectura para reportes CREATE ROLE reportes WITH LOGIN PASSWORD 'Reportes2026!';

-- Rol de backup CREATE ROLE backup_user WITH LOGIN PASSWORD 'Backup2026!';

-- Rol de DBA CREATE ROLE dba WITH LOGIN PASSWORD 'Dba2026!' SUPERUSER;

Otorgar permisos a un rol

-- Conexión a la base
GRANT CONNECT ON DATABASE tienda_tlalli TO app_tienda, reportes, backup_user;

-- Uso del esquema GRANT USAGE ON SCHEMA public TO app_tienda, reportes, backup_user;

-- Permisos por tabla GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_tienda; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_tienda; GRANT SELECT ON ALL TABLES IN SCHEMA public TO reportes; GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;

Hacer que los permisos apliquen a tablas futuras

-- Para que las nuevas tablas creadas por el dba hereden estos permisos
ALTER DEFAULT PRIVILEGES FOR ROLE dba IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_tienda;
ALTER DEFAULT PRIVILEGES FOR ROLE dba IN SCHEMA public
  GRANT SELECT ON TABLES TO reportes;

Roles agrupadores (jerarquía)

PostgreSQL permite que un rol herede de otros:

-- Crear un rol de "lector"
CREATE ROLE rol_lector;

-- Otorgar permisos de lectura al rol GRANT SELECT ON ALL TABLES IN SCHEMA public TO rol_lector;

-- Hacer que "reportes" herede de "rol_lector" GRANT rol_lector TO reportes;

Esto evita repetir permisos. Si mañana agregas una tabla, solo modificas `rol_lector` y todos los que lo heredan la reciben.

Cambiar contraseña

ALTER ROLE app_tienda WITH PASSWORD 'NuevaClave_2026!';

Revocar permisos

REVOKE DELETE ON ALL TABLES IN SCHEMA public FROM app_tienda;

Eliminar rol

DROP ROLE reportes;

Bloque 4 — Roles vs usuarios: cuándo agrupar

| Caso | MySQL | PostgreSQL | |---|---|---| | Un usuario que necesita permisos únicos | `CREATE USER` | `CREATE ROLE ... LOGIN` | | 3 usuarios que necesitan los mismos permisos | No hay roles nativos pre-8.0; simular con grants repetidos o usar proxy users | `CREATE ROLE` sin login, `GRANT rol TO usuario` | | 30 usuarios con permisos en constante cambio | Crear roles MySQL 8 (`CREATE ROLE 'lectura_app'`) y asignar a usuarios | Roles jerárquicos | | Permisos por tabla/columna | `GRANT SELECT (columna) ON tabla TO usuario` | `GRANT SELECT (columna) ON tabla TO rol` | | Permisos por fila (RLS) | No nativo, usar vistas o app | `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` |

Regla práctica: si tienes más de 5 usuarios con permisos similares, usa roles. Si tienes menos, los grants directos están bien.


Bloque 5 — Autenticación con hashes seguros

MySQL: `caching_sha2_password` (default desde 8.0)

`caching_sha2_password` es más seguro que el viejo `mysql_native_password`:

  • Hash SHA-256 + sal aleatorio + iteraciones.
  • Cachea en el servidor para evitar handshake pesado en cada conexión.
  • Compatible con todos los drivers modernos.
Configuración del servidor:

[mysqld]
default_authentication_plugin = caching_sha2_password

PostgreSQL: `scram-sha-256` (recomendado)

PostgreSQL 10+ soporta SCRAM-SHA-256, que es el estándar moderno:

-- Configurar como método por defecto en pg_hba.conf
-- Reemplazar "md5" o "password" por "scram-sha-256"
-- Ejemplo:
-- host  all  all  0.0.0.0/0  scram-sha-256
# postgresql.conf
password_encryption = scram-sha-256

Para verificar que una contraseña usa SCRAM:

SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_tienda';
-- Esperado: SCRAM-SHA-256$4096:...

pg_hba.conf: control de acceso por host

`pg_hba.conf` (Host-Based Authentication) controla quién puede conectarse desde dónde:

# TYPE  DATABASE       USER            ADDRESS         METHOD
local   all            postgres                        peer
host    tienda_tlalli  app_tienda      127.0.0.1/32   scram-sha-256
host    tienda_tlalli  reportes        10.0.0.0/8     scram-sha-256
host    all            all             0.0.0.0/0      reject

Orden importa: PostgreSQL aplica la primera regla que coincida. Pon lo más específico arriba.

Después de modificar `pg_hba.conf`, recarga sin reiniciar:

pg_ctl reload -D /var/lib/postgresql/16/main

AI Mission

Pídele a tu IA que audite tu política de usuarios. Prompt sugerido:

Tengo los siguientes usuarios en tienda_tlalli (MySQL 8):
    root (uso solo para admin) app_tienda (usa la app web) backup_user (corre mysqldump diario) jose (mi usuario personal de DBA)
Dame:
    ¿Cuántos huecos de seguridad tiene esta lista? ¿Qué permisos exactos debería tener cada uno? ¿Cómo aplicaría el principio de menor privilegio? ¿Qué añadirías (auditoría, expiración de contraseñas, etc.)?
Aplica los cambios que la IA sugiera, pero verifica cada GRANT que recomiende. La IA a veces recomienda `GRANT ALL` por simplicidad.


Errores típicos

    Usar `root` desde la aplicación → si la app es comprometida, el atacante tiene control total. GRANT ALL por pereza → convierte cualquier usuario en un mini-DBA. Permitir conexiones desde cualquier host (`%`) sin restricción por IP. No revocar usuarios de exempleados o proyectos cancelados → cuentas zombi con acceso. Contraseñas en texto plano en archivos de configuración → visibles en repos, logs, backups. Confiar en `caching_sha2_password` pero configurar `mysql_native_password` → degradación silenciosa.

LAB práctico — Roles y permisos para tienda_tlalli

Objetivo: dejar tienda_tlalli con cuatro cuentas bien separadas: app, reportes, backup, DBA. Cada una con sus permisos exactos.

Paso 1 — Crear los roles

-- App
CREATE USER 'app_tienda'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'AppTienda_2026!';
GRANT SELECT, INSERT, UPDATE, DELETE ON tienda_tlalli. TO 'app_tienda'@'localhost';

-- Reportes CREATE USER 'reportes'@'%' IDENTIFIED WITH caching_sha2_password BY 'Reportes_2026!'; GRANT SELECT ON tienda_tlalli. TO 'reportes'@'%';

-- Backup CREATE USER 'backup_user'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'Backup_2026!'; GRANT SELECT, LOCK TABLES, RELOAD, SHOW VIEW, EVENT, TRIGGER ON tienda_tlalli. TO 'backup_user'@'localhost';

-- DBA personal (tú) CREATE USER 'jose'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'DbaJose_2026!'; GRANT ALL PRIVILEGES ON tienda_tlalli. TO 'jose'@'localhost' WITH GRANT OPTION;

Paso 2 — Verificar

SHOW GRANTS FOR 'app_tienda'@'localhost';
SHOW GRANTS FOR 'reportes'@'%';
SHOW GRANTS FOR 'backup_user'@'localhost';
SHOW GRANTS FOR 'jose'@'localhost';

Paso 3 — Probar que el principio de menor privilegio se cumple

# Conectarse como app_tienda e intentar hacer DROP
mysql -u app_tienda -p tienda_tlalli
mysql> DROP TABLE clientes;
-- Esperado: ERROR 1142 (42000): DROP command denied to user 'app_tienda'@'localhost'

mysql> SELECT COUNT(*) FROM clientes; -- Esperado: 50 (sí puede leer)

Paso 4 — Configurar pg_hba.conf en PostgreSQL

# Editar pg_hba.conf
sudo nano /etc/postgresql/16/main/pg_hba.conf

Agregar al inicio:

# Conexiones locales para la app
local   tienda_tlalli   app_tienda                       scram-sha-256
host    tienda_tlalli   app_tienda   127.0.0.1/32        scram-sha-256

Reportes desde la red interna

host tienda_tlalli reportes 10.0.0.0/8 scram-sha-256

Backup desde localhost

host tienda_tlalli backup_user 127.0.0.1/32 scram-sha-256

DBA desde tu IP

host tienda_tlalli jose 192.168.1.50/32 scram-sha-256

Bloquear todo lo demás

host all all 0.0.0.0/0 reject
sudo pg_ctlcluster 16 main reload

Entregables del LAB

  • [ ] Cuatro usuarios creados con permisos diferenciados.
  • [ ] Verificación de que `app_tienda` no puede DROP.
  • [ ] `pg_hba.conf` configurado con reglas específicas por rol.
  • [ ] Captura de un intento de DROP denegado.
  • [ ] Cambio de contraseña personal (DBA) en una variable de entorno, no en texto plano.

Quest

Preguntas de opción múltiple (10)

1. ¿Qué es el principio de menor privilegio?

A. Otorgar todos los permisos a todos los usuarios. B. Otorgar a cada usuario solo los permisos estrictamente necesarios. C. Crear usuarios sin contraseña. D. Usar solo el usuario root.

2. ¿Qué privilegio MySQL permite leer y escribir archivos del sistema operativo?

A. `SELECT` B. `FILE` C. `PROCESS` D. `SUPER`

3. ¿Qué hace `REVOKE`?

A. Crea un usuario. B. Quita permisos otorgados previamente. C. Otorga permisos. D. Cambia la contraseña.

4. ¿Qué método de autenticación es el más seguro en PostgreSQL 10+?

A. `password` B. `md5` C. `scram-sha-256` D. `trust`

5. ¿Qué archivo controla desde qué hosts se puede conectar un usuario PostgreSQL?

A. `postgresql.conf` B. `pg_hba.conf` C. `pg_ident.conf` D. `recovery.conf`

6. ¿Qué hace `ALTER DEFAULT PRIVILEGES` en PostgreSQL?

A. Cambia los permisos de todas las tablas existentes. B. Define permisos que se aplicarán a futuras tablas. C. Borra una tabla. D. Crea un nuevo esquema.

7. ¿Cuál es la diferencia entre `CREATE USER` y `CREATE ROLE ... WITH LOGIN` en PostgreSQL?

A. No hay diferencia: ambos crean un usuario conectable. B. `CREATE USER` es solo para administradores. C. `CREATE ROLE` no permite conexión. D. `CREATE USER` requiere superusuario.

8. ¿Por qué es peligroso usar `root` desde la aplicación?

A. No es peligroso. B. Si la app es comprometida, el atacante tiene control total de la base. C. Es muy lento. D. No permite conexiones remotas.

9. ¿Qué hace `GRANT OPTION`?

A. Otorga el permiso de dar permisos a otros. B. Optimiza las queries. C. Cambia el motor de almacenamiento. D. Activa la compresión.

10. ¿Qué vista MySQL permite ver los permisos otorgados a un usuario?

A. `information_schema.USER_PRIVILEGES` B. `mysql.user` C. `performance_schema.users` D. `sys.user_grants`

Preguntas abiertas (3)

A. Una aplicación usa el usuario `root` para conectarse a MySQL porque "así es más fácil". Lista 5 problemas concretos de seguridad de esta decisión y propón una migración en 3 pasos.

B. Tienes 12 usuarios que necesitan solo `SELECT` sobre `tienda_tlalli` pero algunos también `INSERT` en `pedidos`. Diseña la estructura de roles en PostgreSQL para no repetir permisos.

C. El equipo de desarrollo te pide acceso a `tienda_tlalli` desde sus laptops. ¿Qué información necesitas antes de concederlo? ¿Qué condiciones pondrías?


Respuestas modelo

1. B — Cada usuario solo los permisos necesarios. La A es lo contrario del principio. La C y D son inseguras.

2. B — `FILE` permite `LOAD_FILE()` y `SELECT ... INTO OUTFILE`. Crítico si se abusa. `SUPER` permite otras cosas peligrosas pero no acceso a archivos.

3. B — `REVOKE` quita permisos. `GRANT` los otorga. `CREATE USER` crea cuenta. Cambiar contraseña es `ALTER USER ... IDENTIFIED BY`.

4. C — `scram-sha-256` es el estándar moderno de PostgreSQL. `password` envía en claro. `md5` es mejor que `password` pero vulnerable a replay. `trust` no pide contraseña (solo para desarrollo local).

5. B — `pg_hba.conf` (Host-Based Authentication). `postgresql.conf` es configuración general. `pg_ident.conf` mapea usuarios del SO. `recovery.conf` es para PITR (ya no se usa desde PG 12).

6. B — Aplica permisos a tablas futuras, no a las existentes. Para las existentes usas `GRANT` directo. No borra ni crea.

7. A — Desde PostgreSQL 8.1, no hay diferencia práctica: `CREATE USER` es alias de `CREATE ROLE ... LOGIN`. Ambos crean un rol conectable.

8. B — Si la app es comprometida (SQL injection, XSS, dependencia vulnerable), el atacante tiene control total de la base. La A es falsa. La C es irrelevante. La D es falsa.

9. A — `GRANT OPTION` permite al usuario receptor dar esos permisos a otros. Útil para cadenas de delegación, peligroso si se abusa.

10. B — `mysql.user` tiene los privilegios globales y por tabla. `USER_PRIVILEGES` también pero menos detallado. `performance_schema` no tiene esto. `sys` no es para esto.


Tras las huellas — Por Citlalli

Los mexicas no solo recolectaban tributos: los rendían cuentas. Cada calpixqui debía reportar al calpixqui mayor qué recibió, de qué calpulli, en qué fecha, y dónde se depositó. Si los números no coincidían con el padrón, el calpixqui era sancionado. La transparencia no era cortesía, era supervivencia del sistema.

En bases de datos modernas, el equivalente es la auditoría: el log de quién hizo qué, cuándo y desde dónde. La tabla `general_log` de MySQL (en modo debug) o `pgAudit` en PostgreSQL te dan ese registro. Actívalos en producción, no solo en desarrollo.

Pregunta para el cuaderno: si mañana un auditor te pregunta "¿quién borró la tabla `pedidos` el 15 de agosto a las 14:32?", ¿podrías responder con un log, o tendrías que admitir que no lo sabes?

Cierre del cuaderno

    Diferencia entre `caching_sha2_password` y `mysql_native_password`. Permisos mínimos para un usuario de aplicación web. Estructura del archivo `pg_hba.conf` que usaría para producción. Mis usuarios actuales: ¿quién tiene más permisos de los que necesita? Próxima acción: activar `pgAudit` o `general_log` por 1 semana para auditar.