0Pricing
DevOps Bootcamp · Ders

Komut ve Bağımsız Değişken Enjeksiyonunu Önleme

Güvenilmeyen girdilerde sözcük bölünmesini ve eval tabanlı enjeksiyonu ortadan kaldırmak için alıntılama, doğrulama ve dizi aktarımı kullanın.

Komut ve Bağımsız Değişken Enjeksiyonunu Önleme, CoddyKit'te ücretsiz bir DevOps Bootcamp dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, DevOps Bootcamp öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DevOps Bootcamp kursu toplamda 4 dersten oluşur.

Bash'te Enjeksiyon Saldırıları Neden Gerçekleşir

Bash güçlü bir bağlayıcı dildir; metni doğrudan çekirdeğe, diğer programlara ve alt kabuklara iletir. Güvenilmeyen girdi doğrulama veya tırnak içine alma olmadan bir komuta ulaştığı anda bu güç bir güvenlik açığına dönüşür.

Neredeyse tüm Bash enjeksiyonlarının temelinde iki neden vardır:

  • Sözcük bölme: Tırnak içine alınmamış değişkenler boşluklara (IFS) göre bölünür ve tek bir mantıksal değer birden fazla kabuk belirtecine dönüşür.
  • Glob genişletmesi: *, ? ve [ gibi karakterler, komut çalıştırılmadan önce kabuk tarafından genişletilir.

Bir dosya adını, kullanıcı adını, URL parametresini veya ortam değişkenini kontrol eden saldırgan, bunların her ikisinden de yararlanarak rastgele komutlar çalıştırabilir, dosyaları okuyabilir veya ayrıcalıkları yükseltebilir.

Bu ders, bu güvenlik açıklarının tam olarak nasıl ortaya çıktığını ve daha da önemlisi, doğru tırnak kullanımı, girdi doğrulama ve dizi tabanlı bağımsız değişken iletimiyle nasıl ortadan kaldırılacağını gösterir.

Sözcük Bölme: Sessiz Tehdit

Bash, tırnak içine alınmamış bir değişken gördüğünde değerini $IFS içinde listelenen karakterlere göre böler (varsayılan: boşluk, sekme, satır sonu). Tek bir bağımsız değişken gibi görünen şey birden çok bağımsız değişkene dönüşür.

Aşağıdaki betiği çalıştırın ve içinde boşluk bulunan bir dosya adının rm komutuna gönderilen iki ayrı bağımsız değişkene nasıl dönüştüğünü gözlemleyin.

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

Her Zaman Tırnak Kullanın: Savunmacı Bash'in İlk Kuralı

Sözcük bölmeye karşı en basit ve en etkili savunma, değişken genişletmelerini her zaman çift tırnak içine almaktır.

  • "$var" — tam olarak tek bir belirtece genişler; boşlukları, sekmeleri ve satır sonlarını korur.
  • 'literal' — tek tırnak: hiçbir genişletme yapılmaz; sabit dizeler için kullanışlıdır.
  • Açıkça sözcük bölmeye ve glob genişletmesine ihtiyaç duymadığınız sürece $var ifadesini asla tırnaksız kullanmayın.

Aşağıdaki betik, önceki örneğin güvenli sürümünü gösterir.

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

Glob Enjeksiyonu: * Bir Silaha Dönüştüğünde

Tırnak içine alınmamış değişkenler yol adı genişletmesine (glob işlemine) de tabi olur. Kullanıcı denetimindeki girdi * veya ? içeriyorsa Bash, komut çalışmadan önce bu ifadeyi dosya sistemine göre genişletir.

Klasik bir saldırı vektörü şudur: Bir web formu PATTERN=* değerini ayarlar ve betik cp $PATTERN /tmp/leak/ komutunu çalıştırır; böylece geçerli dizindeki her dosya kopyalanır.

Çözüm aynıdır: değişkeni çift tırnak içine alın. Tırnak içine alınmış "$PATTERN" olduğu gibi iletilir; kabuk bunun üzerinde glob genişletmesi yapmaz.

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

Tırnaksız Konumsal Parametrelerle Bağımsız Değişken Enjeksiyonu

Çağırandan bağımsız değişken kabul eden betikler enjeksiyon için başlıca hedeflerdir. Her konumsal parametre ($1, $2, ...) kullanıldığı her yerde tırnak içine alınmalıdır.

Özellikle tehlikeli bir kalıp, $@ veya $* ifadelerini başka bir komuta tırnaksız iletmektir:

  • "$@" — her konumsal parametreyi ayrı ve kendi içinde tırnak içine alınmış bir sözcük olarak genişletir. Her zaman bu biçimi kullanın.
  • Tırnaksız $@ veya $* — sözcük bölmeye ve glob işlemine tabidir.
  • "$*" — tüm parametreleri tek bir sözcükte birleştirir (nadiren istediğiniz şey budur).
#!/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"

eval ve Doğrulanmamış Girdi Yoluyla Komut Enjeksiyonu

eval, bağımsız değişkenini kabuk kodu olarak yeniden ayrıştırır. eval'e ulaşan tüm güvenilmeyen veriler rastgele komutlar çalıştırabilir.

Yaygın tehlikeli kalıplar:

  • eval "$user_input"
  • eval echo \$$var (dolaylı değişken araması)
  • Kullanıcı verilerini bash -c "$input" üzerinden geçirmek

Kural: Güvenilmeyen girdiyi asla eval veya bash -c'ye geçirmeyin. Güvenli Bash seçeneklerini kullanın:

  • Dolaylı genişletme: eval echo \$$varname yerine ${!varname}
  • Dinamik anahtar-değer araması için ilişkisel diziler
  • Oluşturulmuş komut dizeleri yerine işlevler
#!/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

Girdi Doğrulama: Reddetme Listeleri Yerine İzin Listeleri

Bilinen kötü karakterleri reddetmek (bir reddetme listesi) kırılgandır; saldırganlar unuttuğunuz kodlamaları veya karakterleri bulabilir. Bunun yerine izin listesi kullanın: yalnızca güvenli olduğunu bildiğiniz karakterleri kabul edin.

Bash'te izin listesi stratejileri:

  • Düzenli ifade eşleşmesi: [[ "$input" =~ ^[A-Za-z0-9_-]+$ ]]
  • Kalıp eşleşmesi: case "$input" in [A-Za-z0-9]*) ... ;; esac
  • Numaralandırma denetimi: sabit bir geçerli değer kümesiyle karşılaştırma yapın

