Dari manual findutils:
Misalnya membangun seperti dua perintah ini
# risky find -exec sh -c "something {}" \; find -execdir sh -c "something {}" \;
sangat berbahaya. Alasan untuk ini adalah bahwa '{}' diperluas ke nama file yang mungkin mengandung titik koma atau karakter lain yang khusus untuk shell. Jika misalnya seseorang membuat file
/tmp/foo; rm -rf $HOME
maka dua perintah di atas dapat menghapus direktori home seseorang.Jadi untuk alasan ini, jangan jalankan perintah apa pun yang akan meneruskan data yang tidak dipercaya (seperti nama file) ke perintah yang menafsirkan argumen sebagai perintah yang akan ditafsirkan lebih lanjut (misalnya 'sh').
Dalam kasus shell, ada solusi cerdas untuk masalah ini:
# safer find -exec sh -c 'something "$@"' sh {} \; find -execdir sh -c 'something "$@"' sh {} \;
Pendekatan ini tidak dijamin untuk menghindari setiap masalah, tetapi jauh lebih aman daripada mengganti data pilihan penyerang ke dalam teks perintah shell.
- Apakah penyebab masalah
find -exec sh -c "something {}" \;
adalah penggantian untuk{}
tidak dikutip dan karenanya tidak diperlakukan sebagai string tunggal? Dalam solusinya
find -exec sh -c 'something "$@"' sh {} \;
,pertama
{}
diganti, tetapi karena{}
tidak dikutip, tidak"$@"
juga memiliki masalah yang sama dengan perintah aslinya? Sebagai contoh,"$@"
akan diperluas untuk"/tmp/foo;"
,"rm"
,"-rf"
, dan"$HOME"
?mengapa
{}
tidak luput atau dikutip?
- Bisakah Anda memberikan contoh lain (masih dengan
sh -c
, atau tanpa itu jika berlaku; dengan atau tanpafind
yang mungkin tidak perlu) di mana masalah dan solusi yang sama berlaku, dan yang merupakan contoh minimal sehingga kita dapat fokus pada masalah dan solusi dengan sedikit gangguan mungkin? Lihat Cara untuk memberikan argumen pada perintah yang dijalankan oleh `bash -c`
Terima kasih.
Jawaban:
Ini tidak benar-benar terkait dengan mengutip, melainkan pada pemrosesan argumen.
Perhatikan contoh berisiko:
Hal ini diurai oleh shell, dan dibagi menjadi enam kata:
find
,-exec
,sh
,-c
,something {}
(tanpa tanda kutip lagi),;
. Tidak ada yang berkembang. Shell berjalanfind
dengan enam kata tersebut sebagai argumen.Ketika
find
temuan sesuatu untuk proses, katakanlahfoo; rm -rf $HOME
, itu menggantikan{}
denganfoo; rm -rf $HOME
, dan berjalansh
dengan argumensh
,-c
dansomething foo; rm -rf $HOME
.sh
sekarang melihat-c
, dan sebagai hasilnya mem-parsingsomething foo; rm -rf $HOME
( argumen non-opsi pertama ) dan mengeksekusi hasilnya.Sekarang pertimbangkan varian yang lebih aman:
Shell berjalan
find
dengan argumenfind
,-exec
,sh
,-c
,something "$@"
,sh
,{}
,;
.Sekarang ketika
find
temuanfoo; rm -rf $HOME
, itu menggantikan{}
lagi, dan berjalansh
dengan argumensh
,-c
,something "$@"
,sh
,foo; rm -rf $HOME
.sh
melihat-c
, dan mem-parsingsomething "$@"
sebagai perintah untuk menjalankan, dansh
danfoo; rm -rf $HOME
sebagai parameter posisi ( mulai dari$0
), diperluas"$@"
menjadifoo; rm -rf $HOME
sebagai nilai tunggal , dan berjalansomething
dengan argumen tunggalfoo; rm -rf $HOME
.Anda dapat melihat ini dengan menggunakan
printf
. Buat direktori baru, masukkan, dan jalankanMenjalankan varian pertama sebagai berikut
menghasilkan
sedangkan varian kedua, jalankan sebagai
menghasilkan
sumber
{}
tidak luput atau dikutip?;
dan{}
) harus diloloskan (dengan '\') atau dikutip untuk melindungi mereka dari ekspansi oleh shell." Apakah maksud Anda bahwa itu salah untuk bash?findutils
buku pedoman itu paling tidak berasal dari tahun 1996 ... Tentu saja tidak ada salahnya mengutip{}
, baik sebagai'{}'
atau"{}"
, tetapi tidak perlu.find some/path -exec sh -c 'something "$@"' {} \;
sebenarnya ada tiga lapisan pemrosesan argumen / lewat, dua dari variasi shell dan satu dari variasi sistem operasi baku dasar.Bagian 1:
find
hanya menggunakan penggantian teks.Ya, jika Anda tidak mengutipnya, seperti ini:
dan seorang penyerang bisa membuat file bernama
; echo owned
, maka itu akan execyang akan menghasilkan shell berjalan
echo
kemudianecho owned
.Tetapi jika Anda menambahkan tanda kutip, penyerang bisa mengakhiri tanda kutip Anda kemudian menempatkan perintah jahat setelah itu dengan membuat file bernama
'; echo owned
:yang akan menghasilkan shell berjalan
echo ''
,echo owned
.(jika Anda menukar tanda kutip ganda untuk tanda kutip tunggal, penyerang bisa menggunakan jenis tanda kutip lainnya juga.)
Bagian 2:
Dalam
find -exec sh -c 'something "$@"' sh {} \;
,{}
awalnya tidak ditafsirkan oleh shell, itu dieksekusi langsung denganexecve
, jadi menambahkan kutipan shell tidak akan membantu.tidak berpengaruh, karena shell menghapus tanda kutip ganda sebelum dijalankan
find
.menambahkan kutipan yang shell tidak memperlakukan secara khusus, jadi dalam kebanyakan kasus itu hanya berarti perintah tidak akan melakukan apa yang Anda inginkan.
Setelah itu memperluas untuk
/tmp/foo;
,rm
,-rf
,$HOME
seharusnya tidak menjadi masalah, karena mereka adalah argumen untuksomething
, dansomething
mungkin tidak memperlakukan argumen sebagai perintah untuk mengeksekusi.Bagian 3:
Saya menganggap pertimbangan serupa berlaku untuk apa pun yang mengambil input yang tidak dipercaya dan menjalankannya sebagai (bagian dari) perintah, misalnya
xargs
danparallel
.sumber
xargs
hanya berbahaya jika-I
atau-J
digunakan. Dalam operasi normal itu hanya menambahkan argumen ke bagian akhir daftar, seperti-exec ... {} +
halnya.parallel
Sebaliknya, menjalankan shell secara default tanpa permintaan eksplisit dari pengguna, meningkatkan permukaan yang rentan; ini adalah masalah yang menjadi subjek banyak diskusi; lihat unix.stackexchange.com/questions/349483/… untuk penjelasan, dan tautan yang disertakan ke lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).parallel
"penerima" jarak jauh di ujung soket apa pun, dapat melakukan shell-less langsungexecv
. Yang persis apa yang akan saya lakukan, jika di sepatu Anda menerapkan alat. Memiliki program noninteraktif berperilaku berbeda berdasarkan nilai saat iniSHELL
adalah yang paling tidak mengejutkan.execv
disediakan, itu lebih sederhana dan tidak mengherankan, dan aturan yang tidak berubah di seluruh lokasi / lingkungan runtime.Dalam arti tertentu, tetapi mengutip tidak dapat membantu di sini . Nama file yang diganti
{}
bisa berisi karakter apa saja , termasuk tanda kutip . Apa pun bentuk kutipan yang digunakan, nama file dapat berisi hal yang sama, dan "keluar" dari kutipan.Tidak.
"$@"
Perluas parameter posisi, sebagai kata yang terpisah, dan tidak membaginya lebih lanjut. Di sini,{}
ada argumen untukfind
dirinya sendiri, danfind
meneruskan nama file saat ini juga sebagai argumen berbedash
. Ini langsung tersedia sebagai variabel dalam skrip shell, itu tidak diproses sebagai perintah shell itu sendiri.Tidak harus seperti itu, pada kebanyakan shell. Jika Anda menjalankan
fish
, itu harus:fish -c 'echo {}'
mencetak baris kosong. Tetapi tidak masalah jika Anda mengutipnya, shell hanya akan menghapus tanda kutip.Setiap kali Anda memperluas nama file (atau string yang tidak terkontrol) apa adanya di dalam string yang diambil sebagai semacam kode (*) , ada kemungkinan eksekusi perintah sewenang-wenang.
Misalnya, ini mengembang
$f
secara langsung kode Perl, dan akan menyebabkan masalah jika nama file berisi kutipan ganda. Kutipan dalam nama file akan mengakhiri kutipan dalam kode Perl, dan sisa nama file dapat berisi kode Perl:(Nama file harus sedikit aneh karena Perl mem-parsing seluruh kode di depan, sebelum menjalankannya. Jadi kita harus menghindari kesalahan parse.)
Meskipun ini lolos dengan aman melalui argumen:
(Tidak masuk akal untuk menjalankan shell lain langsung dari shell, tetapi jika Anda melakukannya, mirip dengan
find -exec sh ...
case)(* beberapa jenis kode termasuk SQL, jadi XKCD wajib: https://xkcd.com/327/ plus penjelasan: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )
sumber
Kekhawatiran Anda justru menjadi alasan mengapa input kutipan Paralel GNU:
Ini tidak akan berjalan
echo pwned
.Ini akan menjalankan shell, sehingga jika Anda memperpanjang perintah Anda, Anda tidak akan tiba-tiba mendapatkan kejutan:
Untuk perincian lebih lanjut tentang masalah pemijahan-kulit, lihat: https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell
sumber
find . -print0 | xargs -0 printf "Argument: %s\n"
sama aman (atau lebih tepatnya, lebih, karena dengan-print0
satu menangani nama file dengan baris baru dengan benar); mengutip paralel adalah solusi untuk masalah yang tidak ada sama sekali ketika tidak ada shell hadir.| wc
perintah Anda harus melompat melalui lingkaran untuk membuatnya aman. GNU Parallel aman secara default.foo {} | wc
menjadish -c 'foo "$1" | wc
sh {} `, itu mungkin berbeda.