Configuración de Seguridad MySQL/MariaDB: Guía Completa de Hardening
Introducción
La seguridad de las bases de datos es primordial en el panorama actual de amenazas donde las violaciones de datos y los ciberataques son cada vez más comunes. MySQL y MariaDB, aunque son sistemas de bases de datos potentes y ampliamente utilizados, requieren configuración de seguridad adecuada para proteger datos sensibles de acceso no autorizado, ataques de inyección SQL y otras amenazas de seguridad.
Una base de datos comprometida puede llevar a consecuencias catastróficas incluyendo robo de datos, pérdidas financieras, sanciones regulatorias y daño severo a la reputación. Según informes de la industria, el costo promedio de una violación de datos excede los $4 millones, haciendo de la seguridad de bases de datos no solo un requisito técnico sino un imperativo de negocio.
Esta guía completa proporciona instrucciones detalladas para endurecer instalaciones de MySQL y MariaDB, implementar estrategias de defensa en profundidad y seguir mejores prácticas de la industria para seguridad de bases de datos. Ya sea que estés gestionando un entorno de desarrollo o un sistema de producción que maneja datos sensibles de clientes, esta guía te ayudará a establecer medidas de seguridad robustas.
Amenazas de Seguridad a Sistemas de Bases de Datos
Las amenazas comunes incluyen:
- Inyección SQL: Inyección de código SQL malicioso a través de vulnerabilidades de aplicación
- Ataques de Fuerza Bruta: Intentos automatizados de adivinación de contraseñas
- Escalación de Privilegios: Explotación de vulnerabilidades para obtener acceso elevado
- Exposición de Datos: Acceso no autorizado a datos por mala configuración
- Ataques Man-in-the-Middle: Interceptación de tráfico de base de datos no cifrado
- Amenazas Internas: Acciones maliciosas o negligentes de usuarios autorizados
Requisitos Previos
Antes de proceder con la configuración de seguridad:
- MySQL o MariaDB ya instalado y ejecutándose
- Acceso root o sudo al servidor
- Contraseña root de base de datos disponible
- Comprensión básica de comandos SQL
- Backup de configuración de base de datos existente
- Comprensión de los requisitos de base de datos de tu aplicación
Creación de Backup de Configuración
# Backup de configuración MySQL
sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.backup
# Backup de configuración MariaDB
sudo cp /etc/mysql/mariadb.conf.d/50-server.cnf /etc/mysql/mariadb.conf.d/50-server.cnf.backup
# Backup de todos los directorios MySQL
sudo cp -r /etc/mysql /etc/mysql.backup.$(date +%Y%m%d)
# Backup de bases de datos antes de cambios de seguridad
sudo mysqldump --all-databases > /backup/all_databases_pre_security_$(date +%Y%m%d).sql
Hardening de Seguridad Inicial
Ejecutar mysql_secure_installation
El script mysql_secure_installation proporciona mejoras de seguridad esenciales:
# Ejecutar script de seguridad
sudo mysql_secure_installation
Prompts del script y respuestas recomendadas:
Would you like to setup VALIDATE PASSWORD component? [Y/n]
Respuesta: Y (Sí - habilita validación de fortaleza de contraseña)
There are three levels of password validation policy:
LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG:
Respuesta: 2 (STRONG - para entornos de producción)
Change the password for root? [Y/n]
Respuesta: Y (si quieres actualizar contraseña root)
Remove anonymous users? [Y/n]
Respuesta: Y (usuarios anónimos son riesgos de seguridad)
Disallow root login remotely? [Y/n]
Respuesta: Y (root solo debería iniciar sesión localmente)
Remove test database and access to it? [Y/n]
Respuesta: Y (base de datos test es innecesaria en producción)
Reload privilege tables now? [Y/n]
Respuesta: Y (aplicar cambios inmediatamente)
Verificar Resultados del Script de Seguridad
# Iniciar sesión en MySQL
sudo mysql -u root -p
# Verificar usuarios anónimos (debería retornar vacío)
SELECT User, Host FROM mysql.user WHERE User = '';
# Verificar base de datos test (no debería existir)
SHOW DATABASES LIKE 'test';
# Verificar restricciones de host del usuario root
SELECT User, Host FROM mysql.user WHERE User = 'root';
# Salir de MySQL
EXIT;
Gestión de Usuarios y Control de Acceso
Comprender el Sistema de Privilegios de MySQL
MySQL usa un sistema de privilegios jerárquico:
- Privilegios globales: Se aplican a todas las bases de datos
- Privilegios de base de datos: Se aplican a base de datos específica
- Privilegios de tabla: Se aplican a tablas específicas
- Privilegios de columna: Se aplican a columnas específicas
- Privilegios de rutina: Se aplican a procedimientos almacenados y funciones
Crear Usuarios con Privilegio Mínimo
Nunca usar root para aplicaciones. Crear usuarios dedicados con privilegios mínimos requeridos:
-- Conectar como root
sudo mysql -u root -p
-- Crear usuario para base de datos específica
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongP@ssw0rd123!';
-- Crear base de datos
CREATE DATABASE myapp_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Otorgar solo privilegios específicos
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_production.* TO 'app_user'@'localhost';
-- Aplicar cambios
FLUSH PRIVILEGES;
-- Verificar privilegios de usuario
SHOW GRANTS FOR 'app_user'@'localhost';
Directrices de Privilegios:
-- Aplicación web (requisitos típicos)
GRANT SELECT, INSERT, UPDATE, DELETE ON database.* TO 'webapp_user'@'localhost';
-- Usuario de solo lectura para reportes
GRANT SELECT ON database.* TO 'report_user'@'localhost';
-- Usuario de backup (necesita privilegios específicos)
GRANT SELECT, RELOAD, SHOW DATABASES, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backup_user'@'localhost';
-- Usuario desarrollador (limitado a base de datos de desarrollo)
GRANT ALL PRIVILEGES ON dev_database.* TO 'developer'@'localhost';
-- Usuario de monitoreo (acceso de solo lectura al sistema)
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'monitor_user'@'localhost';
Restricciones de Acceso Basadas en Host
Restringir usuarios a hosts o direcciones IP específicas:
-- Usuario solo puede conectar desde localhost
CREATE USER 'local_user'@'localhost' IDENTIFIED BY 'password';
-- Usuario solo puede conectar desde IP específica
CREATE USER 'remote_user'@'192.168.1.50' IDENTIFIED BY 'password';
-- Usuario puede conectar desde subred específica
CREATE USER 'subnet_user'@'192.168.1.%' IDENTIFIED BY 'password';
-- Usuario puede conectar desde dominio específico
CREATE USER 'domain_user'@'%.example.com' IDENTIFIED BY 'password';
-- Evitar usar comodín % para producción
-- MAL: CREATE USER 'user'@'%' IDENTIFIED BY 'password';
Implementar Políticas de Contraseñas
Para MySQL 8.0+:
-- Instalar componente de validación de contraseña
INSTALL COMPONENT 'file://component_validate_password';
-- Configurar política de contraseña
SET GLOBAL validate_password.policy = STRONG;
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;
SET GLOBAL validate_password.check_user_name = ON;
-- Ver política actual
SHOW VARIABLES LIKE 'validate_password%';
-- Hacer configuraciones permanentes en my.cnf
[mysqld]
validate_password.policy=STRONG
validate_password.length=12
validate_password.mixed_case_count=1
validate_password.number_count=1
validate_password.special_char_count=1
Para MariaDB:
-- Instalar plugin de validación de contraseña
INSTALL SONAME 'simple_password_check';
-- Configurar requisitos de contraseña
SET GLOBAL simple_password_check_minimal_length = 12;
SET GLOBAL simple_password_check_digits = 1;
SET GLOBAL simple_password_check_letters_same_case = 1;
SET GLOBAL simple_password_check_other_characters = 1;
-- Agregar a archivo de configuración
[mariadb]
plugin_load_add = simple_password_check
simple_password_check_minimal_length = 12
simple_password_check_digits = 1
simple_password_check_letters_same_case = 1
simple_password_check_other_characters = 1
Expiración y Rotación de Contraseñas
Implementar políticas de expiración de contraseñas:
-- Establecer expiración global de contraseña (días)
SET GLOBAL default_password_lifetime = 90;
-- Establecer expiración de contraseña para usuario específico
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
-- Requerir cambio inmediato de contraseña
ALTER USER 'user'@'localhost' PASSWORD EXPIRE;
-- Nunca expirar contraseña (usar con moderación)
ALTER USER 'service_account'@'localhost' PASSWORD EXPIRE NEVER;
-- Ver estado de expiración de contraseña
SELECT User, Host, password_expired, password_lifetime, password_last_changed
FROM mysql.user;
Bloqueo de Cuenta
Bloquear y desbloquear cuentas de usuario:
-- Bloquear cuenta de usuario
ALTER USER 'suspicious_user'@'localhost' ACCOUNT LOCK;
-- Desbloquear cuenta de usuario
ALTER USER 'user'@'localhost' ACCOUNT UNLOCK;
-- Crear usuario con cuenta bloqueada
CREATE USER 'inactive_user'@'localhost' IDENTIFIED BY 'password' ACCOUNT LOCK;
-- Ver cuentas bloqueadas
SELECT User, Host, account_locked FROM mysql.user WHERE account_locked = 'Y';
Seguimiento de Intentos de Inicio de Sesión Fallidos (MySQL 8.0.19+)
-- Configurar seguimiento de intentos de inicio de sesión fallidos
CREATE USER 'user'@'localhost'
IDENTIFIED BY 'password'
FAILED_LOGIN_ATTEMPTS 3
PASSWORD_LOCK_TIME 2; -- Bloquear por 2 días
-- O usar UNBOUNDED para bloqueo permanente
ALTER USER 'user'@'localhost'
FAILED_LOGIN_ATTEMPTS 5
PASSWORD_LOCK_TIME UNBOUNDED;
-- Ver configuración de inicio de sesión fallido
SELECT User, Host, User_attributes
FROM mysql.user
WHERE JSON_EXTRACT(User_attributes, '$.Password_locking') IS NOT NULL;
Seguridad de Red
Vinculación a Interfaces Específicas
Configurar MySQL para escuchar solo en interfaces de red específicas:
# Editar archivo de configuración
# /etc/mysql/mysql.conf.d/mysqld.cnf (Ubuntu/Debian)
# /etc/my.cnf (CentOS/Rocky)
[mysqld]
# Escuchar solo en localhost (más seguro)
bind-address = 127.0.0.1
# Escuchar en IP específica
bind-address = 192.168.1.100
# Escuchar en múltiples IPs (MySQL 8.0.13+)
bind-address = 127.0.0.1,192.168.1.100
# Para MariaDB 10.3.3+
bind-address = 0.0.0.0 # Escuchar en todas las interfaces
# Luego usar firewall para restringir acceso
Reiniciar MySQL:
sudo systemctl restart mysql
# Verificar vinculación
sudo ss -tulpn | grep 3306
sudo netstat -tulpn | grep 3306
Deshabilitar Resolución de Nombres
Saltar búsquedas de nombres de host DNS para prevenir suplantación DNS:
[mysqld]
skip-name-resolve
Esto fuerza a MySQL a usar solo direcciones IP. Actualizar permisos en consecuencia:
-- Antes: otorgar a nombre de host
GRANT ALL ON database.* TO 'user'@'server.example.com';
-- Después: otorgar a IP
GRANT ALL ON database.* TO 'user'@'192.168.1.100';
-- Revocar permisos basados en nombre de host
REVOKE ALL ON database.* FROM 'user'@'server.example.com';
DROP USER 'user'@'server.example.com';
Reiniciar MySQL y verificar:
sudo systemctl restart mysql
# Verificar configuración
mysql -u root -p -e "SHOW VARIABLES LIKE 'skip_name_resolve';"
Cambiar Puerto Predeterminado
Cambiar puerto de MySQL para reducir superficie de ataque automatizado:
[mysqld]
port = 33306 # Puerto personalizado en lugar de 3306
Actualizar reglas de firewall:
# UFW
sudo ufw delete allow 3306/tcp
sudo ufw allow 33306/tcp
# firewalld
sudo firewall-cmd --permanent --remove-service=mysql
sudo firewall-cmd --permanent --add-port=33306/tcp
sudo firewall-cmd --reload
Reiniciar MySQL y verificar:
sudo systemctl restart mysql
# Conectar con puerto personalizado
mysql -u root -p -P 33306
# Verificar puerto
sudo ss -tulpn | grep mysql
Cifrado SSL/TLS
Generar Certificados SSL
Crear certificados SSL para conexiones cifradas:
# Crear directorio para certificados
sudo mkdir -p /etc/mysql/ssl
cd /etc/mysql/ssl
# Generar clave y certificado de CA
sudo openssl genrsa 2048 > ca-key.pem
sudo openssl req -new -x509 -nodes -days 3650 \
-key ca-key.pem \
-out ca-cert.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=MySQL-CA"
# Generar solicitud de certificado de servidor y clave
sudo openssl req -newkey rsa:2048 -days 3650 -nodes \
-keyout server-key.pem \
-out server-req.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=MySQL-Server"
# Procesar clave RSA de servidor
sudo openssl rsa -in server-key.pem -out server-key.pem
# Firmar certificado de servidor
sudo openssl x509 -req -in server-req.pem -days 3650 \
-CA ca-cert.pem \
-CAkey ca-key.pem \
-set_serial 01 \
-out server-cert.pem
# Generar solicitud de certificado de cliente y clave
sudo openssl req -newkey rsa:2048 -days 3650 -nodes \
-keyout client-key.pem \
-out client-req.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=MySQL-Client"
# Procesar clave RSA de cliente
sudo openssl rsa -in client-key.pem -out client-key.pem
# Firmar certificado de cliente
sudo openssl x509 -req -in client-req.pem -days 3650 \
-CA ca-cert.pem \
-CAkey ca-key.pem \
-set_serial 02 \
-out client-cert.pem
# Verificar certificados
sudo openssl verify -CAfile ca-cert.pem server-cert.pem client-cert.pem
# Establecer permisos apropiados
sudo chown mysql:mysql /etc/mysql/ssl/*
sudo chmod 600 /etc/mysql/ssl/*-key.pem
sudo chmod 644 /etc/mysql/ssl/*-cert.pem
Configurar MySQL para SSL
Editar configuración de MySQL:
[mysqld]
# Configuración SSL
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
# Requerir SSL para todas las conexiones (opcional pero recomendado)
require_secure_transport=ON
# Versiones TLS (MySQL 8.0+)
tls_version=TLSv1.2,TLSv1.3
# Configuración de cipher
ssl-cipher=ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
Reiniciar MySQL:
sudo systemctl restart mysql
# Verificar que SSL está habilitado
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
# Verificar estado SSL
mysql -u root -p -e "STATUS" | grep SSL
Requerir SSL para Usuarios
Forzar usuarios específicos a usar SSL:
-- Requerir SSL para usuario existente
ALTER USER 'app_user'@'%' REQUIRE SSL;
-- Crear usuario con requisito SSL
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
-- Requerir cipher SSL específico
ALTER USER 'app_user'@'%' REQUIRE CIPHER 'ECDHE-RSA-AES256-GCM-SHA384';
-- Requerir certificado de cliente válido
ALTER USER 'app_user'@'%' REQUIRE X509;
-- Requerir subject de certificado específico
ALTER USER 'app_user'@'%' REQUIRE SUBJECT '/C=US/ST=State/CN=MySQL-Client';
-- Requerir emisor de certificado específico
ALTER USER 'app_user'@'%' REQUIRE ISSUER '/C=US/ST=State/CN=MySQL-CA';
-- Ver requisitos SSL de usuario
SELECT User, Host, ssl_type, ssl_cipher, x509_issuer, x509_subject
FROM mysql.user
WHERE User = 'app_user';
-- Eliminar requisito SSL
ALTER USER 'user'@'host' REQUIRE NONE;
Conectar con SSL
# Conectar con SSL
mysql -u app_user -p --ssl-mode=REQUIRED
# Conectar con certificado de cliente
mysql -u app_user -p \
--ssl-ca=/etc/mysql/ssl/ca-cert.pem \
--ssl-cert=/etc/mysql/ssl/client-cert.pem \
--ssl-key=/etc/mysql/ssl/client-key.pem
# Verificar conexión SSL
mysql> \s
# Buscar: SSL: Cipher in use
# O consultar
mysql> SHOW STATUS LIKE 'Ssl_cipher';
Seguridad del Sistema de Archivos
Asegurar Archivos de Configuración
Proteger archivos de configuración de MySQL:
# Establecer permisos seguros en archivos de configuración
sudo chmod 644 /etc/mysql/my.cnf
sudo chmod 644 /etc/mysql/mysql.conf.d/mysqld.cnf
sudo chown root:root /etc/mysql/my.cnf
# Si se almacenan contraseñas en archivo de configuración (no recomendado)
sudo chmod 600 /etc/mysql/my.cnf
Asegurar Directorio de Datos
Proteger directorio de datos de MySQL:
# Establecer propiedad adecuada
sudo chown -R mysql:mysql /var/lib/mysql
# Establecer permisos seguros
sudo chmod 700 /var/lib/mysql
sudo chmod 660 /var/lib/mysql/*
# Verificar permisos
ls -la /var/lib/mysql/
Proteger Binary Logs
Asegurar binary logs de MySQL:
# Establecer propiedad
sudo chown mysql:mysql /var/lib/mysql/mysql-bin.*
# Establecer permisos
sudo chmod 660 /var/lib/mysql/mysql-bin.*
# Configurar purga automática en my.cnf
[mysqld]
expire_logs_days = 7
max_binlog_size = 100M
Deshabilitar LOCAL INFILE
Prevenir carga de datos desde archivos locales:
[mysqld]
local-infile=0
Verificar:
SHOW VARIABLES LIKE 'local_infile';
Configuración de Firewall
UFW (Ubuntu/Debian)
# Permitir MySQL desde IP específica
sudo ufw allow from 192.168.1.50 to any port 3306
# Permitir desde subred específica
sudo ufw allow from 192.168.1.0/24 to any port 3306
# Permitir desde múltiples IPs
sudo ufw allow from 192.168.1.50 to any port 3306
sudo ufw allow from 192.168.1.51 to any port 3306
# Eliminar regla si es necesario
sudo ufw status numbered
sudo ufw delete [número_regla]
# Habilitar firewall
sudo ufw enable
# Verificar estado
sudo ufw status verbose
firewalld (CentOS/Rocky Linux)
# Agregar regla rica para IP específica
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="192.168.1.50"
port protocol="tcp" port="3306" accept'
# Agregar regla rica para subred
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="192.168.1.0/24"
port protocol="tcp" port="3306" accept'
# Eliminar servicio MySQL predeterminado
sudo firewall-cmd --permanent --remove-service=mysql
# Recargar firewall
sudo firewall-cmd --reload
# Verificar reglas
sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
iptables
# Permitir desde IP específica
sudo iptables -A INPUT -p tcp -s 192.168.1.50 --dport 3306 -j ACCEPT
# Permitir desde subred
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 3306 -j ACCEPT
# Descartar todo otro tráfico MySQL
sudo iptables -A INPUT -p tcp --dport 3306 -j DROP
# Guardar reglas (Ubuntu/Debian)
sudo netfilter-persistent save
# Guardar reglas (CentOS/Rocky)
sudo service iptables save
# Ver reglas
sudo iptables -L -n -v
Registro y Monitoreo de Seguridad
Habilitar General Query Log
Registrar todas las consultas para auditoría de seguridad:
[mysqld]
general_log = 1
general_log_file = /var/log/mysql/general.log
Ver log:
sudo tail -f /var/log/mysql/general.log
Advertencia: El general log puede crecer mucho. Usar solo para solución de problemas o auditoría de corto plazo.
Registro de Errores
Configurar registro completo de errores:
[mysqld]
log_error = /var/log/mysql/error.log
log_error_verbosity = 3 # 1=errores, 2=+advertencias, 3=+notas
Binary Logging para Auditoría
Habilitar binary logging:
[mysqld]
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M
sync_binlog = 1 # Sincronizar a disco para confiabilidad
Plugin de Auditoría (MySQL Enterprise / MariaDB)
Plugin de Auditoría de MariaDB:
# Instalar plugin de auditoría
sudo mysql -u root -p <<EOF
INSTALL SONAME 'server_audit';
EOF
Configurar en my.cnf:
[mariadb]
plugin_load_add = server_audit
server_audit_logging = ON
server_audit_events = CONNECT,QUERY,TABLE
server_audit_output_type = FILE
server_audit_file_path = /var/log/mysql/audit.log
server_audit_file_rotate_size = 1000000
server_audit_file_rotations = 9
Ver configuración de auditoría:
SHOW VARIABLES LIKE 'server_audit%';
Monitorear Intentos de Inicio de Sesión Fallidos
Verificar fallos de autenticación:
# Verificar log de errores para fallos de autenticación
sudo grep "Access denied" /var/log/mysql/error.log
# Contar intentos fallidos por usuario
sudo grep "Access denied for user" /var/log/mysql/error.log | awk '{print $NF}' | sort | uniq -c | sort -rn
# Verificar intentos fallidos recientes
sudo tail -100 /var/log/mysql/error.log | grep "Access denied"
Mejores Prácticas de Seguridad
Eliminar Usuarios Anónimos
-- Verificar usuarios anónimos
SELECT User, Host FROM mysql.user WHERE User = '';
-- Eliminar usuarios anónimos
DELETE FROM mysql.user WHERE User = '';
FLUSH PRIVILEGES;
Eliminar Base de Datos Test
-- Eliminar base de datos test
DROP DATABASE IF EXISTS test;
-- Eliminar privilegios de base de datos test
DELETE FROM mysql.db WHERE Db LIKE 'test%';
FLUSH PRIVILEGES;
Deshabilitar Enlaces Simbólicos
Prevenir problemas de seguridad con enlaces simbólicos:
[mysqld]
symbolic-links=0
Asegurar Procedimientos Almacenados
Limitar creación de procedimientos almacenados:
-- Revocar CREATE ROUTINE de usuarios regulares
REVOKE CREATE ROUTINE ON *.* FROM 'app_user'@'localhost';
-- Solo otorgar a usuarios específicos que lo necesiten
GRANT CREATE ROUTINE ON myapp_db.* TO 'developer'@'localhost';
Deshabilitar SHOW DATABASES
Prevenir que usuarios vean lista de bases de datos:
-- Eliminar privilegio SHOW DATABASES
REVOKE SHOW DATABASES ON *.* FROM 'app_user'@'localhost';
-- El usuario aún puede acceder a bases de datos otorgadas
GRANT ALL ON specific_db.* TO 'app_user'@'localhost';
Restricciones de Privilegio FILE
Eliminar privilegio FILE de usuarios:
-- Verificar usuarios con privilegio FILE
SELECT User, Host FROM mysql.user WHERE File_priv = 'Y';
-- Revocar privilegio FILE
REVOKE FILE ON *.* FROM 'user'@'host';
-- Solo otorgar a usuarios de backup si es absolutamente necesario
GRANT FILE ON *.* TO 'backup_user'@'localhost';
Limitar Recursos de Base de Datos
Prevenir agotamiento de recursos:
-- Crear usuario con límites de recursos
CREATE USER 'limited_user'@'localhost' IDENTIFIED BY 'password'
WITH MAX_QUERIES_PER_HOUR 1000
MAX_UPDATES_PER_HOUR 500
MAX_CONNECTIONS_PER_HOUR 100
MAX_USER_CONNECTIONS 5;
-- Modificar usuario existente
ALTER USER 'app_user'@'localhost'
WITH MAX_QUERIES_PER_HOUR 10000
MAX_CONNECTIONS_PER_HOUR 500
MAX_USER_CONNECTIONS 10;
-- Ver límites de recursos
SELECT User, Host, max_questions, max_updates, max_connections, max_user_connections
FROM mysql.user
WHERE User = 'limited_user';
Auditoría de Seguridad
Auditorías de Seguridad Regulares
Crear script de auditoría:
sudo nano /usr/local/bin/mysql-security-audit.sh
Agregar contenido:
#!/bin/bash
REPORT_FILE="/var/log/mysql/security_audit_$(date +%Y%m%d).txt"
echo "Auditoría de Seguridad MySQL - $(date)" > $REPORT_FILE
echo "======================================" >> $REPORT_FILE
# Verificar usuarios sin contraseñas
echo -e "\n[CRÍTICO] Usuarios sin contraseñas:" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host FROM mysql.user WHERE authentication_string = '' OR authentication_string IS NULL;" >> $REPORT_FILE
# Verificar usuarios con host comodín
echo -e "\n[ADVERTENCIA] Usuarios con host comodín (%):" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host FROM mysql.user WHERE Host = '%';" >> $REPORT_FILE
# Verificar usuarios anónimos
echo -e "\n[CRÍTICO] Usuarios anónimos:" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host FROM mysql.user WHERE User = '';" >> $REPORT_FILE
# Verificar usuarios con privilegio FILE
echo -e "\n[ADVERTENCIA] Usuarios con privilegio FILE:" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host FROM mysql.user WHERE File_priv = 'Y';" >> $REPORT_FILE
# Verificar usuarios con privilegio SUPER
echo -e "\n[INFO] Usuarios con privilegio SUPER:" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y';" >> $REPORT_FILE
# Verificar configuración SSL
echo -e "\n[INFO] Estado SSL:" >> $REPORT_FILE
mysql -u root -p[password] -e "SHOW VARIABLES LIKE '%ssl%';" >> $REPORT_FILE
# Verificar base de datos test
echo -e "\n[ADVERTENCIA] Base de datos test existe:" >> $REPORT_FILE
mysql -u root -p[password] -e "SHOW DATABASES LIKE 'test';" >> $REPORT_FILE
# Verificar plugin de autenticación
echo -e "\n[INFO] Plugins de autenticación en uso:" >> $REPORT_FILE
mysql -u root -p[password] -e "SELECT User, Host, plugin FROM mysql.user;" >> $REPORT_FILE
echo -e "\nAuditoría completada. Reporte guardado en: $REPORT_FILE"
Pruebas de Penetración
Usar mysql-audit para evaluación de seguridad:
# Instalar mysql-audit
git clone https://github.com/habitissimo/mysql-audit.git
cd mysql-audit
# Ejecutar auditoría
./mysql-audit -u root -p password -h localhost
# Revisar recomendaciones
Respuesta a Incidentes
Detectar Actividad Sospechosa
-- Verificar conexiones actuales
SHOW PROCESSLIST;
-- Ver historial de conexión de usuario
SELECT User, Host, TIME, STATE, INFO
FROM information_schema.PROCESSLIST;
-- Verificar intentos de inicio de sesión fallidos (desde log de errores)
sudo grep "Access denied" /var/log/mysql/error.log | tail -20
Responder a Compromiso
Si se sospecha compromiso de base de datos:
-- Bloquear inmediatamente cuentas sospechosas
ALTER USER 'suspicious_user'@'%' ACCOUNT LOCK;
-- Cambiar todas las contraseñas
ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'new_strong_password';
-- Revisar y revocar privilegios excesivos
REVOKE ALL PRIVILEGES ON *.* FROM 'user'@'host';
GRANT SELECT, INSERT, UPDATE ON specific_db.* TO 'user'@'host';
-- Matar conexiones sospechosas
KILL CONNECTION process_id;
-- Revisar permisos para todos los usuarios
SELECT User, Host FROM mysql.user;
SHOW GRANTS FOR 'user'@'host';
Cumplimiento y Estándares
Cumplimiento PCI-DSS
Para datos de tarjetas de pago:
- Usar criptografía fuerte (TLS 1.2+)
- Implementar control de acceso fuerte
- Auditorías de seguridad regulares
- Registrar y monitorear todo acceso
- Cambiar contraseñas predeterminadas
- Deshabilitar servicios innecesarios
Cumplimiento GDPR
Para datos personales:
- Implementar cifrado en reposo y en tránsito
- Registro y auditoría de acceso
- Capacidades de anonimización de datos
- Procedimientos de derecho de eliminación
- Procedimientos de notificación de violación de datos
Conclusión
La seguridad de bases de datos es un proceso continuo que requiere vigilancia, auditorías regulares y mantenerse actualizado con mejores prácticas de seguridad y parches. Esta guía ha proporcionado medidas completas de hardening de seguridad para instalaciones de MySQL y MariaDB.
Lista de Verificación de Seguridad
- ✓ Ejecutar mysql_secure_installation
- ✓ Implementar políticas de contraseñas fuertes
- ✓ Usar principio de privilegio mínimo para todos los usuarios
- ✓ Habilitar cifrado SSL/TLS
- ✓ Configurar reglas de firewall
- ✓ Deshabilitar acceso root remoto
- ✓ Eliminar usuarios anónimos y base de datos test
- ✓ Habilitar registro y monitoreo de seguridad
- ✓ Auditorías de seguridad regulares
- ✓ Mantener MySQL/MariaDB actualizado
- ✓ Asegurar permisos del sistema de archivos
- ✓ Documentar procedimientos de seguridad
Resumen de Mejores Prácticas
- Nunca usar root para aplicaciones - Crear usuarios dedicados
- Siempre usar SSL/TLS para conexiones de red
- Implementar defensa en profundidad - Múltiples capas de seguridad
- Monitorear continuamente - Revisión regular de logs y alertas
- Auditar regularmente - Evaluaciones de seguridad trimestrales
- Mantener actualizado - Aplicar parches de seguridad rápidamente
- Documentar todo - Procedimientos y cambios de seguridad
- Probar backups - Verificar procedimientos de restauración
- Limitar acceso - Tanto basado en red como en privilegios
- Educar usuarios - Capacitación en concientización de seguridad
Recursos Adicionales
- Directrices de Seguridad MySQL
- Documentación de Seguridad MariaDB
- Benchmarks CIS MySQL
- Seguridad de Base de Datos OWASP
La seguridad no es una tarea de una sola vez sino un compromiso continuo. Revisa y actualiza regularmente tu postura de seguridad para proteger contra amenazas en evolución.


