ここ 2 年の大規模なサプライチェーン攻撃の多くは、同じ手口で行われました。開発者が npm install を実行すると、自分では選んだ覚えのない依存パッケージが、1 行もコードを書かないうちにそのマシン上でコードを実行したのです。npm は設計としてこれを許しており、現在その判断を撤回しつつあります。Go は一度もこれを許していません。この記事では、この 1 つの違いでどこまで説明がつくのか、Go が本当に安全なのはどこか、そして Node と同じリスクを抱えているのはどこかを見ていきます。すべての主張には出典を示します。
要約
Go vs Node.js:サプライチェーンセキュリティを一覧で比較
| 観点 | Go(Golang) | Node.js(npm) |
|---|---|---|
| インストール時のコード実行 | なし。go get/go build はパッケージのスクリプトを一切実行しません。 | preinstall/install/postinstall が任意のコードを自動で実行します。 |
| 悪意あるコードが実行されるタイミング | import されたうえで、その関数が呼び出されたときだけです。 | インストールした瞬間です。1 行も書かないうちに実行されます。 |
| 完全性の保証 | Merkle ツリーによるチェックサム透明性ログ(sum.golang.org)。改ざんの痕跡が永久に残ります。 | lockfile のハッシュと、比較的新しい provenance(来歴証明)。歴史的に保証は弱めです。 |
| バージョン解決 | Minimal Version Selection(最小バージョン選択)。アップグレードは明示的に行います。 | semver の範囲指定(^、~)。問題のある新しいパッチが自動で解決されることがあります。 |
| 依存関係の規模 | 大きな標準ライブラリにより、直接の依存関係は少なめです。 | 小さなモジュールを多用する文化で、推移的な依存ツリーが大きく広がります。 |
| 脆弱性スキャン | govulncheck が到達可能性を解析するため、誤検知が少なくなります。 | npm audit はツリー全体を警告対象にするため、アラート疲れにつながります。 |
| レジストリのモデル | 分散型。モジュールはソースの URL です。 | 中央のアカウント。1 回のフィッシングでポートフォリオ全体が危険にさらされます。 |
| それでも残るリスク | メンテナーへのフィッシング、タイポスクワット、プロキシによるマルウェアのキャッシュ。 | 左記のすべてに加え、インストールスクリプトを悪用するワーム。 |
デフォルトの挙動と攻撃対象領域では Go が優位です。ただし、人間のメンテナーをフィッシングで狙う執念深い攻撃者は、どちらのエコシステムでも突破できてしまいます。
Go はなぜ npm に最も大きな打撃を与えている攻撃を避けられるのか?
Go にはインストールスクリプトがありません。npm install を実行すると、推移的な依存ツリー内のあらゆるパッケージがライフサイクルフック(preinstall、install、postinstall)を登録できます。npm はそれらを、あなたのユーザー権限で自動的に実行します。あなたがコードを 1 行も実行したり読んだりする前にです(npm scripts ドキュメント)。GitHub は、インストールスクリプトを npm エコシステムで単一では最大のコード実行の攻撃対象領域だと述べています(GitHub Security, 2025)。
Go はこの仕組みを意図的に入れませんでした。ビルドフックはありません。go get はソースを取得して検証し、go build はそれをコンパイルします。どちらも、パッケージ側で定義したスクリプトを実行しません。汚染された Go の依存パッケージは、あなたのコードがそれを import し、さらに悪意ある関数を呼び出すまで、ただのソースコードとして置かれているだけです。この隙は、インストールのたびに全員の環境で実行されるコードと比べて、ずっと狭く、見つけやすいものです。
それぞれのコマンドが何をするかは次のとおりです。
example.bashbash# npm: the postinstall hook runs on every install, automatically $ npm install left-pad > [email protected] postinstall > node ./scripts/setup.js # <- arbitrary code, your privileges, right now # go: nothing in the package gets to run during fetch or build $ go get example.com/some/module go: downloaded example.com/some/module v1.0.0 # source fetched, checksum verified, nothing executed
2026 年 7 月にリリースされる npm v12 がインストールスクリプトをデフォルトでブロックするのも、pnpm がすでに v10 で同じ対応をしたのも、この違いが理由です(npm scripts ドキュメント)。JavaScript のツール群は、Go が最初のリリースから備えていた挙動に近づいています。
npm のサプライチェーン問題は数字で見るとどれほど深刻か?
npm は群を抜いて最大の標的です。Sonatype は 2025 年に 454,648 件の新たな悪意あるパッケージを特定し、これによりブロックした累計件数は 123 万件を超えました。前年比 75% の増加です。そのオープンソースのマルウェアの 99% 以上が npm にありました(Sonatype 11th State of the Software Supply Chain, 2026)。同社の 2025 年第 4 四半期のマルウェアインデックスでは、その四半期にブロックされたマルウェアの 99.8% を npm が占めていました。
悪意ある Go モジュールの件数は、そもそも公表されていません。Sonatype、OpenSSF、そしてほとんどのベンダーは、マルウェアインデックスで Go を独立したエコシステムとして追跡していません。インデックスにするにはインシデントが少なすぎるからです。このデータの欠落自体が多くを物語っています。
2025 年のオープンソースマルウェアの所在(新たな悪意あるパッケージのエコシステム別シェア):
| エコシステム | 2025 年のオープンソースマルウェアに占める割合 |
|---|---|
| npm | 99% 以上 |
| その他すべて(Go を含む)* | 1% 未満 |
*2025 年には 454,648 件の新たな悪意あるパッケージが特定されました。Go はインシデントが少なすぎてインデックス化できないため、独立したエコシステムとして追跡されていません。出典:Sonatype 11th State of the Software Supply Chain, 2026。
2025 年から 2026 年の npm ワームの波で、実際に何が起きたのか?
自己増殖するワームが相次ぎ、既知のリスクは常に続く緊急事態に変わりました。そのすべてがインストールスクリプトを使っています。npm では何年も前から注目を集めるインシデントが起きていました。2018 年には、event-stream が約 800 万回ダウンロードされた依存パッケージに Bitcoin ウォレットを盗むコードを隠しました。2021 年には、ua-parser-js がアカウントの乗っ取り後に暗号資産マイナーを配布しました。しかし 2025 年は、インシデントが立て続けに起き始めた年です。
| 日付 | インシデント | 何が起きたか |
|---|---|---|
| 2025 年 9 月 8 日 | qix へのフィッシング | chalk や debug を含む 18 パッケージが乗っ取られました。影響範囲は週あたり約 26 億ダウンロードです。 |
| 2025 年 9 月 | Shai-Hulud | 初の npm ワーム。postinstall フックが認証情報を収集し、被害者 1 人あたりさらに約 100 パッケージへ自身を注入しました。 |
| 2025 年 11 月 24 日 | Shai-Hulud 2.0 | preinstall に移行し、ホームディレクトリを消去する機能を追加しました。約 796 パッケージが影響を受け、スキャンされたクラウド環境の 27% で見つかりました。 |
| 2026 年 3 月 31 日 | axios | 北朝鮮(DPRK)の攻撃者がメンテナーをソーシャルエンジニアリングで欺きました。postinstall に仕込まれた RAT が、12,000 を超えるプロジェクトで C2 通信を発生させました。 |
| 2026 年 6 月 | Miasma | 「Phantom Gyp」手法で npm のインストールスクリプトのブロックを回避しました。暗黙の node-gyp リビルドを悪用する手法です。 |
2025 年 9 月の qix の侵害は、なりすましドメイン npmjs.help から届いた、本物そっくりの 2FA リセットメールから始まりました。攻撃者はメンテナーの Josh Junon のアカウントを乗っ取り、18 パッケージの悪意あるバージョンを公開しました。その中には chalk(週あたり約 3 億ダウンロード)と debug(約 3 億 5,800 万)が含まれ、合計の影響範囲は週 26 億ダウンロードを超えました。ペイロードはブラウザ側で動作する暗号資産クリッパーで、約 2 時間公開されていました。
その数日後に現れたのが、初の本格的な npm ワームである Shai-Hulud です。インストール時に bundle.js が npm、GitHub、AWS、GCP の認証情報を収集し、TruffleHog を実行してシークレットを抜き出しました。次に、盗んだ npm トークンを使い、被害者それぞれが所有する最大約 100 のパッケージに自身を注入しました。CISA はアラートを発表しています(CISA, 2025)。Sonatype は、数か月の間に自己複製型の npm キャンペーンによって 171,740 件の悪意あるパッケージが公開されたとしています。
Shai-Hulud 2.0(2025 年 11 月 24 日)は、より多くのマシンに届くよう実行タイミングを preinstall に移しました。Node ベースの監視をすり抜けるために Bun ランタイムをインストールし、ホームディレクトリを消去しかねない破壊的なフォールバック処理も追加しています。Wiz は、スキャンしたクラウド環境の約 27% で影響を受けたパッケージを発見しました(Wiz, 2025)。
2026 年には、インストールスクリプトのブロックだけでは足りないことがはっきりしました。axios の侵害(2026 年 3 月 31 日)は、フィッシングで盗まれたパスワードから始まったのではありません。北朝鮮とつながりのある攻撃者(UNC1069 / Sapphire Sleet として追跡されています)が、偽の会社、その会社のブランドを冠した Slack ワークスペース、そしてメンテナーのマシンに RAT を仕込んだ Teams 通話を使い、筆頭メンテナーを狙いました。攻撃者はその後、postinstall に RAT を仕込んだ悪意ある axios のバージョン(週 1 億ダウンロード以上)を公開しました。StepSecurity は、12,000 を超えるプロジェクトで異常な C2 通信を検知しています(StepSecurity, 2026)。続いて Miasma(2026 年 6 月)が、「Phantom Gyp」手法で npm がこれから導入するインストールスクリプトのブロックを回避しました。パッケージに binding.gyp ファイルを同梱すると、npm が暗黙的に行う node-gyp リビルドが、スクリプトを何も宣言しないままペイロードを実行します。
では Go は銀の弾丸なのか?そうとも言い切れません
Go のデフォルト設定は、ある種類の攻撃をまるごと防ぎます。それでも、攻撃者が Go を狙ったことは何度かあります。2025 年に記録された悪意ある Go モジュールは、少数のキャンペーンに分散して数十件程度です。npm の数十万件と比べればごくわずかです。最も注目すべき事例は、Go の設計をすり抜けるために攻撃者がどれだけの手間をかける必要があるかを示しているので、順に見ていきます。
boltdb-go/bolt のバックドアは 2025 年 2 月に公表されましたが、実際には 2021 年 11 月に仕込まれていました。人気の BoltDB モジュール(github.com/boltdb/bolt)になりすましたタイポスクワットです。本物は数千のパッケージが依存し、Shopify や Heroku でも使われていました。偽物にはコマンド&コントロール型のバックドアが含まれていました。攻撃者は悪意ある v1.3.1 を公開して Go Module Mirror にキャッシュさせ、その後 Git タグを書き換えてクリーンなコードを指すようにしました。GitHub でリポジトリを監査した人にはクリーンなコードが見える一方で、プロキシはバックドアを配信し続けました。攻撃は成功しましたが、それはキャッシュの仕組みのエッジケースを突いたからです。npm であれば、インストール時フックがこうした手間なしに同じことを実現していたはずです(Socket, 2025)。
2025 年には、ほかにもいくつかのキャンペーンが確認されています。3 月には、金融業界の開発者を狙ったローダー型マルウェア入りの hypert と layout のタイポスクワットが相次ぎました。ディスクを消去するモジュールや SSH の認証情報を盗むモジュールも見つかっています。いずれも単発のもので、どれも開発者が import パスを打ち間違える必要がありました。自動で実行されたものはありません。
悪意あるモジュールが報告されると、Go のセキュリティチームはそれをプロキシから削除し(以降は 403 SECURITY ERROR が返されます)、Go 脆弱性データベースに追加します。boltdb-go の件では、Google がプロキシと GitHub の両方からモジュールを削除し、脆弱性データベースに登録しました。さらに、Capslock による capability 解析や deps.dev との比較といった進行中の取り組みにも言及しました。Go の防御は攻撃のコストを大きく引き上げますが、安全を保証するわけではありません。それはほかのどのエコシステムも同じです。
Go が構造的に安全な理由は何か?
インストールスクリプトがないことに加えて、Go にはこの優位性を広げる 4 つの設計判断があります。
Go のチェックサムデータベースは特に強力です。go.sum にはすべての依存関係の SHA-256 ハッシュが記録されます。sum.golang.org は Certificate Transparency 方式の Merkle ツリーによる透明性ログです。各モジュールバージョンのハッシュを誰かが初めて取得した時点で記録し、以後永久に保持します(Go モジュールリファレンス)。go コマンドはコードを信頼する前に包含証明と一貫性証明を確認するため、force push された Git タグや改ざんを行うプロキシがあれば、はっきりとエラーになります。Google で Go のセキュリティチームを率いた暗号技術者の Filippo Valsorda は、あらゆる言語エコシステムの中で Go のパッケージ完全性の仕組みが最も優れていると主張しています。世界中のどのクライアントも、あるモジュールバージョンを永久に同じバイト列として解決するからです(Filippo Valsorda)。このログには 1 つ限界があります。証明するのは一貫性であって、コードが良いものであることではありません。また、誰かが監視していなければ役に立ちません。
Minimal Version Selection が問題のあるリリースの広がりを遅らせます。Go のビルドは、すべての要件を満たす最も低いバージョンを使い、アップグレードは明示的に行います(MVS リファレンス)。npm の ^ や ~ による範囲指定では、公開されたばかりの悪意あるパッチが自動的にビルドに入り込むことがあります。Go では、誰かが意図的に要件のバージョンを上げるまで、問題のある新しいリリースが下流に広がることはありません。qix や Shai-Hulud のワームは、まさに MVS が行わない自動アップグレードの挙動に頼っていました。
govulncheck は到達可能性解析で誤検知を減らします。Go 公式のこのスキャナーは、vuln.go.dev で精査・管理されている脆弱性データベースを照合し、あなたのコードが脆弱なシンボルを実際に呼び出している場合にだけ警告します(Go ブログ:govulncheck)。一方 npm audit は、実際に到達するかどうかに関係なく、ツリー内のあらゆる脆弱なバージョンを警告します。そして開発者は、その出力を無視するようになります。
乗っ取られるとポートフォリオ全体が危険にさらされる、中央のアカウントがありません。Go のモジュールはソースの URL で識別されるため、GitHub(または GitLab)のアカウントセキュリティに頼ります。npm では、乗っ取られた 1 つのアカウントから、そのメンテナーが所有するすべてのパッケージが危険にさらされます。npm のワームがあのように拡散する一方で Go のインシデントが局所的にとどまる理由の一部は、この違いにあります。
大きな標準ライブラリも役立ちます。HTTP、JSON、暗号、テンプレートはすべて Go に標準で含まれているため、典型的なプロジェクトでは直接の依存関係が少なく、信頼しなければならないメンテナーも少なくなります。2025 年のある研究では、Go の依存関係 1 つあたりの増幅率(約 4.48 倍)は npm(約 4.32 倍)に近いことがわかりました。つまり、Go のパッケージ 1 つが npm のパッケージより少ない推移的依存関係を引き込むわけではありません。Go が優位なのは、プロジェクトが最初から少ない直接の依存関係で始まり、信頼する人の範囲が小さく保たれるからです。
Go のデフォルトではカバーされない部分
Go は最も大きな入り口を自動的に塞いでくれます。以下のリスクは Go を含むあらゆるエコシステムに当てはまるため、自分で対策する必要があります。
- フィッシングの被害に遭ったメンテナーは、どのエコシステムの防御も突破できてしまいます。メンテナーの GitHub アカウントが乗っ取られると、攻撃者は悪意あるリリースにタグを付けられます。チェックサムデータベースは良いコードと悪いコードを区別できないため、それを忠実に記録します。axios 型の攻撃はあらゆるエコシステムに通用します。だから業界は、ハードウェアキーと Trusted Publishing への移行を進めています。
- 依存関係は結局、名前で選んでいます。Go のチェックサム検証は、選んだモジュールの改ざんは防ぎますが、タイポスクワットを選んでしまうことは防げません。依存関係を追加する前に、import パスを正規のリポジトリと照らし合わせて確認してください。
- プロキシの不変性は、基本的には長所です。すべてのビルドがまったく同じバイト列を得られることを保証するキャッシュの仕組みは、Git タグがクリーンな状態に戻された後も boltdb-go のバックドアを取得可能なまま残した原因でもあります。この事例はまれで、Go チームによる削除プロセスと脆弱性データベースがそれに対応します。
- デフォルトが守るのはインストール時であり、実行時ではありません。Go の強みは、取得時やビルド時に何も実行されないことです。依存関係を import して呼び出せば、ほかの言語と同じく、あなたの権限で実行されます。信頼できないコードを実行時にサンドボックス化するのは、どのエコシステムでも別の問題です。
- 保護は有効なままにしておきましょう。自分で招いてしまう唯一のミスは、
GOSUMDB=offを広い範囲に設定することです。これは前述のチェックサム検証を無効にしてしまいます。有効なままにしておき、GOPRIVATEの対象は本当に社内のパスだけに絞ってください。
両エコシステムはどのように同じ対策へ向かっているのか?
どちらのエコシステムでも弱点は人間なので、両者は同じ防御策を取り入れています。2025 年の攻撃の波を受けて、GitHub は FIDO/WebAuthn による 2FA の必須化、有効期間が短く権限を細かく絞れるトークンの導入、従来のクラシックトークンの廃止を発表しました。さらに、暗号学的なビルドの来歴証明のために、OIDC を使った Trusted Publishing も推進しています(GitHub Security, 2025)。npm v12(2026 年 7 月)はインストールスクリプトをデフォルトでブロックし、min-release-age によるクールダウンを追加します。これにより、2 時間しか公開されていなかったパッケージがあなたの手元に届くことはなくなります。
これらの変更は役に立ちます。しかし axios の件は、それが通用しなくなる地点を示しました。ラップトップで RAT が動いているメンテナーを、トークンのポリシーで止めることはできません。止められたとすれば、ハードウェアの FIDO2 キーと、来歴を検証しクールダウンを設けたインストールを組み合わせた場合だけです。両コミュニティは、攻撃者がツールよりもメンテナーを狙うことを学びつつあります。それでも Go には優位性があります。メンテナーが侵害されても、インストール時に何も実行されないため、被害が小さく済むのです。
今すぐ何をすべきか?
取るべき手順は、使っている技術スタックによって異なります。
Go を書いている場合:
GOSUMDB=offやGONOSUMCHECKを広い範囲に設定しないでください。GOPRIVATEの対象は、本当に社内のパスだけに狭く絞ります。go.sumをコミットし、-mod=readonlyでビルドします。- 到達可能性を考慮したゲートとして、CI で
govulncheck ./...を実行します。 - 新しい依存関係にタイポスクワットがないか精査します。追加する前に、import パスを正規のリポジトリと照合します。
- Socket や deps.dev を使い、重要な依存関係の新しいリリースを監視します。
Node を書いている場合:
- npm 11.16.0 以降にアップグレードして
ignore-scripts=trueを設定し、本当にスクリプトが必要な少数のパッケージには許可リスト(@lavamoat/allow-scriptsなど)を使います。 - ロックファイルを固定した状態で
npm ciを使い、バージョンを厳密に固定します。さらにmin-release-ageのクールダウンを 3 日から 7 日に設定し、2 時間で終わる侵害が手元に届かないようにします。 - Trusted Publishing(OIDC)とハードウェアの FIDO2 キーを導入します。公開の認証に TOTP を頼らないでください。
npm auditだけに頼らず、振る舞い検知を備えた本格的なソフトウェア構成分析(Socket、Snyk)を実行します。- Dependency Confusion を防ぐために、社内パッケージ名を防御的に公開レジストリへ登録しておきます。
よくある質問
Go は本当に Node.js よりサプライチェーンセキュリティの面で安全ですか?
はい、構造的に安全です。主な理由は、go get と go build がインストール時のスクリプトを実行しないことです。これにより、2025 年と 2026 年の主要な npm ワームのほぼすべてが使った preinstall/postinstall という侵入経路がなくなります。2025 年のオープンソースマルウェアの 99% 以上は npm で見つかりました(Sonatype, 2026)。Go はより安全ですが、無敵ではありません。
Go と npm のセキュリティで最も大きな違いは何ですか?
インストール時のコード実行です。npm install を実行すると、npm はツリー内のすべてのパッケージについて、任意のパッケージスクリプトをあなたの権限で自動的に実行します。Go にはビルドフックがまったくないため、悪意ある依存関係は、あなたのコードが import して呼び出すまで何もしません。npm v12 がインストールスクリプトをデフォルトでブロックするようになったのはこのためで、Go は常にそのように動作してきました。
Go でサプライチェーン攻撃が起きたことはありますか?
あります。boltdb-go のバックドアは BoltDB になりすましたタイポスクワットで、3 年間にわたって Go のモジュールキャッシュからリモートアクセス用のバックドアを配信し続けました(Socket, 2025)。2025 年の hypert/layout キャンペーンでは、ローダー型マルウェアを含むタイポスクワットが少なくとも 7 つ公開されました。こうしたインシデントは現実に起きていますがまれで、悪意あるモジュールは報告されるとプロキシから削除されます。
Go のチェックサムデータベースとは何で、なぜ重要なのですか?
sum.golang.org は Merkle ツリーによる透明性ログです。各モジュールバージョンのハッシュを、そのバージョンが最初に確認された時点で永久に記録します。go コマンドはコードを信頼する前にこれらの証明を検証するため、改ざんや書き換えられた Git タグがあれば、はっきりとエラーになります(Go モジュールリファレンス)。あるバージョンについて、すべてのクライアントがまったく同じバイト列を得られます。これは主流のパッケージ管理の中で最も強力な完全性の保証です。
npm のインストールスクリプトをブロックすれば問題は解決しますか?
大きな効果はありますが、すべてを解決するわけではありません。axios の攻撃では、メンテナーをソーシャルエンジニアリングで欺いたうえで postinstall に RAT を仕込みました。Miasma のキャンペーンは、暗黙の node-gyp リビルドを通じてスクリプトのブロックを回避しています。スクリプトのブロックに、ハードウェアの FIDO2 キー、リリースのクールダウン、来歴の検証を組み合わせれば、隙の大部分は塞げます。それでも、フィッシングの被害に遭ったメンテナーは、どの対策も単体ではすり抜けられてしまいます。
セキュリティだけを理由に Node から Go に乗り換えるべきですか?
セキュリティは Go に有利な点です。ただし多くのチームは、並行処理、採用、エコシステム、どれだけ速くリリースできるかなど、言語がもたらすすべてを見て選びます。Go は強力なバックエンド言語であり、サプライチェーン攻撃に対して構造的により安全な言語でもあります。すでに Go を検討しているなら、より安全なデフォルトは Go を学ぶもう 1 つの理由になります。
出典
この記事で引用した一次情報源(最終確認日:2026 年 6 月 29 日):
- Sonatype:State of the Software Supply Chain
- Socket:Malicious Go module exploits proxy caching for persistence(boltdb-go)
- GitHub Security:Our plan for a more secure npm supply chain
- Go モジュールリファレンス:チェックサムデータベース
- Go モジュールリファレンス:Minimal Version Selection
- Go ブログ:govulncheck
- Go:Vulnerability management for Go
- npm ドキュメント:scripts とライフサイクルフック
- Alex Birsan:Dependency Confusion
- CISA:サイバーセキュリティ勧告
- Wiz:リサーチブログ
- StepSecurity:ブログ
- Filippo Valsorda:words.filippo.io