Girdiyi sınırda, yani betiğe girer girmez ve herhangi bir komuta ulaşmadan önce doğrulayın.

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

Bağımsız Değişkenleri Güvenle İletmek İçin Dizileri Kullanma

Koşula bağlı olarak seçenekler eklemek veya girdiler üzerinde döngü kurmak gibi bir komutu dinamik olarak oluşturmanız gerektiğinde, dizeleri birleştirmek yerine Bash dizisi kullanın.

Dizelerin birleştirilmesi tüm yapıyı ortadan kaldırır; Bash dizisi ise her bağımsız değişkeni kabuk tarafından yeniden ayrıştırılmayan ayrı bir öğe olarak korur.

  • Tanımlama: args=()
  • Ekleme: args+=(--flag "$value")
  • Çalıştırma: command "${args[@]}"

"${args[@]}", her öğeyi "$@" ile tamamen aynı şekilde ayrı ve kendi içinde tırnak içine alınmış bir sözcük olarak genişletir.

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

-- Ayıracı: Seçenek Enjeksiyonuna Karşı Koruma

Doğru şekilde tırnak içine alınmış bir bağımsız değişken bile - ile başlıyorsa seçenek olarak yanlış yorumlanabilir. file='-rf .' iken rm "$file" ifadesini düşünün: tırnak içine alma sözcük bölmeyi önler, ancak rm yine de -rf ifadesini seçenek olarak yorumlar.

POSIX kuralı olan --, çoğu GNU/BSD yardımcı programına seçeneklerin sonunu bildirir. -- sonrasındaki her şey seçenek olarak değil, konumsal bağımsız değişken olarak ele alınır.

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

SQL ve Harici Araçlar İçin Girdiyi Temizleme

Bash betikleri veritabanı komut satırı istemcilerini (psql, mysql), kullanıcı tarafından sağlanan URL'lerle curl'ü veya benzer araçları çağırdığında iki ek katman uygulanır:

  • Parametreli sorgular: Kullanıcı verilerini SQL dizelerine asla yerleştirmeyin. Değerleri psql içinde -v, curl içinde --data-urlencode aracılığıyla geçirin.
  • Veriyi koddan ayırın: Değişmez bir biçim dizesiyle printf kullanın; kullanıcı girdisinin biçim dizesi olmasına asla izin vermeyin.

Aşağıdaki örnek, kullanıcı tarafından sağlanan değeri SQL metninin tamamen dışında tutarak PostgreSQL'i güvenli şekilde sorgular.

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

