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ónProtege contra…
-eContinuar el script tras un fallo.
-uUsar variables no definidas.
-o pipefailIgnorar fallos en pipelines.
-EIgnorar 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 sudo solo 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:

  1. Acepte un archivo CSV como argumento.
  2. Verifique que el archivo existe y tiene extensión .csv.
  3. Use mktemp para almacenar un log temporal.
  4. Procese el archivo con awk para mostrar el total de líneas.
  5. Elimine el archivo temporal al finalizar (usa trap).

🏋️‍♂️ Ejercicio práctico 2

Crea un script backup_seguro.sh que:

  1. Acepte como argumento el directorio a respaldar.
  2. Valide que existe.
  3. Cree un backup comprimido con fecha.
  4. Use rutas absolutas para los comandos críticos.
  5. Genere logs con permisos restrictivos (chmod 600).

🏋️‍♂️ Ejercicio práctico 3

Crea un script usuario_seguro.sh que:

  1. Acepte como argumento un nombre de usuario.
  2. Verifique que el nombre solo contiene letras y números.
  3. Cree un archivo temporal para registrar el resultado.
  4. Use id -- "$usuario" para mostrar información del usuario.
  5. Elimine el archivo temporal al salir.

✅ Buenas prácticas de seguridad

  1. ✅ Usa set -Eeuo pipefail en scripts críticos.
  2. ✅ Valida SIEMPRE la entrada del usuario.
  3. ✅ Usa comillas dobles "$VAR" y -- para evitar inyecciones.
  4. ✅ No dependas del PATH: usa rutas absolutas.
  5. ✅ Protege datos sensibles en variables de entorno y archivos de configuración.
  6. ✅ Limpia archivos temporales con trap y mktemp.
  • Loading...