Curso de Shell Scripting
Los scripts mal escritos pueden:
- Exponer datos sensibles (tokens, contraseñas).
- Permitir inyección de comandos peligrosos.
- Ser explotados por archivos con nombres maliciosos.
- Romperse al ejecutarse en diferentes entornos.
En este módulo aprenderás las mejores prácticas de seguridad en Bash.
17.1. Configuración segura con set
Al inicio de los scripts críticos, activa estas opciones:
1#!/usr/bin/env bash 2set -Eeuo pipefail
| Opción | Protege contra… |
|---|---|
-e | Continuar el script tras un fallo. |
-u | Usar variables no definidas. |
-o pipefail | Ignorar fallos en pipelines. |
-E | Ignorar traps de error en funciones/subshells. |
👉 Con esto, el script fallará rápido si algo sale mal.
17.2. Manejo seguro de variables de entrada
Nunca asumas que los argumentos del usuario son seguros.
Ejemplo inseguro:
1ARCHIVO=$1 2cat $ARCHIVO
👉 Si el usuario pasa "; rm -rf /", podría ejecutarse un comando malicioso.
✅ Solución: cita y valida las variables
1ARCHIVO="$1" 2 3# Verifica que el archivo exista 4if [[ ! -f "$ARCHIVO" ]]; then 5 echo "❗ Error: archivo no encontrado" 6 exit 1 7fi 8 9# Usa comillas siempre 10cat "$ARCHIVO"
17.3. Validación de entrada
Antes de usar variables externas:
- Comprueba existencia de archivos o carpetas.
- Valida tipos numéricos.
- Limita longitud y caracteres permitidos.
Ejemplo: validar que un argumento es un número entero positivo:
1if [[ ! "$1" =~ ^[0-9]+$ ]]; then 2 echo "❗ Error: se esperaba un número entero positivo" 3 exit 1 4fi
17.4. Riesgo del word splitting y del IFS
Por defecto, Bash separa valores por espacios. Esto puede romper scripts con nombres de archivos que contengan espacios.
Ejemplo inseguro:
1for f in $(ls *.txt); do 2 echo "Archivo: $f" 3done
👉 Fallará con nombres como mis datos.txt.
✅ Solución: usa arrays o find … -print0
1IFS=$'\n' # separador de líneas 2for f in *.txt; do 3 echo "Archivo: $f" 4done
O mejor:
1find . -name "*.txt" -print0 | while IFS= read -r -d '' f; do 2 echo "Archivo: $f" 3done
👉 -print0 y -d '' protegen contra espacios y caracteres especiales.
17.5. No dependas del PATH
Un atacante podría poner un comando falso con el mismo nombre en otra carpeta. Siempre usa rutas absolutas en scripts críticos.
Malo:
1cp archivo /destino
Mejor:
1/usr/bin/cp archivo /destino
17.6. Manejo de datos sensibles
Nunca pongas contraseñas o tokens en el propio script. Usa variables de entorno o archivos de configuración con permisos restringidos.
1DB_PASS="${DB_PASS:?Error: la variable DB_PASS no está definida}"
👉 El script fallará si DB_PASS no está definida.
17.7. Evitar inyección en comandos externos
Al construir comandos con datos externos, cítalos.
Ejemplo inseguro:
1usuario=$1 2id $usuario
Si el usuario pasa "; rm -rf /", se ejecutaría algo destructivo.
✅ Solución:
1usuario="$1" 2id -- "$usuario"
👉 El -- indica el fin de las opciones y evita interpretaciones maliciosas.
17.8. Uso seguro de archivos temporales
No uses nombres fijos (riesgo de colisiones e inyección).
Malo:
1TMP=/tmp/temporal.txt
Mejor:
1TMP=$(mktemp)
👉 mktemp crea archivos únicos y seguros.
17.9. Limitar permisos y privilegios
- Ejecuta los scripts con el usuario menos privilegiado posible.
- Cambia permisos de archivos sensibles:
1chmod 600 ~/.config/mi_app.conf
- Si un script necesita permisos de superusuario, usa
sudosolo donde sea necesario.
17.10. Limpiar datos temporales con trap
1TMP=$(mktemp) 2trap 'rm -f "$TMP"' EXIT 3 4echo "Datos sensibles" > "$TMP"
👉 El archivo temporal se elimina aunque el script falle o se interrumpa.
17.11. Ejemplo integrador: procesamiento seguro de logs
1#!/usr/bin/env bash 2set -Eeuo pipefail 3 4LOGDIR="$1" 5 6# Validar argumento 7if [[ -z "$LOGDIR" || ! -d "$LOGDIR" ]]; then 8 echo "❗ Error: debes proporcionar un directorio válido" 9 exit 1 10fi 11 12TMP=$(mktemp) 13trap 'rm -f "$TMP"' EXIT 14 15# Buscar logs y contar errores 16find "$LOGDIR" -type f -name "*.log" -print0 | \ 17while IFS= read -r -d '' archivo; do 18 grep -c "ERROR" "$archivo" >> "$TMP" 19done 20 21echo "Total de errores: $(awk '{s+=$1} END{print s}' "$TMP")"
👉 Buenas prácticas aplicadas:
set -Eeuo pipefail→ script robusto ante fallos.- Validación de entrada.
- Manejo seguro de archivos temporales.
- Citas correctas para evitar inyecciones.
🏋️♂️ Ejercicio práctico 1
Crea un script importa_datos.sh que:
- Acepte un archivo CSV como argumento.
- Verifique que el archivo existe y tiene extensión
.csv. - Use
mktemppara almacenar un log temporal. - Procese el archivo con
awkpara mostrar el total de líneas. - Elimine el archivo temporal al finalizar (usa
trap).
🏋️♂️ Ejercicio práctico 2
Crea un script backup_seguro.sh que:
- Acepte como argumento el directorio a respaldar.
- Valide que existe.
- Cree un backup comprimido con fecha.
- Use rutas absolutas para los comandos críticos.
- Genere logs con permisos restrictivos (
chmod 600).
🏋️♂️ Ejercicio práctico 3
Crea un script usuario_seguro.sh que:
- Acepte como argumento un nombre de usuario.
- Verifique que el nombre solo contiene letras y números.
- Cree un archivo temporal para registrar el resultado.
- Use
id -- "$usuario"para mostrar información del usuario. - Elimine el archivo temporal al salir.
✅ Buenas prácticas de seguridad
- ✅ Usa
set -Eeuo pipefailen scripts críticos. - ✅ Valida SIEMPRE la entrada del usuario.
- ✅ Usa comillas dobles
"$VAR"y--para evitar inyecciones. - ✅ No dependas del
PATH: usa rutas absolutas. - ✅ Protege datos sensibles en variables de entorno y archivos de configuración.
- ✅ Limpia archivos temporales con
trapymktemp.
- Loading...