
自己増殖型サプライチェーン攻撃「Shai-Hulud」の恐怖|npmが標的
2025年9月、JavaScriptのパッケージ管理システムであるnpmのエコシステムを揺るがす、大規模なサプライチェーン攻撃が明らかになった。「Shai-Hulud」と名付けられた自己増殖能力を持つワームが、人気パッケージを含む多くのリポジトリを汚染したのだ。
ソフトウェアサプライチェーンセキュリティ企業Socket社によると、2025年9月16日時点で侵害されたパッケージの数は約500個確認されているという。(詳細はこちら)
この攻撃は、単に悪意のあるコードを混入させるだけでなく、開発者の認証情報を窃取し、その権限を悪用して感染を自動的に拡大させる。 最悪の場合、企業のクラウドインフラ全体を危険に晒しかねない、極めて悪質なものだ。
本記事は、この攻撃の概要を紹介するとともに、対策を考察する。
関連ページ:
Popular Tinycolor npm Package Compromised in Supply Chain Attack Affecting 40+ Packages
人気パッケージから始まった、汚染の連鎖
今回の攻撃は、その巧妙さと影響範囲の広さから、多くの開発者やセキュリティ専門家に衝撃を与えている。攻撃の起点は、大人気パッケージ「@ctrl/tinycolor」を含む40以上の人気npmパッケージの侵害だった。
ctrl/tinycolorはWeb開発などで色を扱うためのJavaScriptライブラリで、 軽量で多機能。海外メディアによると週に200万回以上ダウンロードされると報じられるほどの人気ぶりだ。
今回のサプライチェーン攻撃では、この人気の高さに目を付けられた。
脆弱なパッケージのリストはこちら(クリックで開く)
@ahmedhfarag/ngx-perfect-scrollbar – 20.0.20
@ahmedhfarag/ngx-virtual-scroller – 4.0.4
@art-ws/common – 2.0.22, 2.0.28
@art-ws/config-eslint – 2.0.4, 2.0.5
@art-ws/config-ts – 2.0.7, 2.0.8
@art-ws/db-context – 2.0.21, 2.0.24
@art-ws/di-node – 2.0.13
@art-ws/di – 2.0.28, 2.0.32
@art-ws/eslint – 1.0.5, 1.0.6
@art-ws/fastify-http-server – 2.0.24, 2.0.27
@art-ws/http-server – 2.0.21, 2.0.25
@art-ws/openapi – 0.1.9, 0.1.12
@art-ws/package-base – 1.0.5, 1.0.6
@art-ws/prettier – 1.0.5, 1.0.6
@art-ws/slf – 2.0.15, 2.0.22
@art-ws/ssl-info – 1.0.9, 1.0.10
@art-ws/web-app – 1.0.3, 1.0.4
@crowdstrike/commitlint – 8.1.1, 8.1.2
@crowdstrike/falcon-shoelace – 0.4.1, 0.4.2
@crowdstrike/foundry-js – 0.19.1, 0.19.2
@crowdstrike/glide-core – 0.34.2, 0.34.3
@crowdstrike/logscale-dashboard – 1.205.1, 1.205.2
@crowdstrike/logscale-file-editor – 1.205.1, 1.205.2
@crowdstrike/logscale-parser-edit – 1.205.1, 1.205.2
@crowdstrike/logscale-search – 1.205.1, 1.205.2
@crowdstrike/tailwind-toucan-base – 5.0.1, 5.0.2
@ctrl/deluge – 7.2.1, 7.2.2
@ctrl/golang-template – 1.4.2, 1.4.3
@ctrl/magnet-link – 4.0.3, 4.0.4
@ctrl/ngx-codemirror – 7.0.1, 7.0.2
@ctrl/ngx-csv – 6.0.1, 6.0.2
@ctrl/ngx-emoji-mart – 9.2.1, 9.2.2
@ctrl/ngx-rightclick – 4.0.1, 4.0.2
@ctrl/qbittorrent – 9.7.1, 9.7.2
@ctrl/react-adsense – 2.0.1, 2.0.2
@ctrl/shared-torrent – 6.3.1, 6.3.2
@ctrl/tinycolor – 4.1.1, 4.1.2
@ctrl/torrent-file – 4.1.1, 4.1.2
@ctrl/transmission – 7.3.1
@ctrl/ts-base32 – 4.0.1, 4.0.2
@hestjs/core – 0.2.1
@hestjs/cqrs – 0.1.6
@hestjs/demo – 0.1.2
@hestjs/eslint-config – 0.1.2
@hestjs/logger – 0.1.6
@hestjs/scalar – 0.1.7
@hestjs/validation – 0.1.6
@nativescript-community/arraybuffers – 1.1.6, 1.1.7, 1.1.8
@nativescript-community/gesturehandler – 2.0.35
@nativescript-community/perms – 3.0.5, 3.0.6, 3.0.7, 3.0.8, 3.0.9
@nativescript-community/sentry – 4.6.43
@nativescript-community/sqlite – 3.5.2, 3.5.3, 3.5.4, 3.5.5
@nativescript-community/text – 1.6.9, 1.6.10, 1.6.11, 1.6.12, 1.6.13
@nativescript-community/typeorm – 0.2.30, 0.2.31, 0.2.32, 0.2.33
@nativescript-community/ui-collectionview – 6.0.6
@nativescript-community/ui-document-picker – 1.1.27, 1.1.28
@nativescript-community/ui-drawer – 0.1.30
@nativescript-community/ui-image – 4.5.6
@nativescript-community/ui-label – 1.3.35, 1.3.36, 1.3.37
@nativescript-community/ui-material-bottom-navigation – 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-material-bottomsheet – 7.2.72
@nativescript-community/ui-material-core-tabs – 7.2.72, 7.2.73, 7.2.74, 7.2.75, 7.2.76
@nativescript-community/ui-material-core – 7.2.72, 7.2.73, 7.2.74, 7.2.75, 7.2.76
@nativescript-community/ui-material-ripple – 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-material-tabs – 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-pager – 14.1.35, 14.1.36, 14.1.37, 14.1.38
@nativescript-community/ui-pulltorefresh – 2.5.4, 2.5.5, 2.5.6, 2.5.7
@nexe/config-manager – 0.1.1
@nexe/eslint-config – 0.1.1
@nexe/logger – 0.1.3
@nstudio/angular – 20.0.4, 20.0.5, 20.0.6
@nstudio/focus – 20.0.4, 20.0.5, 20.0.6
@nstudio/nativescript-checkbox – 2.0.6, 2.0.7, 2.0.8, 2.0.9
@nstudio/nativescript-loading-indicator – 5.0.1, 5.0.2, 5.0.3, 5.0.4
@nstudio/ui-collectionview – 5.1.11, 5.1.12, 5.1.13, 5.1.14
@nstudio/web-angular – 20.0.4
@nstudio/web – 20.0.4
@nstudio/xplat-utils – 20.0.4, 20.0.5, 20.0.6, 20.0.7
@nstudio/xplat – 20.0.4, 20.0.5, 2.0.6, 20.0.7
@operato/board – 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
@operato/data-grist – 9.0.29, 9.0.35, 9.0.36, 9.0.37
@operato/graphql – 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
@operato/headroom – 9.0.2, 9.0.35, 9.0.36, 9.0.37
@operato/help – 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
@operato/i18n – 9.0.35, 9.0.36, 9.0.37
@operato/input – 9.0.27, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48
@operato/layout – 9.0.35, 9.0.36, 9.0.37
@operato/popup – 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
@operato/pull-to-refresh – 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47
@operato/shell – 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39
@operato/styles – 9.0.2, 9.0.35, 9.0.36, 9.0.37
@operato/utils – 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51
@teselagen/bounce-loader – 0.3.16, 0.3.17
@teselagen/liquibase-tools – 0.4.1
@teselagen/range-utils – 0.3.14, 0.3.15
@teselagen/react-list – 0.8.19, 0.8.20
@teselagen/react-table – 6.10.19, 6.10.20, 6.10.21, 6.10.22
@teselagen/sequence-utils – 0.3.33
@thangved/callback-window – 1.1.4
@things-factory/attachment-base – 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51, 9.0.52, 9.0.53, 9.0.54, 9.0.55
@things-factory/auth-base – 9.0.42, 9.0.43, 9.0.44, 9.0.45
@things-factory/email-base – 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46, 9.0.47, 9.0.48, 9.0.49, 9.0.50, 9.0.51, 9.0.52, 9.0.53, 9.0.54, 9.0.55, 9.0.56, 9.0.57, 9.0.58, 9.0.59
@things-factory/env – 9.0.42, 9.0.43, 9.0.44, 9.0.45
@things-factory/integration-base – 9.0.42, 9.0.43, 9.0.44, 9.0.45
@things-factory/integration-marketplace – 9.0.42, 9.0.43, 9.0.44, 9.0.45
@things-factory/shell – 9.0.42, 9.0.43, 9.0.44, 9.0.45
@tnf-dev/api – 1.0.8
@tnf-dev/core – 1.0.8
@tnf-dev/js – 1.0.8
@tnf-dev/mui – 1.0.8
@tnf-dev/react – 1.0.8
@ui-ux-gang/devextreme-angular-rpk – 24.1.7
@yoobic/design-system – 6.5.17
@yoobic/jpeg-camera-es6 – 1.0.13
@yoobic/yobi – 8.7.53
airchief – 0.3.1
airpilot – 0.8.8
angulartics2 – 14.1.1, 14.1.2
browser-webdriver-downloader – 3.0.8
capacitor-notificationhandler – 0.0.2, 0.0.3
capacitor-plugin-healthapp – 0.0.2, 0.0.3
capacitor-plugin-ihealth – 1.1.8, 1.1.9
capacitor-plugin-vonage – 1.0.2, 1.0.3
capacitorandroidpermissions – 0.0.4, 0.0.5
config-cordova – 0.8.5
cordova-plugin-voxeet2 – 1.0.24
cordova-voxeet – 1.0.32
create-hest-app – 0.1.9
db-evo – 1.1.4, 1.1.5
devextreme-angular-rpk – 21.2.8
ember-browser-services – 5.0.2, 5.0.3
ember-headless-form-yup – 1.0.1
ember-headless-form – 1.1.2, 1.1.3
ember-headless-table – 2.1.5, 2.1.6
ember-url-hash-polyfill – 1.0.12, 1.0.13
ember-velcro – 2.2.1, 2.2.2
encounter-playground – 0.0.2, 0.0.3, 0.0.4, 0.0.5
eslint-config-crowdstrike-node – 4.0.3, 4.0.4
eslint-config-crowdstrike – 11.0.2, 11.0.3
eslint-config-teselagen – 6.1.7, 6.1.8
globalize-rpk – 1.7.4
graphql-sequelize-teselagen – 5.3.8, 5.3.9
html-to-base64-image – 1.0.2
json-rules-engine-simplified – 0.2.1, 0.2.2, 0.2.3, 0.2.4
jumpgate – 0.0.2
koa2-swagger-ui – 5.11.1
mcfly-semantic-release – 1.3.1
mcp-knowledge-base – 0.0.2
mcp-knowledge-graph – 1.2.1
mobioffice-cli – 1.0.3
monorepo-next – 13.0.1, 13.0.2
mstate-angular – 0.4.4
mstate-cli – 0.4.7
mstate-dev-react – 1.1.1
mstate-react – 1.6.5
ng2-file-upload – 7.0.2, 7.0.3, 8.0.1, 8.0.2, 8.0.3, 9.0.1
ngx-bootstrap – 18.1.4, 19.0.3, 19.0.4, 20.0.3, 20.0.4, 20.0.5, 20.0.6
ngx-color – 10.0.1, 10.0.2
ngx-toastr – 19.0.1, 19.0.2
ngx-trend – 8.0.1
ngx-ws – 1.1.5, 1.1.6
oradm-to-gql – 35.0.14, 35.0.15
oradm-to-sqlz – 1.1.2, 1.1.3, 1.1.4, 1.1.5
ove-auto-annotate – 0.0.9, 0.0.10
pm2-gelf-json – 1.0.4, 1.0.5
printjs-rpk – 1.6.1
react-complaint-image – 0.0.32, 0.0.33, 0.0.34, 0.0.35
react-jsonschema-form-conditionals – 0.3.18, 0.3.19, 0.3.20, 0.3.21
react-jsonschema-form-extras – 1.0.1, 1.0.2, 1.0.3, 1.0.4
remark-preset-lint-crowdstrike – 4.0.1, 4.0.2
rxnt-authentication – 0.0.3, 0.0.4, 0.0.5, 0.0.6
rxnt-healthchecks-nestjs – 1.0.2, 1.0.3, 1.0.4, 1.0.5
rxnt-kue – 1.0.4, 1.0.5, 1.0.6, 1.0.7
swc-plugin-component-annotate – 1.9.1, 1.9.2
tbssnch – 1.0.2
teselagen-interval-tree – 1.1.2
tg-client-query-builder – 2.14.4, 2.14.5
tg-redbird – 1.3.1, 1.3.2
tg-seq-gen – 1.0.9, 1.0.10
thangved-react-grid – 1.0.3
ts-gaussian – 3.0.5, 3.0.6
ts-imports – 1.0.1, 1.0.2
tvi-cli – 0.1.5
ve-bamreader – 0.2.6, 0.2.7
ve-editor – 1.0.1, 1.0.2
verror-extra – 6.0.1
voip-callkit – 1.0.2, 1.0.3
wdio-web-reporter – 0.1.3
yargs-help-output – 5.0.3
yoo-styles – 6.0.326
自己増殖能力を持つワーム「Shai-Hulud」の攻撃手法
攻撃者はctrl/tinycolorへの侵害を足がかりに、他のパッケージへと感染を拡大。サイバーセキュリティ企業であるCrowdStrikeが公開しているnpmパッケージ群も被害に遭うなど、汚染は急速に広がった。ただし、CrowdStrikeは「パッケージは被害に遭ったが、Falconセンサーやプラットフォームに影響はなく、顧客は保護されている」との声明を発表している。
このワームは、パッケージのメンテナー(管理者)が持つ権限を悪用し、汚染された新たなバージョンを正規のアップデートとして公開する。そのため、開発者は知らず知らずのうちに、悪意のあるコードを自身のプロジェクトに組み込んでしまうことになる。
認証情報を狙う巧妙な手口
「Shai-Hulud」の真の恐ろしさは、その自己増殖の仕組みにある。
感染したパッケージがインストールされると、スクリプトが実行され、開発環境内に存在する認証情報をスキャンする。 具体的には、「TruffleHog」という正規のシークレットスキャンツールを悪用し、GitHubやAWS、Google Cloud、Azureなどのアクセスキーやトークンを探し出すのだ。
窃取された認証情報は、攻撃者が管理するサーバーへ送信される。 そして、その認証情報が持つ権限を使い、ワームはさらに別のパッケージリポジトリへと侵入し、自らを複製して汚染を拡大していく。
この一連のプロセスが自動化されているため、被害はネズミ算式に増えていく。
従来の攻撃と今回の攻撃の、決定的な違い
従来の多くの攻撃は、開発環境を「経由」するだけで、最終的な目的は完成したアプリケーションの利用者だった。しかし、今回は違う。
従来のサプライチェーン攻撃と「Shai-Hulud」を比較すると、その危険性がよくわかるだろう。
観点 | 従来の多くのサプライチェーン攻撃 | 今回の「Shai-Hulud」攻撃 |
---|---|---|
攻撃の主目的 | アプリケーションの利用者から情報を盗む、あるいは利用者のPCで不正な処理をさせる。 | 開発者の認証情報(クラウドやGitHubの鍵)を盗み、それを悪用してさらに別のリポジトリを乗っ取る。 |
感染の仕組み | 汚染されたパッケージをインストールすると、完成したアプリにマルウェアが埋め込まれる。 | 汚染されたパッケージをインストールした瞬間、開発環境内で認証情報をスキャンし、外部に送信する。 |
被害の広がり方 | 攻撃者が手動で別の人気パッケージを狙い、一つひとつ侵害していく必要がある。 | 盗んだ認証情報を使い、ワームが自律的に他のパッケージリポジトリに侵入し、自己増殖する。 |
真の標的 | アプリケーションとその利用者。 | 開発者自身と、その人がアクセス権を持つ全ての開発インフラ(AWS, Google Cloud, Azureなど)。 |
従来の攻撃が「製品に毒を混ぜて、それを買った客を狙う」ものだとすれば、Shai-Huludは「工場の従業員を操り、工場のマスターキーを盗ませる。その鍵で “他の工場” にも忍び込ませて、同じことを繰り返させる」ようなものだ。
開発環境の乗っ取りは、単なる通過点ではなくそれ自体が主目的であり、次の攻撃への発射台そのものになる。
このワームに感染すると、どうなるのか
一度でもこのワームに感染すると、開発者が持つ権限が悪用され、自分が管理する他の無関係なプロジェクトまで汚染されてしまう可能性がある。気付いた時には、自分が被害者であると同時に、攻撃を拡散する加害者の一部にされてしまっているのだ。
この「開発者が攻撃の踏み台にされ、被害が自動で連鎖していく」という点が、今回の攻撃を特別に危険で恐ろしいものにしている最大の理由だ。
現在の状況
2025年9月18日現在、攻撃に使われた悪意のあるパッケージバージョンは既にリポジトリから削除されている。
また、乗っ取られたパッケージの正当な管理者(メンテナー)は、自身のアカウントの制御を取り戻し、セキュリティを確保した上で、問題のないクリーンなバージョンを新たに公開している。
しかし、この攻撃は自己増殖する性質を持ち、盗まれた認証情報が他の場所で悪用される可能性が否定できない。問題が完全に終息したと断定できず、継続的な監視が必要である。
開発者と組織が今すぐ取るべき対策
この脅威から身を守るためには、迅速かつ多層的な防御策が不可欠である。以下に示す対策を直ちに実施することが推奨される。
対策項目 | 具体的なアクション | 期待される効果 |
---|---|---|
依存関係の監査 | npm audit コマンドを定期的に実行し、プロジェクトの依存ツリー全体をスキャンする。不審なスクリプトや意図しないパッケージが含まれていないか、package-lock.json ファイルの内容を精査する。 | 汚染されたパッケージの早期発見と特定。 |
認証情報の管理強化 | CI/CDパイプラインで使用するアクセストークンやシークレットをすべてローテーション(再発行)する。可能であれば、トークンの権限を必要最小限に絞り、有効期間を短く設定する。 | 認証情報が漏洩した場合の被害範囲を限定する。 |
確定的インストールの徹底 | 開発やデプロイの際にはnpm install ではなくnpm ci コマンドを使用する。これにより、package-lock.json に基づいた、意図した通りのバージョンがインストールされることを保証する。 | 意図しないパッケージバージョンの混入を防ぎ、ビルドの再現性を確保する。 |
リポジトリの監視 | 自身の管理するリポジトリで、身に覚えのないパッケージの公開や、不審なGitHub Actionsワークフローが作成されていないかを監視する。 | 攻撃者によるリポジトリの不正利用を早期に検知する。 |
まとめ
npmパッケージを標的とした自己増殖型ワーム「Shai-Hulud」の出現は、ソフトウェア開発におけるサプライチェーンのリスクが新たな段階に入ったことを示している。
依存関係を通じて自動的に汚染が拡大するこの攻撃は、もはや他人事ではない。
開発者は、自らが利用するオープンソースソフトウェアに対して常に警戒を怠らず、今回提示したような対策を日々の開発プロセスに組み込むことが、自らのプロジェクト、そして所属する組織全体を保護する上で不可欠となるだろう。