DevOps-bootcamp · Oppitunti

Komento- ja argumentti-injektioiden estäminen

Lainaa, validoi ja välitä epäluotettava syöte taulukkoina, jotta estät sanajaon ja eval-pohjaiset injektiot.

Oppitunti 1/413 vaihetta

Komento- ja argumentti-injektioiden estäminen on ilmainen DevOps-bootcamp-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu DevOps-bootcamp-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.

Miksi injektiohyökkäyksiä tapahtuu Bashissa

Bash on tehokas liimakieli — se välittää tekstiä suoraan ytimelle, muille ohjelmille ja alikuorille. Tämä teho muuttuu riskiksi heti, kun epäluotettu syöte päätyy komennolle ilman tarkistusta tai lainausta.

Lähes kaikkien Bash-injektioiden taustalla on kaksi perussyytä:

  • Sanojen pilkkominen: Lainaamattomat muuttujat pilkotaan tyhjemerkkien (IFS) kohdalta, jolloin yksi looginen arvo muuttuu useiksi komentotulkin tunnuksiksi.
  • Jokerimerkkien laajennus: Merkit kuten *, ? ja [ laajennetaan komentotulkissa ennen komennon suorittamista.

Hyökkääjä, joka hallitsee tiedostonimeä, käyttäjänimeä, URL-parametria tai ympäristömuuttujaa, voi hyödyntää kumpaakin suorittaakseen mielivaltaisia komentoja, lukeakseen tiedostoja tai korottaakseen käyttöoikeuksia.

Tässä oppitunnissa näytetään tarkasti, miten nämä haavoittuvuudet syntyvät ja — mikä tärkeintä — miten ne poistetaan oikealla lainauksella, syötteen tarkistuksella ja taulukoihin perustuvalla argumenttien välityksellä.

Sanojen pilkkominen: huomaamaton uhka

Kun Bash kohtaa lainaamattoman muuttujan, se pilkkoo sen arvon kaikkien $IFS-muuttujassa lueteltujen merkkien kohdalta (oletuksena välilyönti, sarkain ja rivinvaihto). Yhdeltä argumentilta näyttävästä arvosta tulee useita argumentteja.

Suorittakaa alla oleva skripti ja tarkkailkaa, kuinka välilyönnin sisältävä tiedostonimi muuttuu kahdeksi erilliseksi rm-komennon argumentiksi.

#!/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

Lainatkaa aina: puolustavan Bash-ohjelmoinnin ensimmäinen sääntö

Yksinkertaisin ja tehokkain suoja sanojen pilkkomista vastaan on lainata muuttujalaajennukset aina kaksinkertaisilla lainausmerkeillä.

  • "$var" — laajennetaan täsmälleen yhdeksi tunnukseksi, jolloin välilyönnit, sarkaimet ja rivinvaihdot säilyvät.
  • 'literal' — yksinkertaiset lainausmerkit: mitään laajennuksia ei tehdä; sopii kiinteille merkkijonoille.
  • Älkää käyttäkö muuttujaa $var lainaamattomana, paitsi jos tarvitsette nimenomaisesti sanojen pilkkomista ja jokerimerkkien laajennusta.

Alla oleva skripti näyttää edellisen esimerkin turvallisen version.

#!/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

Jokerimerkkien injektio: kun * muuttuu aseeksi

Lainaamattomat muuttujat ovat alttiita myös polkulaajennukselle (jokerimerkkien käsittelylle). Jos käyttäjän hallitsema syöte sisältää merkin * tai ?, Bash laajentaa sen tiedostojärjestelmän perusteella ennen komennon suorittamista.

Perinteinen hyökkäysvektori on verkkolomake, joka asettaa arvon PATTERN=*, minkä jälkeen skripti suorittaa komennon cp $PATTERN /tmp/leak/ — tällöin kaikki nykyisen hakemiston tiedostot kopioidaan.

Korjaus on sama: lainatkaa muuttuja kaksinkertaisilla lainausmerkeillä. Lainattu "$PATTERN" välitetään sellaisenaan, eikä komentotulkki suorita sille jokerimerkkien laajennusta.

#!/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

Argumentti-injektio lainaamattomien paikkaparametrien kautta

Kutsujalta argumentteja vastaanottavat skriptit ovat ensisijaisia injektiokohteita. Jokainen paikkaparametri ($1, $2, ...) on lainattava kaikkialla, missä sitä käytetään.

Erityisen vaarallista on välittää $@ tai $* lainaamattomana toiselle komennolle:

  • "$@" — laajentaa jokaisen paikkaparametrin erilliseksi, yksitellen lainatuksi sanaksi. Käyttäkää aina tätä muotoa.
  • $@ tai $* lainaamattomana — altistuu sanojen pilkkomiselle ja jokerimerkkien laajennukselle.
  • "$*" — yhdistää kaikki parametrit yhdeksi sanaksi, mikä on harvoin haluttu lopputulos.
#!/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"

Komento-injektio eval-komennon ja tarkistamattoman syötteen kautta

eval tulkitsee argumenttinsa uudelleen komentotulkin koodina. Kaikki eval-komennolle päätyvä epäluotettu data voi suorittaa mielivaltaisia komentoja.

Yleisiä vaarallisia malleja:

  • eval "$user_input"
  • eval echo \$$var (epäsuora muuttujahaku)
  • Käyttäjädatan välittäminen komennolla bash -c "$input"

Sääntö: älkää koskaan välittäkö epäluotettua syötettä komennolle eval tai bash -c. Käyttäkää turvallisia Bash-vaihtoehtoja:

  • Epäsuora laajennus: ${!varname} komennon eval echo \$$varname sijaan
  • Assosiatiiviset taulukot dynaamiseen avain–arvo-hakuun
  • Funktiot muodostettujen komentomerkkijonojen sijaan
#!/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

Syötteen tarkistus: sallitut listat kiellettyjen listojen sijaan

Tunnetusti haitallisten merkkien hylkääminen (kielletty lista) on hauraata — hyökkääjät löytävät koodauksia tai merkkejä, jotka olette unohtaneet. Määrittäkää sen sijaan sallittu lista: hyväksykää vain merkit, joiden tiedätte olevan turvallisia.

Sallittujen listojen strategioita Bashissa:

  • Säännöllisen lausekkeen vertailu: [[ "$input" =~ ^[A-Za-z0-9_-]+$ ]]
  • Kuvion vertailu: case "$input" in [A-Za-z0-9]*) ... ;; esac
  • Enumeraation tarkistus: verratkaa arvoa kiinteään joukkoon kelvollisia arvoja

