Jaka jest różnica między `curl | sh` i `sh -c„ $ (curl) ”`?


23

Jedną z metod łatwej instalacji dla Dockera (na przykład) jest:

curl -sSL https://get.docker.com/ | sh

Jednak widziałem również niektóre, które wyglądają tak (na przykładzie Docker):

sh -c "$(curl -sSL https://get.docker.com/)"

Wydają się być funkcjonalnie takie same, ale czy istnieje powód, aby używać jednego nad drugim? Czy to tylko preferencja / estetyka?

(Uwaga: należy zachować ostrożność podczas uruchamiania skryptu z nieznanego źródła.)

Odpowiedzi:


40

Istnieje praktyczna różnica.

curl -sSL https://get.docker.com/ | shrozpoczyna się curli shjednocześnie łączy wyjście curlz wejściem sh. curlrozpocznie się pobieranie (z grubsza) tak szybko, jak shmożna uruchomić skrypt. Serwer może wykryć nieprawidłowości w czasie i wstrzyknąć szkodliwy kod niewidoczny po prostu pobierając zasób do pliku lub bufora lub przeglądając go w przeglądarce.

W sh -c "$(curl -sSL https://get.docker.com/)", curlprowadzony jest ściśle zanim shzostanie uruchomiona. Cała zawartość zasobu jest pobierana i przekazywana do powłoki przed shuruchomieniem. Twoja powłoka uruchamia się dopiero shpo curlwyjściu i przekazuje do niej tekst zasobu. Serwer nie może wykryć shpołączenia; jest uruchamiany dopiero po zakończeniu połączenia. Jest to podobne do pobrania najpierw skryptu do pliku.

(Może to nie mieć znaczenia w przypadku dokera, ale może to być ogólnie problem i podkreśla praktyczną różnicę między tymi dwoma poleceniami.)


2
Dzięki, wydaje się, że to najważniejsza różnica między nimi.
Sarke

Czy mógłbyś zacytować zasób wspierający to roszczenie? Byłbym bardzo zainteresowany wiedzą, w jaki sposób serwer może wykryć połączenie „sh”.
Alfred Armstrong,

1
@AlfredArmstrong Uhm, umieściłem link do vulnerable to server-side detectionfrazy. Prowadzi do postu na blogu, który szczegółowo wyjaśnia, jak to osiągnąć. TL; DR: prześpij się w swoim skrypcie i obserwuj opóźnienie w odbiorze na serwerze.
Jonas Schäfer

1
@JonasWielicki dzięki - link nie był bardzo jasny - nie twoja wina, chyba CSS SE. Ludzie są wspaniale podstępni, prawda? :)
Alfred Armstrong

1
Wszystko to prowadzi mnie do zastanowienia się, czy ktokolwiek kiedykolwiek próbował wykonać tę sztuczkę w rzeczywistości bez natychmiastowego złapania. Nie znaczy to, że prawdopodobnie ma to duże znaczenie, ponieważ prawdopodobnie po prostu skrypt może zrobić coś złośliwego nawet bez takich sztuczek, a każdy, kto nie przeczyta go w całości, będzie narażony na atak.
ilkkachu

11

Uważam, że są praktycznie identyczne. Są jednak rzadkie przypadki, w których są różne.

$(cmd)zostaje zastąpiony wynikami cmd. Jeśli długość tego polecenia wyniku przekracza maksymalną wartość długości argumentu zwróconą przez getconf ARG_MAX, spowoduje obcięcie wyniku, co może skutkować nieprzewidywalnymi wynikami.

Opcja potoku nie ma tego ograniczenia. Każdy wiersz wyniku curlpolecenia zostanie wykonany po bashnadejściu z potoku.

Ale ARG_MAX ma zwykle zakres 256 000 znaków. W przypadku instalacji dokera jestem pewien, że skorzystam z dowolnej metody. :-)


Ciekawe, więc jest różnica. Dzięki
Sarke

1
W razie wątpliwości użyj metody potokowej. W odpowiedzi nie określiłem preferencji, ale wolę metodę potoku do tego rodzaju zastosowań, ponieważ nigdy nie wiadomo, ile danych przechodzi przez potok.
Greg Tarsa

2
„obetnie wynik” - Powłoka powinna wysyłać komunikat o błędzie, a nie dyskretnie obcinać. Podczas testowania nawet otrzymuję błąd z powłoki daleko poniżej ARG_MAX, bash ogranicza pojedynczy argument do 131072 bajtów w moim systemie podczas getconf ARG_MAXdrukowania 2097152. Ale tak czy inaczej, błąd lub obcięcie, to nie zadziałałoby.
hvd

Ale stare implementacje powłoki Bourne'a miały znacznie niższe limity. W 4.2BSD limit wynosił 10240 znaków, a we wcześniejszych systemach był jeszcze niższy. Oczywiście, że było to 30 lat temu, więc dzisiaj raczej nie spotkasz się z tak niskimi limitami. Jeśli dobrze pamiętam, niektóre z tych wczesnych pocisków po prostu dyskretnie się obciąły.
AndyB

Limit 128 kB dla pojedynczego argumentu to kwestia Linuksa, nie chodzi o Bash.
ilkkachu

8

W curl -sSL https://get.docker.com/ | sh:

  • Oba polecenia curli shbędą uruchamiane jednocześnie w odpowiednich podpowłokach

  • STDOUT z curlzostanie przekazany jako STDIN do sh(to robi rura |, robi)

Natomiast w sh -c "$(curl -sSL https://get.docker.com/)":

  • Podstawienie polecenia $(), zostanie wykonane jako pierwsze, tzn. Najpierw curlzostanie uruchomione w podpowłoce

  • Podstawienie polecenia $(), zostanie zastąpione przez STDOUT zcurl

  • sh -c (nieinteraktywna powłoka niezalogowana) wykona STDOUT z curl


1
Czy jest jakaś różnica?
Sarke

@Sarke Tak, teoretycznie tak jak wspomniałem, ale praktycznie nie zauważalne. (Byłby widoczny efekt, jeśli nie podasz zamiany poleceń).
heemayl

1
@Sarke, jeśli skrypty nie zostaną całkowicie pobrane, niekoniecznie zauważysz to za pomocą pipingu. Proces wysyłany do potoku może zignorować ten sygnał.
Janus Troelsen

@JanusTroelsen, co masz na myśli mówiąc, że nie został pobrany całkowicie? Dlaczego tak się dzieje, z wyjątkiem błędu serwera, w którym to przypadku nic nie będzie przesyłane do sh.
hasufell

Lub przerwanie połączenia ... transfer na wiele sposobów może się nie powieść
Janus Troelsen

0

Jedną z różnic między nimi (wziętymi z innych odpowiedzi w Internecie) jest to, że jeśli nie pobierzesz całego skryptu naraz, może on odciąć się w połowie skryptu w nieznanym punkcie i zmienić znaczenie polecenia, tak aby było wykonany. Wydaje się więc, że najpierw należy pobrać cały plik, a następnie go ocenić.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.