2026年4月29日に発覚したnpm供給網攻撃 Mini Shai-Hulud。@cap-js/sqlite、@cap-js/postgres、@cap-js/db-service、mbtの4パッケージが汚染され、SAP CAP/BTP開発者のエコシステムが標的に。Destination ServiceやPrincipal Propagationを正しく使っていればSAP認証は守られるが、.envに直書きしていると即漏洩。SAPの仕組みと現場ギャップ、認証情報を.envから外す具体策、Safe Chainによる根本対策まで徹底解説。
2026年4月29日、Aikidoのリサーチチームが新しいnpm供給網攻撃の警告を出しました。今回の標的は SAP開発者のエコシステム です。
汚染が確認されているのは以下の4パッケージ。
@cap-js/sqlite v2.2.2@cap-js/postgres v2.2.2@cap-js/db-service v2.10.0mbt v1.2.48「自分はBTPで開発してないから関係ない」と思った方。それは誤りです。
mbt は BTPデプロイのCI/CDで実質ほぼ全員が通るツールです。Side-by-Side拡張開発をやっていれば、ローカルかGitHub Actionsのどこかで使っているはず。
本記事には、もう一つ重要な前提があります。
SAP認証情報が
.envから漏れるかどうかは、開発者の実装次第です。
CAP + Destination Service + Principal Propagationを 正しく組み合わせていれば、SAPのID/Passwordはローカルにも環境変数にもCI Secretsにも存在しません。今回の攻撃でも漏れません。
逆に雑に.envに書いているなら、その瞬間に漏れます。
つまり今回の攻撃は、Destination Serviceなどの仕組みを正しく使えていない開発者にとって深刻。仕組みを正しく使っている人にとっては、SAP認証は守られています。
ただし注意点があります。SAPには「仕組みは存在するが、現場で実際に使うには社内調整・権限取得・設定理解の壁がある」ものが多い。「使えるはず」と「使えている」の間には大きなギャップがあります。
そして仕組みを正しく使っていても、Safe Chainは入れた方がいい。SAP認証以外にも漏れるものは多いからです。
@cap-js 系のパッケージは SAP Cloud Application Programming Model (CAP) のデータベースサービス層です。mbt は Cloud MTA Build Tool、BTPへデプロイする .mtar アーカイブを生成するビルドツール。
どちらもSAP開発者なら必ずどこかで触る標準ツールです。
攻撃者は正規パッケージのpackage.jsonに preinstall フックを仕込みました。
"scripts": {
"preinstall": "node setup.mjs"
}
npm install を実行した瞬間、インストール完了を待たずに setup.mjs が走ります。
第1段階の setup.mjs はBunブートストラッパーです。OSとアーキテクチャを判定。GitHub公式リポジトリから Bun 1.3.13 ランタイムを勝手にダウンロード します。そのBunを使って第2段階の execution.js を実行する流れ。
Node.jsだけでは動かない設計にすることで、防御側の解析を遅らせる狙いです。
第2段階の execution.js は 11.7MB の難読化ペイロード。CI環境を検知し、ロシア語ロケール下では実行を停止します。それ以外のマシンではデーモン化。
汚染されたバージョン以外のファイル(正規のCAPコード)は、クリーンな前バージョンとバイトレベルで一致しています。つまり「動作は正常、悪意あるコードは陰で別動」。検知が極めて難しい構造です。
SAP S/4HANA Cloud Public Editionなどに対するカスタム拡張。コア(本体)を触らず、外側にカスタムロジックを置く Side-by-Side拡張が主流です。
BTPでCAPアプリを書くのが代表的なパターン。ただ最近は自前のEC2やKubernetes、オンプレで同じことをやる組織も増えています。
「BTPじゃなくて自前サーバーだから関係ない」が成立しないのは、現場の実態がこうだからです。
mbt build をCI/CDに組み込んでいるケースが大多数npm install した瞬間、ペイロードは作動つまり「BTPで動かしているか」は問題ではありません。「@cap-js/* か mbt を依存に持っているか」が問題です。
ローカル開発機。GitHub Actionsランナー。自前のJenkins。CodeBuild。どこでもいい。汚染バージョンを npm install した瞬間にゲームは始まっています。
Aikidoの解析によると、execution.js が窃取対象にしているのは以下です。
gh auth token の出力含む).npmrc から)「環境変数すべて」が対象です。ここから先、開発者の実装スタイルによって漏れるものが大きく変わります。
CAPには本来、SAP認証情報をコードや環境変数から完全に排除する仕組みが用意されています。
BTPのサービスで、バックエンド接続情報の集中管理庫です。
これらを BTP Cockpit上で設定し、暗号化保管します。
CAPアプリのコードはこうなる:
// destination名を指定するだけ
const s4 = await cds.connect.to('S4HANA_API')
// あとは普通のCDSクエリ
const customers = await s4.read('A_BusinessPartner')
Basic認証情報がコードにも .env にも一切出てこない。Destination名という抽象化されたラベルしかコード上に存在しません。実際の認証ヘッダはDestination Serviceが裏で組み立てます。
さらに強力なのがこれ。
[Fiori UIユーザー]
↓ JWT(本人)
[BTP上のCAPアプリ]
↓ Destination Service が「本人として」叩く
[S/4HANA(本人の権限で動く)]
エンドユーザーの認証情報がそのままバックエンドまで透過的に伝搬される仕組み。CAPアプリ側にSAPの「サービスユーザー」を持つ必要がありません。
BTPには SAP Credential Store という暗号化シークレット保管庫サービスもあります。AWS Secrets Manager相当の機能をSAPエコシステム内で提供。Destination Serviceに格納できない種類の認証情報(外部APIキー、独自の暗号鍵等)もここに集約可能です。
汚染パッケージが npm install で実行されても:
.env には SAP_USERNAME も SAP_PASSWORD もそもそも存在しない正しく実装している開発者にとって、SAP認証情報の漏洩は発生しません。
これがCAP + BTPの本来の姿です。
ここが今回の記事で一番伝えたい部分です。
SAPは仕組みを用意しています。Destination Service、Principal Propagation、Credential Store、Communication Arrangement、IAS — どれも本来は強力です。
しかし現場で実際に使うには、以下のような壁があります。
結果として、多くの現場で起こっていること:
.envに直書き.envに直書き、リファクタする時間がないつまり「SAPには立派な仕組みがあるが、現場で使えていない」というギャップが広く存在します。これを認識した上で対策を考える必要があります。
.env に SAP 認証情報が直書きされている場合、以下が 全部漏れます。
| 漏洩対象 | 典型的な保管先 |
|---|---|
| SAP API認証情報(Communication User ID/Password) | ~/.env、SAP_USERNAME/SAP_PASSWORD 環境変数、CI/CD secrets |
| BTP Service Key JSON | 環境変数、.env、Kubernetes Secret |
| HANA Cloud接続情報 | 環境変数、.env |
| OAuth Client Credentials | OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET 環境変数 |
| API Gateway / Reverse Proxyのキー | .env、CI secrets |
| MCP-AIエージェント連携トークン | MCP config |
ここで重要なのは認証方式の話。
「うちはOAuth Client Credentialsだから安全」「Client Certificate認証にしてるから大丈夫」。
これらは攻撃者が認証情報そのものを持っていない前提でのみ意味があります。
.env に OAUTH_CLIENT_SECRET=... と書いてあれば、SAP_PASSWORD=...と書いてあるのと変わりません。
認証情報が手元から漏れた瞬間、その認証方式の強度は無関係になる。これが供給網攻撃の本質です。
もし認証情報が漏れた場合、API Gateway的な統合用Communication Userは、業務上の必要から広い権限を持っているのが普通です。たとえば以下が読めます。
これは確実に発生します。検知も遅れます。
理由は単純です。API Gatewayは普段から大量データをReadしているシステム。攻撃者の大量Readが「いつもの動き」に紛れます。
抜かれたデータの用途は広い。競合への横流し。ランサム交渉材料。BEC下準備。闇市場。何にでも使えます。
「SAPはCSRFトークンが必要だから書き込みは難しい」。こういう反論を受けることがあります。
確かにOData/SOAPのPOSTは2段階です。事前の x-csrf-token: fetch GETでトークンを取得。同セッションで送る必要がある。
ただしこれはブラウザ起点のCSRF攻撃を防ぐスピードバンプです。認証情報を持った攻撃者には何の障害にもなりません。
攻撃者は自分のサーバーから単にHTTP 2発打てばいい。Cookie Jarでセッションを維持するスクリプトは数行で書けます。
現実的なWriteシナリオ:
少量・正規フォーマット・営業時間内のWriteは、攻撃者がプロなら検知をすり抜けます。
最初の侵入は「内側から」です。誰かが npm install を実行した瞬間にペイロードが作動。
しかしその後、攻撃者は外部から自由に出入りできる状態を構築します。
preinstall フックが起動A Mini Shai-Hulud has Appeared).vscode/tasks.json、.claude/execution.js、.claude/settings.json などを書き込み
chore: update dependenciesclaude <claude@users.noreply.github.com>GitHubの伝達経路として、攻撃者は OhNoWhatsGoingOnWithGitHub:<base64> パターンのコミットメッセージでトークンの dead-drop を行っています。
これは攻撃者が新しいトークンを獲得するたびに更新できる構造です。マルウェア自体を再配布せずに、攻撃者が指令を更新できる仕組み。
すべてのリポジトリ、ローカル、CIサーバーで以下を確認します。
# lockfile全部grep
grep -r "@cap-js/sqlite.*2.2.2" .
grep -r "@cap-js/postgres.*2.2.2" .
grep -r "@cap-js/db-service.*2.10.0" .
grep -r "mbt.*1.2.48" .
# node_modules配下
find . -name "setup.mjs" -path "*/node_modules/*"
find . -name "execution.js" -path "*/node_modules/*"
# package.jsonに不審なpreinstallがないか
grep -r '"preinstall".*"node setup.mjs"' .
GitHub組織内検索:
OhNoWhatsGoingOnWithGitHub(コミットメッセージ)A Mini Shai-Hulud has Appearedclaude <claude@users.noreply.github.com>.claude/ や .vscode/setup.mjs の追加@cap-js/sqlite@2.2.1 などへ)node_modules を完全削除して npm ci で入れ直し「環境変数すべて」が対象です。漏れた可能性のあるものはすべてローテーションします。
.env に書いていた場合)chore: update dependencies 名義のコミットが出ていないか.env から外す応急処置の先、根本的に再発を防ぐ方法はこれです。
CAPアプリをBTPで動かすなら、これが本来の正解。
cds.connect.to('S4_DESTINATION') のみ.env から認証情報を排除する選択肢:
EC2基盤で動いているなら、AWS Secrets Manager + IAM Role が最も現実的。改修1〜2日、効果すぐ出ます。
import boto3, json
from functools import lru_cache
@lru_cache(maxsize=1)
def get_sap_credentials():
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='sap/prod/api_gateway')
return json.loads(response['SecretString'])
.env から SAP_PASSWORD=... を削除すれば、.env 漏洩経路は無効化されます。
「全部入りユーザー」を1つだけ作って使い回すのは、被害範囲を最大化する設定です。
シナリオごとに分離します。
各ユーザーが持つCommunication Arrangementは「そのシナリオに必要な最小限のスコープのみ」。万一漏れても被害が一部に限定されます。
統合基盤側のconfigファイル(sap_config.yml など)で シナリオ別にユーザーを分けてログイン する実装にしておく。これで運用上の手間も最小限です。
「Communication Systemに送信元IP制限を掛ける」のは理想ですが、SAP S/4HANA Cloud Public Edition本体には、顧客が直接設定できるIP Allowlist機能はありません。これはSaaSの設計上の制約です。
代替策として現実的なのは:
完璧な閉じ方は難しいので、認証情報の保護(.envから外す)を最優先で。
Manage Logsで、Communication Userごとの呼び出しパターンを定常的に監視します。
これらをSplunk/Datadog/CloudWatch Logsへエクスポートしてアラート化。攻撃が始まってから検知までのタイムラグを最小化できます。
ここまで読んで「うちはDestination Service使ってるから関係ない」と思った方。それでもSafe Chainは入れるべきです。
理由は2つ。
理由1: SAP認証以外も全部対象
Destination Serviceで守れるのはSAP認証情報だけです。今回の攻撃で漏れる対象は他にも大量にあります。
SAPは守れても、開発環境全体は守れない。Safe Chainは npm 経由の侵入そのものをブロックします。
理由2: 1コマンドで導入できる無料ツール
Aikido Safe Chain(OSS、無料)は、npm install、npx、yarn、pnpm、pnpx のコマンドに割り込みます。Aikido Intelの脅威フィードに対してパッケージを照合してからインストールを実行する仕組み。
npm install するだけ。ワークフロー変更不要今回のMini Shai-Hulud攻撃の場合。汚染バージョンの公開直後にAikidoのリサーチチームが検知・公開しています。
Safe Chainがインストールされていれば、攻撃発覚前にうっかり汚染バージョンを取得してしまうリスクはほぼゼロでした。
導入は1コマンド:
npx @aikidosec/safe-chain install
無料。IDE Command経由でも動作。既存プロジェクトに副作用なし。
組織全体のデバイス保護を考えるなら Aikido Endpoint Protection も有効です。ブラウザ拡張、コードライブラリ、IDEプラグイン、ビルド依存をまとめて可視化・制御できます。
Mini Shai-Hulud攻撃は単発ではありません。
Bitwarden CLIを狙ったShai-Hulud: The Third Coming。PyPIとnpmを狙ったGPT-Proxy Backdoor。これらに続く、明らかに 連続した攻撃キャンペーン です。
次がどのエコシステムを狙うか。現時点では不明です。ただSAP/CAPエコシステムが一度狙われた以上、再度狙われる可能性は高いと考えるべき。
Aikido Intel は無料の脅威フィードです。新しいマルウェア・脆弱性情報がリアルタイムで配信されます。SAP系の依存ツリーが大きい組織は、購読しておく価値があります。
@cap-js/sqlite@2.2.2、@cap-js/postgres@2.2.2、@cap-js/db-service@2.10.0、mbt@1.2.48mbt をCIに使っていれば射程内.env にSAP認証情報を書く必要はない.env 直書きは今回の攻撃で全部漏れる.envから外すこと。手段はBTP Destination Service、AWS Secrets Manager、HashiCorp Vault、SAP Credential Store等。組織状況に合わせて選ぶSAPは会社の心臓部です。
その心臓に繋がるパイプを npm install 一発で奪い取られる時代に、私たち開発者は入っています。
BTPには本来、こうした攻撃から認証情報を守る仕組みが用意されています。せっかくの機能なので、これを機に正しく使いましょう。
そしてSafe Chainは、その上にもう一枚かけるガードレール。両方やって、ようやく安心です。
参考リンク
Aikido Community Japan
公開日: 2026/5/1
最終更新: 2026/5/1
セキュリティの最新情報をお届けします。