
2026/07/06 5:44
依存パッケージは VCS から直接取得する必要があります
RSS: https://news.ycombinator.com/rss
要約▶
Japanese 翻訳:
著者は、Go を新職で運用してきた十年以上の実践に基づき、Go の依存関係管理が Ruby を上回るのは、主に「パッケージを公開する」というステップを廃止し、代わりに go.mod に指定された URL 経由で VCS(例えば GitHub)から直接コードを取得するためであるとしている。このモデルでは、go.mod が依存関係の仕様書およびロックファイルとして機能し、semver の範囲を示さずに正確なバージョン番号をリスト化している。完全性は、sum.golang.org 上の既知の値とハッシュの照合を行うこと、ならびにリポジトリの改ざんを防ぐためのプロキシの使用によって保証される(left-pad などのインシデントがそれを示している)。Go の依存関係のアウディットはソロ開発者にとって容易であり、git log またはフォージ UI を通じてコミットをレビューし、必要に応じて go.mod を更新することで可能となる。
対照的に、Ruby は rubygems.org から .gem アーカイブをダウンロードし、tar で展開し、diff を用いて変更を追跡する必要があるが、これは個々のコミットの履歴を失わせる。この「パッケージの公開に依存する」欠陥は RubyGems、npm、PyPI など多くのシステムに影響を与え、ソースリポジトリが清浄であっても悪意あるコードの注入を可能にする。xz のexploit や event-stream の疑わしい index.min.js などは、ソースリポジトリへの侵害ではなく、公開されたパッケージ自体を乗っ取ることでこのような脆弱性を具体的に示している。JavaScript ベースの言語は、コンパイルされたリソース、TypeScript で生成されたコード、ミニフィケーションされたファイル、およびバイナリー blobs といった観点から追加のアウディット課題に直面している。
RubyGems がクールドアンスと AI 支援スキャンを導入したにもかかわらず、著者はこれらを Go の堅牢な設計と比較して不透明性に乏しい一時的な修正であると見なしている。Ruby の不足を解消するために、著者は Bundler を変更し、GitHub URL および特定のタグですべての依存関係を指定できるようにすることを提案しており、Gemfile.lock でハッシュを使用することで、「パッケージ公開に依存する」モデルから「直接 VCS に基づく」モデルへとシフトさせるのである。これにより、単一障害点が排除され、サプライチェーンセキュリティが大幅に向上し、開発者がバージョン完全性に対してより大きな制御を持つことができるようになる。
本文
Ruby と Go の依存関係管理:セキュリティと監査の観点からの比較
過去 1 ヶ月、新しい正職で Ruby を開発してきました。ここ 10 年あまり Go を主に開発してきた私にとって、この環境の変化は非常に楽しくありました。以前も Ruby を扱った経験がありましたが、「どちらがより優れている」と断定するのは難しい状況です。
Ruby と Go はほぼすべての点で大きく異なりますが、それぞれの文脈において効率的にタスクを達成できることは共通しています。
Go の依存関係管理とセキュリティ
Go が明確に優れていると感じる側面の一つは、依存関係の管理、特に セキュリティ面にあります。
強固な設計原理
- 攻撃への耐性: パッケージを「公開する」ステップが存在しないため、悪意のある依存関係に対して格段に強い構造になっています。
- 特定方法: 依存関係は URL で特定されます(例:
)。github.com/user/pkg - フェッチ方法: VCS(ここでは Git)の確認を行い、
ファイルで指定したタグやコミットからコードを直接フェッチします。go.mod - 仕様書としての
:go.mod- 依存関係の仕様書とロックファイルの役割を兼ねており、厳密なバージョン番号のみが記載されます(例:
のような範囲指定はありません)。~>1.1 - 直接依存関係も間接依存関係も含め、完全な依存関係ツリーがこのファイルにリストされます(
コマンドが自動で書き込みます)。go
- 依存関係の仕様書とロックファイルの役割を兼ねており、厳密なバージョン番号のみが記載されます(例:
- 改ざん防止機構:
に登録された既知のハッシュ値とすべてのファイルのハッシュを照合し、タグへの改ざんを防ぎます。sum.golang.org- リポジトリの内容が意図的に変更されるのを防ぐため、プロキシを経由する構成になっています。
重要: 依存関係は URL によって特定され、コードは VCS から直接フェッチされます。これは直接依存関係と間接依存関係の双方に適用されます。
監査の手顺
依存関係の確認や更新は比較的簡単です。通常以下の手順を行います:
を実行してフォージの Web UI を介し、すべてのコミットを確認する。git log -p old..new- 必要に応じて
ファイルを更新する。go.mod
- 既存の依存関係が大きく変更されることは稀なため、作業は迅速に進みます。
- 深く細かい検証を行う必要はなく、単に不審な点をチェックすれば十分です。
- 特に目立つ例:グロービブ処理を行うライブラリの中に
やexec.Command(..)
が含まれているケース。http.Post(..)
- 特に目立つ例:グロービブ処理を行うライブラリの中に
不正なコードを隠蔽することは極めて困難であり、このような監査作業は一人の開発者でも何年も続けることが可能です。より大きな依存関係ツリーを持つプロジェクトでは時間がかかりますが、難しくはありません。
Ruby の依存関係管理と課題
一方、Ruby の場合は事情が異なります。「パッケージを公開する」ステップが存在するためです。
アーカイブを作成し、それを.gem
にアップロードします。rubygems.org- このアーカイブの中身を何でも入れられるため、ソースコードのリポジトリの内容と一致しているとは保証されていません。
監査の手顺(Ruby)
監査を行うためには、以下のような手動処理が必要です:
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
Ruby における問題点
- 手順が非常に手間がかかります。
- 個々のコミット情報が失われ、監査自体が一般に難しくなってしまいます。
- 差分が小さい場合のみ問題ありませんが、場合によっては巨大な出力になり、コミット履歴にアクセスできないことが大きな負担となります。
- 「自分が何を監査し、何を見過ごしたか」が混乱することも十分あり得ます。
- このため、このような監査を行っている人の数は極めて少ないと考えられます(作業が面倒だから)。
セキュリティ攻撃の本質とその他の課題
サイディンチャネル攻撃の実態
私がこれまで目にしたサイディンチャネル攻撃のほとんどは、「パッケージ公開攻撃」と呼ぶのが正確です。
- 攻撃は「パッケージを公開する」ステップに何かを注入することで成り立ちます。
- プラットフォーム(RubyGems, npm, PyPI, FTP など)の違いよりも、アーカイブへの悪意のあるコードの注入という点で本質は同じです。
- 実際のソースコードリポジトリが侵害されることは稀であり、露見しやすいためです。
- 効果的な攻撃には、わずかに exploit を隠蔽する必要があるためです。
過去の実例(npm)
最近の
npm 関連の侵害事件はすべて、以下の方法に依存していましたが、ソースコードリポジトリそのものが直接改ざんされたわけではありません:
アカウントへのアクセス権限を取得し、公開されたパッケージ内にコードを注入する方法。npm- 例 1:
- ソースコードには悪意のあるコードが含まれていましたが不活性化しており、バイナリ形式のテストファイルに隠されていました。改ざんされたリリース文件中で初めて有効化されました。xz - 例 2: 2018 年の
-event-stream
に依存し、公開されたflatmap-stream
パッケージ上のみにnpm
ファイル内に悪意のあるコードが存在していました。index.min.js
コンパイル済みリソースの課題
この点で第二の問題が浮かび上がります:コンパイル済みリソースを含むパッケージの問題です。
- TypeScript が生成する JavaScript コードは完全に読めず、オリジナルと比較しても読みづらく、監査しにくいものです。
- minified ファイルやバイナリデータのケースも同様です。
- これは Ruby ではあまり問題にならず、特に
において大きな課題となっています。npm
提言:Go Modules のアプローチへの移行と考察
現状の状況
- Go Modules に他にもセキュリティ機能がありますが、割愛します。
- 重要なのは透明性と、「パッケージを公開する」というモデル自体の見直しです。
結論: AI ツールだけではこの問題を解決することはできません。「パッケージを公開する」モデルには不可欠な透明性が欠けています。
バッダー(Bundler)での既存の対策
完全に変更することは困難ですが、特定の状況では以下の対応策が存在します:
gem 'rails', git: 'https://github.com/rails/rails.git', tag: 'v8.1.2'
- これにより Rails gem は Git からフェッチされますが、依存関係は依然として
から取得されます。rubygems.org - Bundler に Git をすべての依存関係に使用させる方法はあるかもしれませんが、非常に困難で誤って
を使用しやすくなります。rubygems.org
提案する仕組み(Gemfile の変更案)
現存する 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' # ……etc end
はハッシュ値の記録などには引き続き利用可能ですが(Gemfile.lock
と同様の役割)、それ以外の用途においてはそれほど役に立ちません。go.sum
おわりに
この仕組みを完全に変更することは困難であり、混乱を招く可能性もあります。しかし、ハイジャックされたパッケージが絶え間ないストリームとして発生する様子に対処することもまた難しいものです。
少なくとも合理的な道筋があるように感じます。あるいは全く別のアプローチもあるかもしれません。重要なのは、責任ある開発者として信頼性を持って依存関係を監査したいと思いつつ、現在の RubyGems や多くのパッケージマネージャーがそれを難しくさせている現実です。