Czy uruchamianie echa bez cudzysłowów jest niebezpieczne?

11

Widziałem kilka podobnych tematów, ale dotyczą one nie cytowania zmiennych, które, jak wiem, mogą prowadzić do niepożądanych wyników.

Widziałem ten kod i zastanawiałem się, czy byłoby możliwe wstrzyknięcie czegoś do uruchomienia po wykonaniu tej linii kodu:

echo run after_bundle

Viktor Fonic
źródło
Natknąłem się na to, gdy miałem: target = "*** LIVE SERVER ***"; echo target: $ target; a *** rozwinął się do listy folderów ... 😬
Matt Parkins

Odpowiedzi:

17

W konkretnym przypadku

echo run after_bundle

cytowanie nie jest potrzebne. Nie jest potrzebne cytowanie, ponieważ argumentem echosą ciągi statyczne, które nie zawierają żadnych rozszerzeń zmiennych ani podstawień poleceń itp. Są to „tylko dwa słowa” (i jak podkreśla Stéphane , są one dodatkowo zbudowane z przenośnego zestawu znaków ).

„Niebezpieczeństwo” pojawia się, gdy mamy do czynienia ze zmiennymi danymi, które powłoka może rozszerzać lub interpretować. W takich przypadkach należy uważać, aby powłoka działała prawidłowo i aby wynik był zgodny z przeznaczeniem.

Poniższe dwa pytania zawierają odpowiednie informacje na ten temat:


echojest czasem używany do „ochrony” potencjalnie szkodliwych poleceń w odpowiedziach na tej stronie. Na przykład mogę pokazać, jak usunąć pliki lub przenieść pliki do nowego miejsca docelowego za pomocą

echo rm "${name##*/}.txt"

lub

echo mv "$name" "/new_dir/$newname"

Spowoduje to wyprowadzenie poleceń na terminal zamiast faktycznego usuwania lub zmiany nazw plików. Użytkownik może następnie sprawdzić polecenia, zdecydować, że wyglądają dobrze, usunąć echoi uruchomić ponownie.

Twoje polecenie echo run after_bundlemoże być instrukcją dla użytkownika lub może być „zakomentowanym” fragmentem kodu, który jest zbyt niebezpieczny, aby działał bez znajomości konsekwencji.

Używając w echoten sposób, musisz wiedzieć, co robi zmodyfikowane polecenie i musisz zagwarantować, że zmodyfikowane polecenie jest w rzeczywistości bezpieczne (potencjalnie nie byłoby, gdyby zawierało przekierowania, a używanie go w potoku nie działa itp.)

Kusalananda
źródło
Dodanie cudzysłowów nie wystarcza jednak, aby wiedzieć, co zrobiłaby powłoka - podobnie jak nie można powiedzieć, że echo rm "first file.txt" "second file.txt"w jakikolwiek sposób różni się echo rm "first" "file.txt" "second" "file.txt", wynik obu jest taki sam. Jeśli chcesz wygenerować polecenie powłoki jako dane wyjściowe, musisz użyć printf '%q ' rm "first file.txt" "second file.txt"; echolub czegoś równoważnego, który ponownie wygeneruje cytowanie składniowe, które zostanie ocenione jako argvprzekazane.
Charles Duffy
@CharlesDuffy Naprawdę mam nadzieję, że nikt nie skopiuje wyników debugowania i wklei je w powłoce!
Kusalananda
1
Generowanie poleceń powłoki, a następnie ich przesyłanie do shpotoku nie jest czymś niezwykłym, i widząc, że ludzie pytają „dlaczego foodziała, kiedy uruchamiam go w wierszu poleceń, ale ten skrypt, który emituje ten ciąg znaków echoprzed wierszem, nie działa? „ zdarza się tutaj cały czas . Co więcej, wynik debugowania nie jest pomocny, jeśli ukrywa twoje błędy - a jeśli twoje błędy są związane z cytowaniem, to echoich nie ujawni.
Charles Duffy
27

Tylko dodatkowa uwaga na podstawie dobrej odpowiedzi @ Kusalananda .

echo run after_bundle

jest w porządku, ponieważ żaden z tych 3 argumentów¹ nie przekazał echoznaków specjalnych dla powłoki.

