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):"
lsMettete 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
$varsenza 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):'
lsInjection 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_dstInjection 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 dieval 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
fiValidazione 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' # REJECTEDUso 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_targetSanificazione 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
-vinpsqlo--data-urlencodeincurl. - Separate i dati dal codice: usate
printfcon 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
evalcon dati non attendibili; preferite${!var}per la ricerca indiretta. - Limitate i permessi — eseguite gli script con i privilegi minimi necessari; evitate
sudonegli 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
evalebash -c "$input"; usate${!varname}per un'espansione indiretta sicura. - Aprite sempre gli script con
set -euo pipefaileIFS=$'\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
- Prevenire l'iniezione di comandi e argomenti
- Gestire i segreti in sicurezza e mantenere pulito l'ambiente
- Esecuzione con privilegi minimi e disciplina nell'uso di sudo
- Analisi statica e audit con ShellCheck