`curl | sh`と `sh -c“ $(curl)”`?


23

(たとえば)Dockerの簡単なインストール方法の1つは次のとおりです。

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

ただし、次のようなものも見ました(Dockerの例を使用):

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

機能的には同じように見えますが、一方を他方よりも使用する理由はありますか?それとも、単に好み/美学のものですか?

(注意してください、未知の起源からスクリプトを実行するとき、非常に注意してください。)

回答:


39

実用的な違いがあります。

curl -sSL https://get.docker.com/ | sh開始curlshの出力接続、同時にcurl入力としますsh。スクリプトを実行できるcurl速度で(大体)ダウンロードshを実行します。サーバーは、タイミングの不規則性を検出し、リソースをファイルまたはバッファーに単にダウンロードするとき、またはブラウザーで表示するときに表示されない悪意のあるコードを挿入できます。

ではsh -c "$(curl -sSL https://get.docker.com/)"curlが実行される前に厳密にsh実行されます。リソースの内容全体がダウンロードされ、シェルshが開始される前にシェルに渡されます。シェルは、終了したshときにのみ開始されcurl、リソースのテキストをシェルに渡します。サーバーはsh呼び出しを検出できません。接続が終了した後にのみ開始されます。最初にスクリプトをファイルにダウンロードすることに似ています。

(これはdockerの場合には関係ないかもしれませんが、一般的には問題になる可能性があり、2つのコマンドの実際の違いを強調しています。)


2
おかげで、これは2つの間の最も重要な違いのようです。
サルケ

この主張を裏付ける資料を引用してください。サーバーが「sh」コールを検出する方法を知りたいと思います。
アルフレッドアームストロング

1
@AlfredArmstrongうーん、vulnerable to server-side detectionフレーズにリンクを付けました。それは彼らがそれを達成する方法を詳細に説明するブログ投稿につながります。TL; DR:スクリプトにスリープを設定し、サーバーでの受信の遅延を観察します。
ジョナスシェーファー

1
@JonasWielickiありがとう-リンクはあまり明確ではなかった-あなたのせいではなく、SEのCSSまで。人々は見事に卑劣ですよね?:)
アルフレッドアームストロング

1
すぐに捕まることなく、誰かが実際にそのトリックをやろうとしたことがあるのではないかと思うようになります。そのようなトリックがなくてもスクリプトに悪意のあることを行わせることができ、それを完全に読まない人は脆弱になるため、おそらくそれはおそらく重要です。
-ilkkachu

11

私はそれらが実質的に同一であると信じています。ただし、まれに異なる場合があります。

$(cmd)の結果で置換されますcmd。その結果コマンドの長さがで返される引数の最大長を超えるgetconf ARG_MAXと、結果が切り捨てられ、予測できない結果が生じる可能性があります。

パイプオプションにはこの制限はありません。curlコマンドからの出力の各行bashは、パイプから到着すると実行されます。

ただし、ARG_MAXは通常256,000文字の範囲です。Dockerのインストールでは、どちらの方法を使用しても自信があります。:-)


興味深いので、違いがあります。おかげで
Sarke

1
疑わしい場合は、パイプメソッドを使用します。私は答えにプリファレンスを指定しませんでしたが、パイプを介して送信されるデータの量がわからないため、この種の使用にはパイプメソッドを選択します。
グレッグタルサ

2
「それは結果を切り捨てます」-シェルは静かに切り捨てるのではなく、そのためのエラーメッセージを発行する必要があります。テストするとき、私もはるかに下のシェルからのエラーを取得ARG_MAXする場合、bashは、私のシステム上の131072バイトに個々の引数を制限しgetconf ARG_MAXたプリント2097152。しかし、いずれにしても、エラーでも切り捨てでも機能しません。
hvd

しかし、Bourneシェルの古い実装には、はるかに低い制限がありました。4.2BSDでは制限は10240文字でしたが、初期のシステムではさらに低くなりました。もちろんそれは30年前だったので、今日そのような低い制限に遭遇することはまずありません。正しく思い出せば、これらの初期のシェルのいくつかは静かに切り捨てられました。
-AndyB

1つの引数に対する128 kBの制限はLinuxの問題であり、Bashの問題ではありません。
-ilkkachu

8

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

  • curlおよびの両方のコマンドは、shそれぞれのサブシェルで同時に開始されます

  • からのSTDOUT curlはSTDINとして渡されますsh(これがパイプです、、、|

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

  • コマンド置換$()が最初に実行されcurlます。つまり、サブシェルで最初に実行されます

  • コマンド置換、$()はSTDOUTに置き換えられますcurl

  • sh -c (非対話型、非ログインシェル)は、STDOUTを実行します curl


1
それで、本当の違いはありますか?
サルケ

@Sarkeはい、理論的には私が言及したようですが、実際にはほとんど目立ちません。(コマンドの置換を引用符で囲まない場合、目に見える効果があります。)
heemayl

1
@Sarke、スクリプトが完全にダウンロードされない場合、必ずしもパイピングでスクリプトに気付くとは限りません。パイプされるプロセスは、そのシグナルを無視できます。
ヤヌストロエルセン

@JanusTroelsen完全にダウンロードされていないということはどういう意味ですか?サーバーエラーを除いて、なぜshにパイプされるものもありません。
ハスフェル

または、接続の中断...転送が失敗する可能性のある方法はたくさんあります
Janus Troelsen

0

2つの違い(Webの他の回答から取得)は、スクリプト全体を一度にダウンロードしないと、未知のポイントでスクリプトの途中で切断され、コマンドの意味が変更される可能性があることです。実行されました。したがって、最初にファイル全体をダウンロードしてから、評価する方が良いようです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.