Quelle est la différence entre `curl | sh` et `sh -c“ $ (curl) ”`?

23

Une méthode d'installation facile pour Docker (par exemple) est la suivante:

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

Cependant, j'en ai également vu certains qui ressemblent à ceci (en utilisant l'exemple Docker):

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

Ils semblent être fonctionnellement les mêmes, mais y a-t-il une raison de les utiliser l'un par rapport à l'autre? Ou est-ce juste une préférence / une chose esthétique?

(À noter, soyez très prudent lorsque vous exécutez un script d'origine inconnue.)

Sarke
la source

Réponses:

39

Il y a une différence pratique.

curl -sSL https://get.docker.com/ | shdémarre curlet shen même temps, connecter la sortie de curlavec l'entrée de sh. curlexécutera avec le téléchargement (à peu près) aussi vite que shpeut exécuter le script. Le serveur peut détecter les irrégularités dans le timing et injecter du code malveillant non visible lors du simple téléchargement de la ressource dans un fichier ou un tampon ou lors de sa visualisation dans un navigateur.

Dans sh -c "$(curl -sSL https://get.docker.com/)", curlest exécuté strictement avant l' shexécution de. Tout le contenu de la ressource est téléchargé et transmis à votre shell avant le shdémarrage de. Votre shell ne démarre qu'à la shfin curlet lui transmet le texte de la ressource. Le serveur ne peut pas détecter l' shappel; il n'est démarré qu'après la fin de la connexion. Cela revient à télécharger le script dans un fichier en premier.

(Cela peut ne pas être pertinent dans le cas du docker, mais cela peut être un problème en général et met en évidence une différence pratique entre les deux commandes.)

Jonas Schäfer
la source
2
Merci, cela semble être la différence la plus importante entre les deux.
Sarke
Pourriez-vous citer une ressource sauvegardant cette revendication s'il vous plaît? Je serais très intéressé de savoir comment le serveur peut détecter l'appel «sh».
Alfred Armstrong
1
@AlfredArmstrong Uhm, j'ai mis un lien sur la vulnerable to server-side detectionphrase. Cela mène à un article de blog qui explique en détail comment ils y parviennent. TL; DR: mettez un sommeil dans votre script et observez le retard de réception sur le serveur.
Jonas Schäfer
1
@JonasWielicki merci - le lien n'était pas très clair - pas votre faute, jusqu'au CSS de SE je pense. Les gens sont brillamment sournois, n'est-ce pas? :)
Alfred Armstrong
1
Tout cela m'amène à me demander si quelqu'un a déjà essayé de faire ce truc en réalité sans se faire prendre immédiatement. Ce n'est pas vraiment important, car vous pourriez probablement demander au script de faire quelque chose de malveillant même sans de telles astuces, et toute personne qui ne le lirait pas entièrement serait vulnérable.
ilkkachu
11

Je pense qu'ils sont pratiquement identiques. Cependant, il existe de rares cas où ils sont différents.

$(cmd)se substitue aux résultats de cmd. Si la longueur de cette commande de résultat dépasse la valeur de longueur d'argument maximale renvoyée par getconf ARG_MAX, elle tronquera le résultat, ce qui peut entraîner des résultats imprévisibles.

L'option pipe n'a pas cette limitation. Chaque ligne de sortie de la curlcommande sera exécutée par bashlorsqu'elle arrive du tuyau.

Mais ARG_MAX est généralement dans la plage de 256 000 caractères. Pour une installation de docker, je serais confiant en utilisant l'une ou l'autre méthode. :-)

Greg Tarsa
la source
Intéressant, donc il y a une différence. Merci
Sarke
1
En cas de doute, utilisez la méthode des tuyaux. Je n'ai pas spécifié de préférence dans ma réponse, mais je préférerais la méthode du tuyau pour ce type d'utilisation car vous ne savez jamais combien de données transitent par le tuyau.
Greg Tarsa
2
"il tronquera le résultat" - Le shell devrait émettre un message d'erreur pour cela, pas tronquer silencieusement. Lors des tests, j'obtiens même une erreur du shell bien en dessous ARG_MAX, bash limite un argument individuel à 131072 octets sur mon système, lors de l' getconf ARG_MAXimpression 2097152. Mais de toute façon, erreur ou troncature, cela ne fonctionnerait pas.
hvd
Mais les anciennes implémentations du shell Bourne avaient des limites beaucoup plus basses. Dans 4.2BSD, la limite était de 10240 caractères, et sur les systèmes antérieurs, elle était encore plus basse. Bien sûr, c'était il y a 30 ans, il est donc peu probable que vous rencontriez des limites aussi basses aujourd'hui. Si je me souviens bien, certains de ces premiers obus ont simplement été tronqués en silence.
AndyB
La limite de 128 Ko pour un seul argument est une chose Linux, il ne s'agit pas de Bash.
ilkkachu
8

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

  • Les deux commandes, curlet sh, démarreront en même temps, dans les sous-coques respectives

  • Le STDOUT de curlsera passé en tant que STDIN à sh(c'est ce que fait le tuyau ,, |fait)

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

  • La substitution de commande,, $()sera exécutée en premier c'est curl-à- dire sera exécutée en premier dans un sous-shell

  • La substitution de commande $(), sera remplacée par le STDOUT decurl

  • sh -c (shell non interactif, sans connexion) exécutera le STDOUT à partir de curl

heemayl
la source
1
Alors, y a-t-il une vraie différence?
Sarke
@Sarke Oui, théoriquement comme je l'ai mentionné, mais pratiquement à peine perceptible. (Il y aurait un effet visible si vous laissez la substitution de commandes sans guillemets.)
heemayl
1
@Sarke, si les scripts ne sont pas téléchargés complètement, vous ne le remarquerez pas nécessairement avec des tuyaux. Le processus qui est canalisé peut ignorer ce signal.
Janus Troelsen
@JanusTroelsen que voulez-vous dire par non téléchargé complètement? Pourquoi cela se produirait-il, sauf pour une erreur de serveur, auquel cas il n'y aura rien de prévu pour sh.
hasufell
Ou interruption de connexion ... il y a tellement de façons dont un transfert peut échouer
Janus Troelsen
0

Une différence entre les deux (tirée d'autres réponses sur le Web) est que si vous ne téléchargez pas le script en entier à la fois, il pourrait couper à mi-chemin du script à un moment inconnu et changer la signification de la commande à réalisé. Il semble donc que le téléchargement du fichier entier puis l'évaluation soit préférable.

Lance Pollard
la source