HN 日本語サマリー

← 一覧へ戻る
プログラミング

依存関係はVCSから直接取得すべき

Dependencies should be fetched directly from VCS (arp242.net)

38 pointsby mrngm30 コメント

要約

Go言語の依存関係管理は、URLでVCSから直接コードを取得するため、セキュリティ面で優れていると著者は主張します。一方、RubyGemsのようなパッケージシステムは、パッケージを公開するステップがあり、ソースコードとの不一致や悪意のあるコードの混入リスクが高いと指摘しています。依存関係の監査の容易さについても比較し、VCSからの直接取得モデルを推奨しています。

全文翻訳

先月、新しい仕事でRubyを書いています。過去10年間のほとんどをGoで書いてきたので、気分転換になって楽しいです。以前もRubyを書いていましたが(数年前)、どちらが「より良い」とはっきり言えません。RubyはGoとはほとんどすべての点で異なり、どちらも独自のやり方で物事を成し遂げるのに非常に効果的だと感じています。Goが明らかに優れていると感じる側面の一つは、依存関係管理、特にそのセキュリティ面です。Goは悪意のある依存関係に対して魔法のように無敵というわけではありませんが、それに抵抗する力ははるかに強いです。なぜなら、「パッケージを公開する」というステップがないからです。Goでは、依存関係はURLで識別されます。例えば github.com/user/pkg です。Goは使用されているVCS(この場合はgit)を識別し、go.modファイルで指定されたタグまたはコミットを取得します。go.modファイルは、依存関係の仕様と「ロックファイル」の両方の役割を果たします。正確なバージョンがリストされており、~>1.1のようなものはありません。直接的および間接的な依存関係が含まれており、完全な依存関係ツリーがリストされています(goコマンドはgo.modに書き込みます)。すべてのファイルのハッシュは、sum.golang.org上の既知のハッシュと比較され、タグの置き換えを防ぎます。また、プロキシを使用してリポジトリがleft-padされるのを防ぎます。Go Modulesにはセキュリティ機能を含む他の側面もありますが、この記事の目的のために省略します。関連する部分は「依存関係はURLで識別され、コードはVCSから直接取得され、直接的および間接的な依存関係の両方に対してこれを行います」ということです。 依存関係の監査と更新は簡単です。git log -p old..new(通常はフォージのWeb UI経由)を実行し、すべてのコミットを読み、go.modファイルを更新します。私は依存関係をあまり多く持っておらず、持っているものもあまり頻繁には変更されません。通常はかなり速いです。ここでは注意深い詳細なレビューは必要ありません。疑わしいものを探すだけで十分です。globbingライブラリ内のexec.Command(..)やhttp.Post(..)のようなものは目立つでしょう。何かを隠すのは難しいです。私はこれを何年も、すべての依存関係に対して行ってきました。一人の開発者としてです。簡単です。プロジェクトによっては依存関係ツリーがはるかに大きい場合があり、これはより時間がかかりますが、難しくも混乱もしません。ただ時間がかかるだけで、依然として簡単です。 Rubyでは状況が異なります。なぜなら、「パッケージを公開する」というステップがあるからです。つまり、.gemアーカイブを作成してrubygems.orgにアップロードします。そこに何でも入れることができます。ただし、.gemの内容がソースリポジトリと一致するという保証はありません。それを監査するには、次のようなことを行う必要があります。 curl -s https://rubygems.org/downloads/example-2.7.5.gem >old.gem curl -s https://rubygems.org/downloads/example-2.8.2.gem >new.gem mkdir old new tar xf old.gem -C old tar xf new.gem -C new (cd old && tar xf data.tar.gz) (cd new && tar xf data.tar.gz) diff -urN old new これは機能しますが、簡単とは程遠いです。個々のコミットは失われ、一般的に監査がより困難になります。場合によってはdiffが十分に小さいので問題ありません。他の場合では非常に大きく、コミットにアクセスできないのは面倒です。また、何を確認し、何を確認しなかったかについて混乱する可能性も十分にあります。このような監査を行う人の数は、それが単に面倒すぎるため、非常に少ないと思います。 これはRubyに限ったことではありません。これは多くの(ほとんどの?)パッケージシステムが機能する方法です。私が目にした「サイドチャネル攻撃」のほとんどは、おそらく「パッケージ公開攻撃」と呼ぶ方が正確でしょう。それらは、「パッケージを公開する」ステップに何かを注入することに依存しています。それがRubyGemsであれ、npmであれ、PyPIであれ、.tar.gz FTPダウンロードであれ、あるいは他のものであれ、それは比較的些細な詳細です。実際のソースリポジトリが侵害されることはまれです。なぜなら、それはあまりにも目立つからです。効果的であるためには、少なくとも少しはエクスプロイトを隠す必要があります。最近のnpmの侵害はすべて、npmアカウントへのアクセスを取得し、公開されたパッケージに何かを注入することに依存していました。xzはソースリポジトリにエクスプロイトコードを持っていましたが、それは不活性で、バイナリテストファイルに隠されており、変更された.tar.gzリリースでのみアクティブ化されました。2018年にはevent-streamがflatmap-streamへの依存関係を追加しましたが、そのindex.min.jsには公開されたnpmパッケージにのみ悪意のあるコードが含まれていました。 これは第二の問題を浮き彫りにします:コンパイルされたリソースを含むパッケージです。TypeScriptが生成するJavaScriptは完全に読めないわけではありませんが、元のTypeScriptよりも読みにくく監査しにくいことは確かです。ましてや、ミニファイされたファイルやバイナリブロブについては言うまでもありません。これはRubyではそれほど大きな問題ではありませんが、npmでははるかに大きな問題です。 先週、RubyGemsはクールダウンオプションと「最も重要なgemに対するAI支援脆弱性スキャン」を追加しました。短期的な対策としては悪くないですが、より適切な解決策は、パッケージを公開するというモデル全体を再考することだと感じています。必要な透明性が欠けています。AIツールがそれを魔法のように修正してくれるわけではありません。 おそらく20年前に私もRubyGemsに似たものを作成したでしょう。SourceForgeで.tar.gzファイルを配布するのが一般的な方法であり、多くのプロジェクトには公開されているVCSリポジトリがなかったか、まったくありませんでした。一般的に、当時はそれはかなりうまく機能していました。私は誰かを非難しているわけではありません。私も同じことをしたでしょう。RubyGemsは単に異なる世界のために設計されています。 RubyGemsやnpmがGoのやり方をすべての点で完全にコピーする必要があるとは言いませんし、これらすべてが完全に完璧だとも言いません。しかし、私の知る限りでは、これまでに考え出された中で最良のものです。Go Modulesの他の側面(最小バージョン選択など)はそれほど重要ではありません。全体的な仕組みを完全に変更することは困難であり、潜在的に混乱を招く可能性があることを理解しています。しかし、乗っ取られたパッケージのこの終わりのないストリームに対処することもまた困難で混乱を招きます。だから… Bundlerの特定のケースでは、すでにいくつかのサポートがあります。次のように実行できます。 gem 'rails', git: 'https://github.com/rails/rails.git', tag: 'v8.1.2' これにより、rails gemはgitから取得されますが、依存関係は引き続きrubygems.orgから取得されます。Bundlerにすべての依存関係にgitを使用させる方法があるかもしれませんが、それは困難な戦いであり、誤ってrubygems.orgを使用しやすいです。ただ考えているだけですが、次のようなものは、既存のGemfileを壊すことなく、大いに役立つでしょう。 # rubygems.orgからの取得を許可しない; # gem()の動作を変更してgitを使用する。 must 'use-git' gem 'github.com/rails/rails', 'v8.1.2' # 間接的(「bundle install」などで自動的に書き込まれ更新される) indirect do gem 'github.com/rails/actionview', 'v8.1.2' gem 'github.com/fxn/zeitwerk', 'v2.8.2' # ...など... end Gemfile.lockはハッシュ(go.sumに似ています)に使用できますが、それ以外はあまり役に立ちません。ここで解決すべき多くの詳細があり、それらをうまく解決するのが容易ではないと主張するつもりはありませんが、合理的な道筋があるように思われますか?あるいは、まったく別の何かかもしれません。わかりません。重要な点は、責任ある開発者として依存関係を確実に監査したいのですが、RubyGemsはそれを非常に難しくしており、他のパッケージマネージャーも同様です(ただし、それらには関心がありません)。 インターネットの他の場所で Cursed Bundler: Ruby Gemsをインストールするためにgo getを使用する その他のセキュリティ投稿 2014年11月14日 Pythonのpickleとmarshalモジュールのセキュリティ 2024年10月28日 Jia Tanning Goコード 2016年9月5日 CDNに関するいくつかの考察 2019年11月9日 Curlからシェルへのパイプはそれほど悪くない 2019年3月13日 Amazon.comはなぜメールに署名しないのか? その他のRuby投稿 2014年8月20日 Ruby on Railsで送信メールをインターセプトする 2014年6月24日 FlagShihTzuをFormtasticと連携させる