I (dodatkowy punkt, o którym chcę tu wspomnieć) nie ma ustawień regionalnych systemu, w których bajty te mogłyby zostać przetłumaczone na znaki specjalne dla powłoki.

Wszystkie te znaki są w tym, co POSIX nazywa przenośnym zestawem znaków . Znaki te powinny być obecne i zakodowane tak samo we wszystkich zestawach znaków w systemie POSIX².

Tak więc wiersz poleceń będzie interpretowany tak samo niezależnie od ustawień regionalnych.

Teraz, jeśli zaczniemy używać znaków spoza tego przenośnego zestawu znaków, dobrze jest zacytować je, nawet jeśli nie są one specjalne dla powłoki, ponieważ w innym ustawieniu narodowym bajty, które je tworzą, mogą być interpretowane jako różne znaki, które mogłyby specjalne dla powłoki. Zauważ, że chodzi o to, czy używasz echoczy jakiejkolwiek innej komendy, problem nie dotyczy, echoale sposobu, w jaki powłoka analizuje swój kod.

Na przykład w UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

To àjest zakodowane jako 0xc3 0xa0. Teraz, jeśli masz ten wiersz kodu w skrypcie powłoki, a skrypt powłoki jest wywoływany przez użytkownika, który używa ustawień regionalnych, których zestaw znaków nie jest UTF-8, te dwa bajty mogą tworzyć bardzo różne znaki.

Na przykład w fr_FR.ISO8859-15ustawieniach narodowych typowe ustawienia francuskie używające standardowego jednobajtowego zestawu znaków, który obejmuje język francuski (taki sam, jaki stosuje się w większości języków Europy Zachodniej, w tym angielskiego), bajt 0xc3 jest interpretowany jako Ãznak, a 0xa0 jako inny niż łamanie spacji.

W niektórych systemach, takich jak NetBSD³, to nieprzerwane miejsce jest traktowane jako pusty znak ( isblank()po zwróceniu wartości true, jest dopasowywany [[:blank:]]) i powłoki w ten bashsposób traktują go jako ogranicznik tokenów w swojej składni.

Oznacza to, że zamiast uruchamiać echoz $'voil\xc3\xa0'argumentem, uruchamiają go z $'voil\xc3'argumentem, co oznacza, że ​​nie zostanie wydrukowany voilàpoprawnie.

