Shell komutunun string değişkeni ile korunması


9

Bir programlama dilinde, basit bir kabuk komutu yürütüyorum

cd var; echo > create_a_file_here

ile var ben dosyası "create_a_file_here" oluşturmak istediğiniz yere (umarım) bir dizinin bir dize içeren bir değişken olma. Birisi bu kod satırını görürse, örneğin atayarak koddan yararlanmak mümkündür:

var = "; rm -rf /"

İşler oldukça çirkinleşebilir. Dizeyi aramak için belki olurdu Yukarıdaki durum önlemek için bir yolu var gibi bazı özel karakterler için ';' kabuk komutunu çalıştırmadan önce, ama bu muhtemelen tüm istismarları kapsar şüpheliyim.

Herkes "cd var" sadece bir dizini değiştirmek ve başka bir şey sağlamak için iyi bir yol biliyor mu?


4
Kabuğu nasıl çağırabileceğinize bağlı varolarak, bir argüman olarak da geçebilirsiniz . Örneğin çağırarak shargümanlarla -c, 'cd "$1"; echo > create_a_file_here', 'sh', varişler ve herhangi bir değişiklik gerekmez var. 'sh'Bağımsız değişken olarak geçirilir $0.
ipsec

1
Hangi programlama dili? Eğer POSIX kullanıyorsunuz shveya benzer bir sözdizimi ile kendi programlama dili oluştururken ama genişlediği varyerine yazmayı gerektiren cd "$var"? Yoksa bashbununla shopt -s cdable_varsmı? Oh, sanırım başka bir program bu komutları çalıştırmak için bir kabuk çatal. Öyleyse sadece alıntı yapın var, ancak bir alıntı karakterinin kendisini içermediğinden emin olun ...
Peter Cordes

@PeterCordes Bash hakkında konuşuyorsanız, çift tırnak içeren çift tırnaklı bir değişken uygundur. örneğin, s='"'; echo "$s"baskılar ".
wjandrea

@WJAndrea: evet, ancak güvenilmeyen girdiden değişken bir atama oluşturulurken yenilmeyen bir "koz" teklifi yok. Oh, çözüm: var=untrusted stringana programda yapın, bu yüzden varçağırırken zaten ayarlanmış bir ortam değişkeni sh. O zaman sadece her genişlettiğinizde alıntı yapmanız gerekir, bu da güvenilir bir şekilde mümkündür. Ah, bu fikrin zaten Stéphane'nin cevabının bir parçası olduğunu görüyorum>. <
Peter Cordes

Yanıtlar:


9

Doğru anlarsam, varprogramlama dilinizde bir değişkendir.

Ve programlama dilinizde, bir kabuktan "cd ", bu değişkenin içeriği olan birleştirme dizesini ve yorumunu yorumlamasını istersiniz "; echo > create_a_file_here".

Eğer öyleyse, evet, içeriği varsıkı bir şekilde kontrol edilmezse , bu bir komut enjeksiyon güvenlik açığıdır.

Değişkenin içeriğini kabuğun sözdiziminde properly deneyebilir ve düzgün bir şekilde alıntılayabilirsiniz, böylece cdyerleşene tek bir argüman olarak iletilmesi garanti edilir .

Başka bir yaklaşım, bu değişkenin içeriğini başka bir şekilde aktarmak olacaktır. Bariz bir yol, bunu bir ortam değişkenine geçirmektir. Örneğin, C dilinde:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Bu kez, kabuktan yorumlamasını istediğiniz kod sabittir, yine de kabuğun sözdiziminde düzgün şekilde yazmamız gerekir (burada POSIX uyumlu bir kabuk olduğu varsayılır):

  • split + glob'u önlemek için kabuk değişken genişlemesi belirtilmelidir
  • İhtiyacınız -Piçin cdbir basit yapmakchdir()
  • başlangıç (veya bazı kabuklarda) ile --ilgili sorunları önlemek için seçeneklerin sonunu işaretlemeniz gerekirvar-+
  • CDPATHOrtamda olması durumunda boş dizgiye ayarladık
  • echoKomutu sadece cdbaşarılı olursa çalıştırırız .