Tarkistakaa syöte rajapinnalla — heti, kun se tulee skriptiin — ennen kuin se päätyy millekään komennolle.

#!/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

Argumenttien turvallinen välittäminen taulukoiden avulla

Kun komento on muodostettava dynaamisesti — esimerkiksi lippoja ehdollisesti lisäämällä tai syötteitä läpikäymällä — käyttäkää Bash-taulukkoa merkkijonojen yhdistämisen sijaan.

Merkkijonojen yhdistäminen hävittää rakenteen; Bash-taulukko säilyttää jokaisen argumentin erillisenä alkiona ilman komentotulkin uutta tulkintaa.

  • Määrittely: args=()
  • Lisääminen: args+=(--flag "$value")
  • Suorittaminen: command "${args[@]}"

"${args[@]}" laajentaa jokaisen alkion erilliseksi, yksitellen lainatuksi sanaksi — aivan kuten "$@".

#!/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[@]}"

---erotin: suojaus lippuinjektiolta

Oikein lainattu argumenttikin voidaan tulkita valitsimeksi, jos se alkaa merkillä -. Tarkastelkaa komentoa rm "$file", kun file='-rf .': lainaus suojaa sanojen pilkkomiselta, mutta rm tulkitsee -rf-osan silti lipuiksi.

POSIX-käytäntö -- ilmaisee useimmille GNU/BSD-apuohjelmille valitsinten lopun. Kaikki ---erottimen jälkeinen käsitellään paikkaperustaisena argumenttina, ei koskaan lippuna.

#!/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

Syötteen puhdistaminen SQL:ää ja ulkoisia työkaluja varten

Kun Bash-skriptit kutsuvat tietokantojen komentorivityökaluja (psql, mysql), käyttäjän antamia URL-osoitteita käsittelevää curl-komentoa tai vastaavia työkaluja, sovelletaan kahta lisäkerrosta:

  • Parametrisoidut kyselyt: Älkää koskaan upottako käyttäjän dataa SQL-merkkijonoihin. Välittäkää arvot psql-komennolle valitsimella -v tai curl-komennolle valitsimella --data-urlencode.
  • Erotelkaa data ja koodi: Käyttäkää printf-komentoa kiinteän muotoilumerkkijonon kanssa; älkää koskaan antako käyttäjän syötteen toimia muotoilumerkkijonona.

Alla oleva esimerkki kysyy PostgreSQL:stä turvallisesti ja pitää käyttäjän antaman arvon kokonaan SQL-tekstin ulkopuolella.

#!/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"