To staje się dużo gorzej z chińskich zestawów znaków, takich jak BIG5, BIG5-HKSCS, GB18030, GBK, które mają wiele postaci, których kodowanie zawiera tego samego kodowania jak |, `, \(aby wymienić najgorszy) (również ten śmieszny SJIS, aka Microsoft Kanji, z wyjątkiem że jest ¥zamiast \, ale nadal jest traktowany jak \większość narzędzi, ponieważ jest tam zakodowany jako 0x5c).

Na przykład, jeśli w zh_CN.gb18030chińskim języku, piszesz skrypt taki jak:

echo  reboot

Skrypt ten wyświetli się 詜 rebootw lokalizacji używającej GB18030 lub GBK, 唰 rebootw lokalizacji używającej BIG5 lub BIG5-HKSCS, ale w lokalizacji C używającej ASCII lub lokalizacji używającej ISO8859-15 lub UTF-8, spowoduje rebooturuchomienie, ponieważ kodowanie GB18030 of to 0xd4 0x7c, a 0x7c to kodowanie |w ASCII, więc kończymy:

 echo �| reboot

(ten reprezentujący jednak bajt 0xd4 jest renderowany w ustawieniach regionalnych). Przykład użycia mniej szkodliwego unamezamiast reboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

( unamebył prowadzony).

Tak więc radzę zacytować wszystkie ciągi zawierające znaki spoza przenośnego zestawu znaków.

Należy jednak pamiętać, że ponieważ kodowanie \i `znajdują się w kodowaniu niektórych z tych znaków, lepiej nie używać \lub "..."lub $'...'(wewnątrz których `i / lub \nadal są specjalne), a '...'zamiast tego cytować znaki spoza przenośnego zestawu znaków.

Nie znam żadnego systemu, który ma ustawienia narodowe, w których zestaw znaków ma dowolny znak ( 'oczywiście inny niż sam), którego kodowanie zawiera kodowanie ', więc te '...'powinny być zdecydowanie najbezpieczniejsze.

Zauważ, że kilka powłok obsługuje również $'\uXXXX'zapis do wyrażania znaków na podstawie ich punktu kodowego Unicode. W powłokach takich jak zshi bashznak jest wstawiany zakodowany w zestawie znaków regionu (chociaż może powodować nieoczekiwane zachowania, jeśli ten zestaw znaków nie ma tego znaku). Pozwala to uniknąć wstawiania znaków innych niż ASCII do kodu powłoki.

Tak więc powyżej:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

Lub:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(z zastrzeżeniem może on uszkodzić skrypt, gdy zostanie uruchomiony w lokalizacjach, które nie mają tych znaków).

Lub lepiej, ponieważ \jest również specjalny dla echo(lub przynajmniej niektórych echo implementacji, przynajmniej tych zgodnych z Uniksem):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(zauważ, że \jest to również specjalne w pierwszym argumencie do printf, więc lepiej jest unikać znaków spoza ASCII, na wypadek, gdyby mogły zawierać kodowanie \).

Pamiętaj, że możesz także:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(byłoby to przesadą, ale mogłoby ci dać spokój, jeśli nie jesteś pewien, które postacie znajdują się w przenośnym zestawie znaków)

Upewnij się także, aby nigdy nie używać starożytnej `...`formy zastępowania poleceń (która wprowadza kolejny poziom przetwarzania odwrotnego ukośnika), ale używaj $(...)zamiast tego.


¹ technicznie, echojest również przekazywana jako argument do echonarzędzia (aby poinformować go, jak został wywołany), to jest to argv[0]i argcto 3, chociaż w większości powłok obecnie echojest wbudowane, tak że exec()z /bin/echopliku z listy 3 argumentów jest symulowane przez muszla. Często uważa się, że lista argumentów zaczyna się od drugiej ( argv[1]do argv[argc - 1]), ponieważ to na nią głównie działają polecenia.

² znaczący wyjątek od tej niedorzecznej ja_JP.SJISlokalizacji systemów FreeBSD, których zestaw znaków nie ma \ani ~charakteru!

³ zauważ, że podczas gdy wiele systemów (FreeBSD, Solaris, a nie GNU) uważa U + 00A0 za lokalizację [[:blank:]]w ustawieniach UTF-8, nieliczne robią to w innych ustawieniach, takich jak ISO8859-15, być może w celu uniknięcia tego rodzaju problemów.

Stéphane Chazelas
źródło
W pierwszym akapicie powiesz nam „... o znakach z tych 3 argumentów przekazanych do echo...”, liczę tylko 2 argumenty przekazane do polecenia echo, argumenty, które mogę policzyć, runi after_bundle, staram się wyjaśnić, jak policzyłem i dostałem 3 argumenty?
Ferrybig,
1
@ViktorFonic, zobacz edycję o liczbie argumentów (i że główny problem nie występuje echo). Zobacz (exec -a foo /bin/echo --help)w systemie GNU i powłoce GNU, jak przekazać arbitralny pierwszy argument do /bin/echonarzędzia.
Stéphane Chazelas
@Ferrybig Zobacz edycję Stephane'a, przypis 1. Argumenty do polecenia w zwykłym stylu C to tablica argumentów, przy czym argv [0] jest samą nazwą pliku wykonywalnego. Trochę podobne $0i parametry pozycyjne w powłokach.
Sergiy Kolodyazhnyy
Istnieje 373 kodowania, iconvw których ESCjest konwertowany na '. Spróbuj (jako przykład):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
Izaak
Istnieje 173 kodowania, w których niektóre punkty kodowe (inne niż ESC) są konwertowane na a '. Spróbować printf '\u2804' | iconv -f utf8 -t BRF | xxd. Istnieją kodowania, w których powstaje wiele współrzędnych kodowych '. Staje się około 8695 współrzędnych kodowych w UCS-4 '. Spróbować printf '\U627' | iconv -cf utf-8 -t UCS-4. Kilka (37) kodowań przekształca znak 0x127 na a '. Spróbujprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
Izaak