0Pricing
DevOps Bootcamp · Lezione

Prevenire l'iniezione di comandi e argomenti

Metta tra virgolette, convalidi e passi tramite array gli input non attendibili per eliminare word splitting e iniezioni basate su eval.

Prevenire l'iniezione di comandi e argomenti è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.

Perché si verificano gli attacchi di injection in Bash

Bash è un potente linguaggio di collegamento: passa il testo direttamente al kernel, ad altri programmi e alle subshell. Questa potenza diventa una responsabilità non appena un input non attendibile raggiunge un comando senza validazione o virgolette.

Quasi ogni injection in Bash è riconducibile a due cause principali:

  • Suddivisione in parole: le variabili senza virgolette vengono suddivise sugli spazi vuoti (IFS), trasformando un singolo valore logico in più token della shell.
  • Espansione dei glob: caratteri come *, ? e [ vengono espansi dalla shell prima ancora che il comando venga eseguito.

Un attaccante che controlla un nome di file, un nome utente, un parametro URL o una variabile d'ambiente può sfruttare entrambi i meccanismi per eseguire comandi arbitrari, leggere file o aumentare i privilegi.

Questa lezione mostra esattamente come si presentano queste vulnerabilità e, soprattutto, come eliminarle usando virgolette corrette, validazione degli input e passaggio degli argomenti tramite array.

La suddivisione in parole: la minaccia silenziosa

Quando Bash incontra una variabile senza virgolette, ne suddivide il valore in corrispondenza di ogni carattere elencato in $IFS (impostazione predefinita: spazio, tabulazione, nuova riga). Quello che sembra un unico argomento diventa una serie di argomenti.

Eseguite lo script qui sotto e osservate come un nome di file contenente uno spazio diventa due argomenti distinti per rm.

#!/usr/bin/env bash
# Dangerous: unquoted variable
FILE='important file.txt'

# Create the file so the demo is self-contained
touch "$FILE"

echo "Files before:"
ls

# BUG: rm sees TWO arguments: 'important' and 'file.txt'
# If 'important' does not exist, rm prints an error but continues.
rm $FILE   # <-- unquoted, word-split happens here

echo "Files after (unquoted rm):"
ls

Mettete sempre le virgolette: la prima regola del Bash difensivo

La difesa più semplice ed efficace contro la suddivisione in parole consiste nel racchiudere sempre tra virgolette doppie le espansioni delle variabili.

  • "$var" — viene espansa in un unico token, preservando spazi, tabulazioni e nuove righe.
  • 'literal' — virgolette singole: nessuna espansione, utili per le stringhe fisse.
  • Non usate mai $var senza virgolette, a meno che non vi servano esplicitamente la suddivisione in parole e l'espansione dei glob.

Lo script qui sotto mostra la versione sicura dell'esempio precedente.

#!/usr/bin/env bash
set -euo pipefail

FILE='important file.txt'
touch "$FILE"

echo 'Files before:'
ls

# SAFE: double-quotes keep the filename as one token
rm "$FILE"

echo 'Files after (quoted rm):'
ls

Injection tramite glob: quando * diventa un'arma

Le variabili senza virgolette sono inoltre soggette all'espansione dei percorsi (globbing). Se l'input controllato dall'utente contiene * o ?, Bash lo espande in base al filesystem prima dell'esecuzione del comando.

Un vettore d'attacco classico è un modulo web che imposta PATTERN=* mentre lo script esegue cp $PATTERN /tmp/leak/, copiando ogni file della directory corrente.

La correzione è identica: racchiudete la variabile tra virgolette doppie. Un "$PATTERN" tra virgolette viene passato letteralmente; la shell non esegue mai l'espansione dei glob su di esso.

#!/usr/bin/env bash
set -euo pipefail

# Simulate attacker-supplied input
PATTERN='*'

mkdir -p /tmp/safe_demo_src /tmp/safe_demo_dst
touch /tmp/safe_demo_src/secret1.txt /tmp/safe_demo_src/secret2.txt

cd /tmp/safe_demo_src

# UNSAFE: glob expands, copies every file
# cp $PATTERN /tmp/safe_demo_dst/

# SAFE: pattern is treated as a literal filename
cp "$PATTERN" /tmp/safe_demo_dst/ 2>&1 || echo 'No file named literally "*" — attack neutralised'

rm -rf /tmp/safe_demo_src /tmp/safe_demo_dst

Injection degli argomenti tramite parametri posizionali senza virgolette

Gli script che accettano argomenti dal chiamante sono bersagli privilegiati per le injection. Ogni parametro posizionale ($1, $2, ...) deve essere racchiuso tra virgolette in ogni punto in cui viene utilizzato.

Un modello particolarmente pericoloso consiste nel passare $@ o $* senza virgolette a un altro comando:

  • "$@" — espande ogni parametro posizionale come una parola separata e racchiusa individualmente tra virgolette. Usate sempre questa forma.
  • $@ o $* senza virgolette — soggetti alla suddivisione in parole e al globbing.
  • "$*" — unisce tutti i parametri in un'unica parola (quasi mai ciò che serve).
#!/usr/bin/env bash
set -euo pipefail

# Safe wrapper: forward all arguments quoted
grep_wrapper() {
    local pattern="$1"
    shift
    # "$@" preserves each file argument as one token
    grep -rn "$pattern" "$@"
}

# Usage: ./script 'error msg' /var/log/syslog '/path with spaces/app.log'
echo 'Searching current script for "safe":'
grep_wrapper 'safe' "$0"

Injection dei comandi tramite eval e input non validato

eval analizza nuovamente il proprio argomento come codice della shell. Qualsiasi dato non attendibile che raggiunga eval può eseguire comandi arbitrari.

Modelli comuni pericolosi:

  • eval "$user_input"
  • eval echo \$$var (ricerca indiretta di una variabile)
  • Passaggio di dati dell'utente tramite bash -c "$input"

Regola: non passate mai input non attendibili a eval o bash -c. Usate invece alternative sicure di Bash:

  • Espansione indiretta: ${!varname} invece di eval echo \$$varname
  • Array associativi per la ricerca dinamica di coppie chiave-valore
  • Funzioni invece di stringhe di comando generate
#!/usr/bin/env bash
set -euo pipefail

# Simulated attacker-supplied variable name
VARNAME='PATH; echo INJECTED'

# UNSAFE: eval lets attacker run 'echo INJECTED'
# eval "echo \$$VARNAME"

# SAFE: indirect expansion only resolves valid variable names
# First validate that VARNAME is a legal identifier
if [[ "$VARNAME" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
    echo "Value: ${!VARNAME}"
else
    echo "ERROR: invalid variable name: '$VARNAME'" >&2
    exit 1
fi

Validazione degli input: allowlist invece di denylist

Rifiutare i caratteri noti come pericolosi (una denylist) è fragile: gli attaccanti possono trovare codifiche o caratteri che avete dimenticato. Usate invece una allowlist: accettate solo i caratteri che sapete essere sicuri.

Strategie di allowlist in Bash:

  • Corrispondenza con un'espressione regolare: [[ "$input" =~ ^[A-Za-z0-9_-]+$ ]]
  • Corrispondenza con un pattern: case "$input" in [A-Za-z0-9]*) ... ;; esac
  • Controllo tramite enumerazione: confronto con un insieme fisso di valori validi

Validate al confine, non appena l'input entra nello script, prima che raggiunga qualsiasi comando.

#!/usr/bin/env bash
set -euo pipefail

validate_username() {
    local name="$1"
    # Allowlist: only lowercase letters, digits, underscore, hyphen; 1-32 chars
    if [[ ! "$name" =~ ^[a-z0-9_-]{1,32}$ ]]; then
        echo "ERROR: invalid username '${name}'" >&2
        return 1
    fi
    echo "Username accepted: $name"
}

validate_username 'alice'          # OK
validate_username 'bob_smith-2'    # OK
validate_username 'root; rm -rf /' # REJECTED
validate_username '../etc/passwd'   # REJECTED

Uso degli array per passare gli argomenti in modo sicuro

Quando dovete costruire dinamicamente un comando, ad esempio aggiungendo flag in modo condizionale o iterando sugli input, usate un array Bash invece di concatenare stringhe.

La concatenazione di stringhe elimina tutta la struttura; un array Bash conserva ogni argomento come elemento distinto, senza una nuova analisi da parte della shell.

  • Dichiarazione: args=()
  • Aggiunta: args+=(--flag "$value")
  • Esecuzione: command "${args[@]}"

"${args[@]}" espande ogni elemento come una parola separata e racchiusa individualmente tra virgolette, esattamente come "$@".

#!/usr/bin/env bash
set -euo pipefail

# Build a find command safely with an array
BASEDIR='/tmp'
USER_PATTERN='*.log'   # could come from user input (validate first!)
MAX_DAYS=7

cmd=(find "$BASEDIR" -type f -name "$USER_PATTERN" -mtime "+${MAX_DAYS}")

# Optionally add -delete only when requested
DELETE=false
if [[ "$DELETE" == 'true' ]]; then
    cmd+=(-delete)
fi

echo "Running: ${cmd[*]}"
"${cmd[@]}"

Il separatore --: protezione contro l'injection dei flag

Anche un argomento racchiuso correttamente tra virgolette può essere interpretato erroneamente come un'opzione se inizia con -. Considerate rm "$file" quando file='-rf .': le virgolette proteggono dalla suddivisione in parole, ma rm interpreta comunque -rf come una serie di flag.

La convenzione POSIX -- segnala la fine delle opzioni alla maggior parte delle utilità GNU/BSD. Tutto ciò che segue -- viene trattato come argomento posizionale, mai come flag.

#!/usr/bin/env bash
set -euo pipefail

# Simulate a dangerous filename supplied by the user
FILENAME='-rf /tmp/safe_demo_target'

mkdir -p /tmp/safe_demo_target
touch /tmp/safe_demo_target/keep_me.txt

echo 'Files before:'
ls /tmp/safe_demo_target/

# UNSAFE (flag injection — do NOT uncomment in real systems):
# rm "$FILENAME"

# SAFE: -- ends option processing; filename is treated literally
rm -- "$FILENAME" 2>&1 || echo "No such file (attack neutralised): $FILENAME"

echo 'Files after:'
ls /tmp/safe_demo_target/
rm -rf /tmp/safe_demo_target

Sanificazione degli input per SQL e strumenti esterni

Quando gli script Bash invocano CLI per database (psql, mysql), curl con URL forniti dall'utente o strumenti simili, si applicano due livelli aggiuntivi:

  • Query parametrizzate: non interpolate mai i dati dell'utente nelle stringhe SQL. Passate i valori tramite -v in psql o --data-urlencode in curl.
  • Separate i dati dal codice: usate printf con una stringa di formato letterale; non permettete mai che l'input dell'utente sia la stringa di formato.

L'esempio qui sotto esegue una query sicura su PostgreSQL, mantenendo completamente fuori dal testo SQL il valore fornito dall'utente.

#!/usr/bin/env bash
set -euo pipefail

# Validate first: only allow alphanumeric usernames
USERNAME="${1:-alice}"
if [[ ! "$USERNAME" =~ ^[a-z0-9_]{1,32}$ ]]; then
    echo 'ERROR: invalid username' >&2
    exit 1
fi

# UNSAFE:
# psql -c "SELECT * FROM users WHERE name = '$USERNAME';"

# SAFE: pass value as a psql variable, never inside the SQL text
# psql -v username="$USERNAME" -c 'SELECT * FROM users WHERE name = :username;'

# Demonstrate the principle with printf (safe format string usage)
printf 'Query would use username: %s\n' "$USERNAME"

Lista di controllo per la messa in sicurezza: riunire tutti gli elementi

Uno script Bash sicuro e pronto per la produzione combina in una difesa coerente e stratificata tutte le tecniche di questa lezione. Ecco un modello minimo ma completo:

  • set -euo pipefail — esce in caso di errore, considera come errori le variabili non impostate e propaga gli errori nelle pipe.
  • Validate all'ingresso — usate un'allowlist per ogni input esterno prima che raggiunga qualsiasi comando.
  • Mettete tutto tra virgolette — "$var", "$@", "${array[@]}" — senza eccezioni, a meno che non vi serva la suddivisione.
  • Usate gli array per costruire dinamicamente i comandi.
  • Anteponete -- agli argomenti quando passate nomi di file o stringhe forniti dall'utente.
  • Non usate mai eval con dati non attendibili; preferite ${!var} per la ricerca indiretta.
  • Limitate i permessi — eseguite gli script con i privilegi minimi necessari; evitate sudo negli script che accettano input dell'utente.
#!/usr/bin/env bash
# Hardened template — safe argument injection prevention
set -euo pipefail
IFS=$'\n\t'

#--- 1. Validate inputs at the boundary ---
SEARCH_DIR="${1:-}"
PATTERN="${2:-}"

[[ -z "$SEARCH_DIR" || -z "$PATTERN" ]] && { echo 'Usage: script <dir> <pattern>' >&2; exit 1; }
[[ ! "$SEARCH_DIR" =~ ^[A-Za-z0-9/_.-]+$ ]] && { echo 'ERROR: unsafe directory path' >&2; exit 1; }
[[ ! "$PATTERN" =~ ^[A-Za-z0-9._-]+$ ]]    && { echo 'ERROR: unsafe pattern'        >&2; exit 1; }

#--- 2. Build command with an array ---
cmd=(find -- "$SEARCH_DIR" -type f -name "$PATTERN")

#--- 3. Execute — no string interpolation, no eval ---
echo "Executing: ${cmd[*]}"
"${cmd[@]}"

Verifica delle conoscenze: virgolette e prevenzione delle injection

Verificate la vostra comprensione dei concetti chiave di questa lezione.

Riepilogo della lezione: prevenire l'injection di comandi e argomenti

Avete esaminato l'intero insieme di strumenti difensivi per gestire in sicurezza gli input Bash:

  • La suddivisione in parole e l'espansione dei glob sono i meccanismi fondamentali che trasformano le variabili non sicure in vettori di injection.
  • Racchiudete ogni variabile tra virgolette doppie ("$var", "$@", "${arr[@]}") per neutralizzare entrambe le minacce.
  • Usate "$@", mai $@ o $* senza virgolette, quando inoltrate gli argomenti.
  • Anteponete -- ai nomi di file forniti dall'utente per prevenire l'injection dei flag.
  • Applicate un'allowlist a tutti gli input esterni con un controllo tramite espressione regolare ([[ $v =~ ^pattern$ ]]) prima che raggiungano qualsiasi comando.
  • Costruite i comandi dinamici con gli array (cmd+=() → "${cmd[@]}"), mai tramite concatenazione di stringhe.
  • Eliminate eval e bash -c "$input"; usate ${!varname} per un'espansione indiretta sicura.
  • Aprite sempre gli script con set -euo pipefail e IFS=$'\n\t' per una base di sicurezza solida.

Applicate con coerenza fin dalla prima riga di ogni script, queste pratiche riducono quasi a zero la superficie d'attacco di Bash per le vulnerabilità della classe injection.

Domande Frequenti

La lezione «Prevenire l'iniezione di comandi e argomenti» è gratuita?

Sì — il testo completo di «Prevenire l'iniezione di comandi e argomenti» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.

Cosa imparerò in «Prevenire l'iniezione di comandi e argomenti»?

Metta tra virgolette, convalidi e passi tramite array gli input non attendibili per eliminare word splitting e iniezioni basate su eval. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare DevOps Bootcamp?

Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Prevenire l'iniezione di comandi e argomenti»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?

Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Prevenire l'iniezione di comandi e argomenti
  2. Gestire i segreti in sicurezza e mantenere pulito l'ambiente
  3. Esecuzione con privilegi minimi e disciplina nell'uso di sudo
  4. Analisi statica e audit con ShellCheck
← Torna a DevOps Bootcamp