(En az) kalan bir sorun var: eğer öyleyse var, -çağrılan dizine -değil, önceki dizine (depolandığı gibi $OLDPWD) girer ve OLDPWD=- CDPATH= cd -P -- "$DIR"onun etrafında çalışacağı garanti edilmez. Yani şöyle bir şeye ihtiyacınız olacak:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Sadece yapıyor bu Not system(concat("cd \"", var, "\"; echo..."));olduğu değil gitmek için yol, sadece sorunun hareketli olurdu.

Örneğin, var = "$(rm -rf /)"yine de bir sorun olabilir.

Sadece için teklif metnine güvenilir yolu Bourne gibi kabuklarına tek tırnak kullanmaktır ve dizede oluşabilecek tek tırnak özen da. Örneğin, bir dönüş char *var = "ab'cd"için char *escaped_var = "'ab'\\''cd'". Yani, hepsini 'değiştirin '\''ve içindeki her şeyi sarın '...'.

Hala o alıntı dize ters tırnakların içinde kullanılmaz varsayar ve hala ihtiyacım olacağını --, -P, &&, CDPATH=...


12

Basit çözüm: Kabuğu programınızdan aramayın. Hiç.

Buradaki örnek önemsizdir, dizini değiştirmek ve dosya oluşturmak hangi programlama dilinde olursa olsun kolay olmalıdır. Ancak harici bir komut çalıştırmanız gerekse bile, genellikle bunu kabuktan yapmanıza gerek yoktur.

Yani, örneğin Python'da, çalıştırmak yerine os.system("somecmd " + somearg)kullanın subprocess.run(["somecmd", somearg]). In C yerine system()kullanılması, fork()ve exec()(veya öyle bir kütüphane).

Kabuğu kullanmanız gerekiyorsa, Stéphane'nin cevabında olduğu gibi komut satırı argümanlarını alıntılayın veya ortamdan geçirin . Kendinizi özel karakterle endişesi bulursanız Ayrıca, doğru çözüm olduğunu değil , potansiyel olarak tehlikeli karakterler (kara liste) filtrelemek için çalışıyorum, ama sadece tutmak bilinen karakterler güvenli olması için (beyaz liste).

Yalnızca işlevlerini bildiğiniz karakterlere izin verin, bu şekilde bir şey eksik olma riski daha az olur. Nihai sonuç, yalnızca izin vermeye karar vermeniz [a-zA-Z0-9_]olabilir, ancak bu işi yapmak için yeterli olabilir. Ayrıca yerel ayar olmadığını kontrol etmek ve benzeri aksanlı harfleri içermeyen araç grubu isteyebilirsiniz äve öbu içinde. Muhtemelen herhangi bir kabuk tarafından özel olarak kabul edilmezler, ancak yine de geçip geçmediklerinden emin olmak daha iyidir.


1
Karşılaşmak için kullandığınız her [a-zA-Z0-9]şeyin à, kodlaması bashbazı bölgelerde bazı kabuklar (örneğin ) tarafından yanlış yorumlanabilecek gibi şeyler içermediğinden emin olun .
Stéphane Chazelas

@ StéphaneChazelas (ilgisiz :) onlar (ve etkilenen yerlerin hemen altında mı?)
Wilf

10

Bir programlama dilinde, kabuk komutlarını yürütmekten daha iyi şeyler yapmanın yolları olmalıdır. Örneğin, cd varprogramlama dilinizin eşdeğeriyle değiştirmek chdir (var);, değeri olan herhangi bir aldatmanın varistenmeyen ve muhtemelen kötü amaçlı eylemler yerine yalnızca "Dizin bulunamadı" hatasıyla sonuçlanmasını sağlamalıdır .

Ayrıca, dizinleri değiştirmek yerine mutlak yolları da kullanabilirsiniz. Sadece dizin adını, eğik çizgiyi ve kullanmak istediğiniz dosya adını birleştirin.

C, gibi bir şey yapabilirim:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Elbette programlama diliniz benzer bir şey yapabilir mi?


Cevabınız için teşekkürler, ancak maalesef geçici çözümü bash komutlarıyla kullanmak zorundayım. Değişkeni alıntılamayı test ettim - muru'nun önerdiği gibi - ve işe yarıyor gibi görünüyor!
ES___
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.