Tietoturvan vahvistamisen tarkistuslista: kaikki tekniikat yhdessä

Tuotantokelpoinen turvallinen Bash-skripti yhdistää kaikki tämän oppitunnin tekniikat johdonmukaiseksi, kerroksittaiseksi suojaukseksi. Tässä on minimaalinen mutta täydellinen vahvistettu mallipohja:

  • set -euo pipefail — lopettaa virheessä, käsittelee määrittelemättömät muuttujat virheinä ja välittää putken virheet eteenpäin.
  • Tarkistakaa syöte heti alussa — määrittäkää jokaiselle ulkoiselle syötteelle sallittu lista ennen kuin se päätyy millekään komennolle.
  • Lainatkaa kaikki — "$var", "$@", "${array[@]}" — ei poikkeuksia, paitsi jos tarvitsette pilkkomista.
  • Käyttäkää taulukoita dynaamisten komentojen muodostamiseen.
  • Lisätkää argumenttien eteen --, kun välitätte käyttäjän antamia tiedostonimiä tai merkkijonoja.
  • Älkää koskaan käyttäkö komentoa eval epäluotetun datan kanssa; suosikaa epäsuoraan hakuun laajennusta ${!var}.
  • Rajoittakaa käyttöoikeudet — suorittakaa skriptit vähimmillä tarvittavilla käyttöoikeuksilla ja välttäkää sudo-komentoa käyttäjän syötettä vastaanottavissa skripteissä.
#!/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[@]}"

Tietotesti: lainaus ja injektioiden estäminen

Testatkaa ymmärryksenne tämän oppitunnin keskeisistä käsitteistä.

Oppitunnin kertaus: komento- ja argumentti-injektioiden estäminen

Olette käyneet läpi Bashin turvallisen syötteenkäsittelyn kattavan puolustuskeinovalikoiman:

  • Sanojen pilkkominen ja jokerimerkkien laajennus ovat perusmekanismeja, jotka muuttavat vaaralliset muuttujat injektiovektoreiksi.
  • Lainatkaa jokainen muuttuja kaksinkertaisilla lainausmerkeillä ("$var", "$@", "${arr[@]}") kummankin uhan torjumiseksi.
  • Käyttäkää argumenttien välittämiseen "$@"-muotoa — älkää koskaan käyttäkö lainaamattomia muotoja $@ tai $*.
  • Lisätkää käyttäjän antamien tiedostonimien eteen -- lippuinjektion estämiseksi.
  • Määrittäkää kaikelle ulkoiselle syötteelle sallittu lista säännöllisen lausekkeen tarkistuksella ([[ $v =~ ^pattern$ ]]) ennen kuin syöte päätyy millekään komennolle.
  • Muodostakaa dynaamiset komennot taulukoilla (cmd+=() → "${cmd[@]}"), älkää koskaan merkkijonoja yhdistämällä.
  • Poistakaa käytöstä eval ja bash -c "$input"; käyttäkää ${!varname}-laajennusta turvalliseen epäsuoraan hakuun.
  • Aloittakaa skriptit aina komennoilla set -euo pipefail ja IFS=$'\n\t' turvallisen perustason saavuttamiseksi.

Kun näitä käytäntöjä sovelletaan johdonmukaisesti jokaisen skriptin ensimmäisestä rivistä alkaen, Bashin injektioluokan haavoittuvuuksien hyökkäyspinta pienenee lähes olemattomaksi.

Aloita maksutta

Opi DevOps-bootcamp tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
142
Oppitunnit
568

Usein kysytyt kysymykset

Onko oppitunti ”Komento- ja argumentti-injektioiden estäminen” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa DevOps-bootcamp-oppimispolun 3 oppituntia, myös oppitunnin “Komento- ja argumentti-injektioiden estäminen”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Komento- ja argumentti-injektioiden estäminen”?

Lainaa, validoi ja välitä epäluotettava syöte taulukkoina, jotta estät sanajaon ja eval-pohjaiset injektiot. Harjoittelet DevOps-bootcamp-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni DevOps-bootcamp-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin DevOps-bootcamp-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”Komento- ja argumentti-injektioiden estäminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä DevOps-bootcamp-oppitunnilla?

Kyllä. Jokainen DevOps-bootcamp-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Komento- ja argumentti-injektioiden estäminen
  2. Salaisten tietojen turvallinen käsittely ja ympäristön siisteys
  3. Vähimpien oikeuksien suoritus ja sudo-kuri
  4. Staattinen analyysi ja tarkastus ShellCheckilla
← Takaisin: DevOps-bootcamp