Güçlendirme Denetim Listesi: Her Şeyi Bir Araya Getirme

Üretim kalitesinde güvenli bir Bash betiği, bu dersteki tüm teknikleri tutarlı ve katmanlı bir savunmada birleştirir. Aşağıda asgari ancak eksiksiz bir güçlendirilmiş şablon yer alıyor:

  • set -euo pipefail — hatada çıkın, atanmamış değişkenleri hata olarak ele alın ve işlem hattı hatalarını aktarın.
  • Girişte doğrulayın — tüm harici girdileri herhangi bir komuta ulaşmadan önce izin listesiyle denetleyin.
  • Her şeyi tırnak içine alın — bölmeye ihtiyaç duymadığınız sürece istisna yok: "$var", "$@", "${array[@]}".
  • Dinamik komut oluşturmak için tabloları kullanın.
  • Kullanıcı tarafından sağlanan dosya adlarını veya dizeleri geçirirken bağımsız değişkenlerin başına -- ekleyin.
  • Güvenilmeyen verilerle asla eval kullanmayın; dolaylı arama için ${!var} kullanmayı tercih edin.
  • İzinleri kısıtlayın — betikleri gereken en düşük ayrıcalıklarla çalıştırın; kullanıcı girdisi kabul eden betiklerin içinde sudo kullanmaktan kaçının.
#!/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[@]}"

Bilgi Denetimi: Tırnak Kullanımı ve Enjeksiyonu Önleme

Bu dersteki temel kavramları anlayıp anlamadığınızı sınayın.

Ders Özeti: Komut ve Bağımsız Değişken Enjeksiyonunu Önleme

Güvenli Bash girdi işleme için eksiksiz savunma araç setini ele aldınız:

  • Sözcük bölme ve glob genişletmesi, güvenli olmayan değişkenleri enjeksiyon vektörlerine dönüştüren temel mekanizmalardır.
  • Her iki tehdidi de bastırmak için her değişkeni çift tırnak içine alın ("$var", "$@", "${arr[@]}").
  • Bağımsız değişkenleri iletirken "$@" kullanın; tırnaksız $@ veya $* ifadelerini asla kullanmayın.
  • Seçenek enjeksiyonunu önlemek için kullanıcı tarafından sağlanan dosya adlarının başına -- ekleyin.
  • Tüm harici girdiler için herhangi bir komuta ulaşmadan önce düzenli ifade korumasıyla ([[ $v =~ ^pattern$ ]]) izin listesi uygulayın.
  • Dinamik komutları dize birleştirme yerine dizilerle oluşturun (cmd+=() → "${cmd[@]}").
  • eval ve bash -c "$input" kullanımlarını ortadan kaldırın; güvenli dolaylı genişletme için ${!varname} kullanın.
  • Güçlendirilmiş bir temel yapı için betikleri her zaman set -euo pipefail ve IFS=$'\n\t' ile başlatın.

Her betiğin ilk satırından itibaren tutarlı şekilde uygulanan bu yöntemler, enjeksiyon sınıfı güvenlik açıkları açısından Bash'in saldırı yüzeyini neredeyse sıfıra indirir.

Sıkça Sorulan Sorular

“Komut ve Bağımsız Değişken Enjeksiyonunu Önleme” dersi ücretsiz mi?

Evet — “Komut ve Bağımsız Değişken Enjeksiyonunu Önleme” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve DevOps Bootcamp kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DevOps Bootcamp kursu toplamda 4 dersten oluşur.

“Komut ve Bağımsız Değişken Enjeksiyonunu Önleme” dersinde ne öğreneceğim?

Güvenilmeyen girdilerde sözcük bölünmesini ve eval tabanlı enjeksiyonu ortadan kaldırmak için alıntılama, doğrulama ve dizi aktarımı kullanın. DevOps Bootcamp ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

DevOps Bootcamp öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te DevOps Bootcamp, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.

“Komut ve Bağımsız Değişken Enjeksiyonunu Önleme” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu DevOps Bootcamp dersinde kod yazıp çalıştırabilir miyim?

Evet. Her DevOps Bootcamp dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Komut ve Bağımsız Değişken Enjeksiyonunu Önleme
  2. Güvenli Gizli Bilgi Yönetimi ve Ortam Hijyeni
  3. En Az Ayrıcalıklı Çalıştırma ve sudo Disiplini
  4. ShellCheck ile Statik Analiz ve Denetim
← DevOps Bootcamp Sayfasına Dön