Was ist der Unterschied zwischen `curl | sh` und `sh -c“ $ (curl) ”`?

23

Eine einfache Installationsmethode für Docker (zum Beispiel) ist:

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

Ich habe jedoch auch einige gesehen, die so aussehen (anhand des Docker-Beispiels):

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

Sie scheinen funktionell gleich zu sein, aber gibt es einen Grund, eine übereinander zu verwenden? Oder ist es nur eine Präferenz / ästhetische Sache?

(Seien Sie besonders vorsichtig, wenn Sie Skripte unbekannter Herkunft ausführen.)

Sarke
quelle

Antworten:

39

Es gibt einen praktischen Unterschied.

curl -sSL https://get.docker.com/ | shstartet curlund verbindet shgleichzeitig den Ausgang von curlmit dem Eingang von sh. curlWird mit dem Download (ungefähr) so schnell wie shmöglich ausgeführt. Der Server kann Unregelmäßigkeiten im Timing erkennen und schädlichen Code einschleusen, der nicht sichtbar ist, wenn die Ressource einfach in eine Datei oder einen Puffer heruntergeladen oder in einem Browser angezeigt wird.

In sh -c "$(curl -sSL https://get.docker.com/)", curlist streng laufen , bevor der shausgeführt wird. Der gesamte Inhalt der Ressource wird heruntergeladen und an Ihre Shell übergeben, bevor die shgestartet wird. Ihre Shell startet erst, shwenn sie beendet curlwurde, und übergibt den Text der Ressource an sie. Der Server kann den shAnruf nicht erkennen . es wird erst gestartet, nachdem die Verbindung beendet ist. Dies ähnelt dem Herunterladen des Skripts in eine Datei.

(Dies ist im Docker-Fall möglicherweise nicht relevant, kann jedoch allgemein problematisch sein und zeigt einen praktischen Unterschied zwischen den beiden Befehlen auf.)

Jonas Schäfer
quelle
2
Danke, das scheint der wichtigste Unterschied zwischen den beiden zu sein.
Sarke
Könnten Sie bitte eine Ressource nennen, die diese Behauptung stützt? Es würde mich sehr interessieren, wie der Server den "sh" -Aufruf erkennen kann.
Alfred Armstrong
1
@AlfredArmstrong Ähm, ich habe einen Link auf die vulnerable to server-side detectionPhrase gesetzt. Es führt zu einem Blogbeitrag, der ausführlich erklärt, wie sie es erreichen. TL; DR: Machen Sie in Ihrem Skript eine Pause und beobachten Sie die Empfangsverzögerung auf dem Server.
Jonas Schäfer
1
@ JonasWielicki danke - der Link war nicht sehr klar - nicht deine Schuld, bis auf SE's CSS denke ich. Die Leute sind brillant hinterhältig, nicht wahr? :)
Alfred Armstrong
1
All dies führt mich zu der Frage, ob jemals jemand versucht hat, diesen Trick in der Realität auszuführen, ohne sofort erwischt zu werden. Nicht, dass es wahrscheinlich viel ausmacht, da Sie das Skript wahrscheinlich auch ohne solche Tricks dazu bringen könnten, etwas Bösartiges zu tun, und jeder, der es nicht vollständig liest, wäre verwundbar.
Ilkkachu
11

Ich glaube, dass sie praktisch identisch sind. Es gibt jedoch seltene Fälle, in denen sie unterschiedlich sind.

$(cmd)wird durch die Ergebnisse von ersetzt cmd. Wenn die Länge dieses Ergebnisbefehls den von zurückgegebenen Wert für die maximale Argumentlänge überschreitet getconf ARG_MAX, wird das Ergebnis abgeschnitten, was zu unvorhersehbaren Ergebnissen führen kann.

Die Pipe-Option hat diese Einschränkung nicht. Jede Ausgabezeile des curlBefehls wird ausgeführt bash, sobald sie von der Pipe eingeht.

ARG_MAX liegt jedoch normalerweise im Bereich von 256.000 Zeichen. Für eine Docker-Installation würde ich sicher sein, beide Methoden zu verwenden. :-)

Greg Tarsa
quelle
Interessant, es gibt also einen Unterschied. Danke
Sarke
1
Verwenden Sie im Zweifelsfall die Pipe-Methode. Ich habe in meiner Antwort keine Präferenz angegeben, aber ich würde die Pipe-Methode für diese Art der Verwendung vorziehen, da Sie nie wissen, wie viele Daten durch die Pipe kommen.
Greg Tarsa
2
"es wird das Ergebnis abschneiden" - Die Shell sollte dafür eine Fehlermeldung ausgeben, nicht stillschweigend abschneiden. Beim Testen bekomme ich sogar einen Fehler von der Shell weit unten ARG_MAX, die Bash begrenzt ein einzelnes Argument auf 131072 Bytes auf meinem System, wenn getconf ARG_MAXgedruckt wird 2097152. Aber wie auch immer, Fehler oder Kürzung, es würde nicht funktionieren.
HDV
Aber alte Implementierungen der Bourne-Shell hatten viel niedrigere Grenzen. In 4.2BSD waren es 10240 Zeichen und in früheren Systemen sogar noch weniger. Das war natürlich vor 30 Jahren, daher ist es unwahrscheinlich, dass Sie heute auf so niedrige Grenzwerte stoßen. Wenn ich mich richtig erinnere, haben einige dieser frühen Muscheln nur lautlos abgeschnitten.
AndyB
Die 128-kB-Grenze für ein einzelnes Argument ist eine Linux-Sache, es geht nicht um Bash.
Ilkkachu
8

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

  • Beide Befehle curlund shwerden gleichzeitig in den jeweiligen Subshells gestartet

  • Das STDOUT von curlwird als STDIN an übergeben sh(dies ist, was Pipe |, tut)

In Erwägung nachstehender Gründe sh -c "$(curl -sSL https://get.docker.com/)":

  • Die Befehlsersetzung $()wird zuerst ausgeführt, dh curlzuerst in einer Subshell ausgeführt

  • Die Befehlsersetzung $()wird durch die STDOUT von ersetztcurl

  • sh -c (nicht interaktive, nicht angemeldete Shell) führt das STDOUT von aus curl

heemayl
quelle
1
Gibt es einen wirklichen Unterschied?
Sarke
@Sarke Ja, theoretisch wie gesagt, aber praktisch kaum spürbar. (Es wäre ein sichtbarer Effekt, wenn Sie die Befehlsersetzung nicht in
Anführungszeichen setzen
1
@Sarke, wenn das Skript nicht vollständig heruntergeladen wird, wird es beim Piping nicht unbedingt bemerkt. Der Prozess, zu dem geleitet wird, kann dieses Signal ignorieren.
Janus Troelsen
@JanusTroelsen was meinst du mit nicht ganz runtergeladen? Warum sollte das passieren, außer bei einem Serverfehler? In diesem Fall wird nichts an sh weitergeleitet.
Hasufell
Oder Verbindungsunterbrechung ... es gibt so viele Möglichkeiten, wie eine Übertragung fehlschlagen kann
Janus Troelsen,
0

Ein Unterschied zwischen den beiden besteht darin, dass, wenn Sie nicht das gesamte Skript auf einmal herunterladen, das Skript an einem unbekannten Punkt zur Hälfte unterbrochen und die Bedeutung des Befehls geändert werden könnte hingerichtet. Es scheint also besser zu sein, zuerst die gesamte Datei herunterzuladen und dann auszuwerten.

Lance Pollard
quelle