Gitlab CI/CD に AikidoSec/safe-chain を導入して npm サプライチェーン攻撃対策をしてみた
はじめに
9月に npm サプライチェーン攻撃「Shai-Hulud」が発生しました。
その際に既知のマルウェア対策として AikidoSec/safe-chain を導入した方も多いのではないでしょうか。
公式には GitHub Actions と Azure DevOps がサポートされていますが、今回は GitLab CI/CD に AikidoSec/safe-chain を導入し、実際に悪意のあるパッケージをブロックできることを確認してみました。
AikidoSec/safe-chain とは
AikidoSec/safe-chain は、悪意のあるパッケージのインストールを防ぐためのセキュリティツールです。
Aikido Security というベルギーに本社を置く会社によって作成され、OSS として GitHub に公開されています。
仕組み
仕組みとしては npm コマンドを置き換え、npm パッケージのインストール前にインストール対象のパッケージ名とバージョンの組み合わせがマルウェアリストに記載されているかどうかを調べるというものです。
マルウェアリストは以下の URL に JSON ファイルとして設置されています。
※ ソースコード内に上記 JSON ファイルを参照する記述があります。
対象の CI/CD プラットフォームについて
公式には GitHub Actions と Azure DevOps をサポートしていると記載があります。
実際にソースコード上でも環境変数 GITHUB_PATH
や TF_BUILD
が設定されている場合にパスを通すようになっています。
そこで今回は公式にサポートされていない GitLab CI/CD に AikidoSec/safe-chain を導入してみました。
GitLab CI/CD に AikidoSec/safe-chain を導入してみた
上記で解説した通り、要はパスを通せば動きます。
今回は以下のように、AikidoSec/safe-chain を導入し npm ci
を実行するステージを記述した .gitlab-ci.yml
を作成しました。
default:
image: node:latest
stages:
- setup
setup:
stage: setup
script:
- npm i -g @aikidosec/safe-chain
- safe-chain setup-ci
- export PATH="$HOME/.safe-chain/shims:$PATH"
- npm ci
artifacts:
paths:
- node_modules
ブロックすることを確認する
ブロック確認用として配布されている safe-chain-test のインストールを行い、正しくブロックされパイプラインが停止することを確認します。
npm install safe-chain-test
package.json
は以下の通りです。
{
"name": "example-safe-chain",
"version": "1.0.0",
"dependencies": {
"safe-chain-test": "^0.0.1-security"
}
}
もしローカルに AikidoSec/safe-chain を導入しており、インストールがブロックされる場合は、以下のように --safe-chain-malware-action=prompt
をつけてインストールを行いましょう。
npm install safe-chain-test --safe-chain-malware-action=prompt
そうして出力された package-lock.json
は以下になります。
{
"name": "example-safe-chain",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "example-safe-chain",
"version": "1.0.0",
"dependencies": {
"safe-chain-test": "^0.0.1-security"
}
},
"node_modules/safe-chain-test": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/safe-chain-test/-/safe-chain-test-0.0.1-security.tgz",
"integrity": "sha512-nJoRuRb52IWYNLNX/Bpwot6w+1U1cykpp08eTUdqZOoJ3AcJkiOi4hrHJx4OtT/c4wbK7MoDlKi763DP8BgD2Q=="
}
}
}
そして gitlab-ci.yml
, package.json
, package-lock.json
の3つをコミット、プッシュし、パイプラインの実行を確認します。
無事失敗を確認できたら成功です。
以下のログに blocked by safe-chain
と記述があり、ブロックしてくれたことが確認できました。
$ npm i -g @aikidosec/safe-chain
added 109 packages in 6s
28 packages are looking for funding
run `npm fund` for details
$ safe-chain setup-ci
Setting up shell aliases. This will wrap safe-chain around npm, npx, yarn, pnpm, pnpx, bun, and bunx commands.
Created 7 Unix shim(s) in /root/.safe-chain/shims
Created shims in /root/.safe-chain/shims
Added shims directory to PATH for CI environments.
$ export PATH="$HOME/.safe-chain/shims:$PATH"
$ npm ci
npm error code E403
npm error 403 403 Forbidden - blocked by safe-chain - GET https://registry.npmjs.org/safe-chain-test/-/safe-chain-test-0.0.1-security.tgz
npm error 403 In most cases, you or one of your dependencies are requesting
npm error 403 a package version that is forbidden by your security policy, or
npm error 403 on a server you do not have access to.
npm error A complete log of this run can be found in: /root/.npm/_logs/2025-10-19T07_11_27_127Z-debug-0.log
Safe-chain: blocked 1 malicious package downloads:
- safe-chain-test@0.0.1-security (https://registry.npmjs.org/safe-chain-test/-/safe-chain-test-0.0.1-security.tgz)
Exiting without installing malicious packages.
Cleaning up project directory and file based variables
00:00
ERROR: Job failed: exit code 1
さいごに
GitLab CI/CD に AikidoSec/safe-chain を導入してみました。
パスを通すというやり方ですので GitLab CI/CD 以外にも CodeBuild や Jenkins などでも同様のやり方で導入することが可能です。
当初はサポートされていなかった bun も 1.1.0 で対応され、主要 npm パッケージマネージャーはすべて対応したので、CI/CD プラットフォームもサポート対象が増えることに期待しています。