記事一覧に戻る
security-news2026年5月1日37分で読めます

SAP Side-by-Side開発者を狙うnpm供給網攻撃 — Mini Shai-Hulud

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による根本対策まで徹底解説。

SAPBTPCAP供給網攻撃npmSide-by-SideMini Shai-HuludSafe 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.0
  • mbt 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 Side-by-Side開発者にとって致命的か

SAP S/4HANA Cloud Public Editionなどに対するカスタム拡張。コア(本体)を触らず、外側にカスタムロジックを置く Side-by-Side拡張が主流です。

BTPでCAPアプリを書くのが代表的なパターン。ただ最近は自前のEC2やKubernetes、オンプレで同じことをやる組織も増えています。

「BTPじゃなくて自前サーバーだから関係ない」が成立しないのは、現場の実態がこうだからです。

  • CAPで書いたNode.jsアプリをDocker化して任意の環境にデプロイ
  • BTPに乗せる場合も乗せない場合も、mbt build をCI/CDに組み込んでいるケースが大多数
  • 開発者ローカルで npm install した瞬間、ペイロードは作動

つまり「BTPで動かしているか」は問題ではありません。「@cap-js/* か mbt を依存に持っているか」が問題です。

ローカル開発機。GitHub Actionsランナー。自前のJenkins。CodeBuild。どこでもいい。汚染バージョンを npm install した瞬間にゲームは始まっています。

何が盗まれるか

Aikidoの解析によると、execution.js が窃取対象にしているのは以下です。

  • GitHubトークン(gh auth token の出力含む)
  • npmトークン(.npmrc から)
  • 環境変数すべて
  • GitHub Actions secrets(専用のメモリダンパーで Runner.Worker プロセスから抽出)
  • AWS STS identity、Secrets Manager、SSM Parameters
  • Azure Subscriptions、Key Vault、Key Vault secrets
  • GCP Project identity、Secret Manager
  • Kubernetes Service Account tokens
  • Claude config、MCP config、GCPトークンDB、Azureトークンキャッシュ、Signal config、Electrum wallet、VPN config

「環境変数すべて」が対象です。ここから先、開発者の実装スタイルによって漏れるものが大きく変わります。

正しく実装している場合 — SAP認証は守られる

CAPには本来、SAP認証情報をコードや環境変数から完全に排除する仕組みが用意されています。

Destination Service

BTPのサービスで、バックエンド接続情報の集中管理庫です。

  • 接続先URL
  • 認証方式(Basic / OAuth / Principal Propagation / mTLS / SAML Assertion)
  • 認証情報そのもの

これらを 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が裏で組み立てます。

Principal Propagation

さらに強力なのがこれ。

[Fiori UIユーザー]
   ↓ JWT(本人)
[BTP上のCAPアプリ]
   ↓ Destination Service が「本人として」叩く
[S/4HANA(本人の権限で動く)]

エンドユーザーの認証情報がそのままバックエンドまで透過的に伝搬される仕組み。CAPアプリ側にSAPの「サービスユーザー」を持つ必要がありません。

Credential Store

BTPには SAP Credential Store という暗号化シークレット保管庫サービスもあります。AWS Secrets Manager相当の機能をSAPエコシステム内で提供。Destination Serviceに格納できない種類の認証情報(外部APIキー、独自の暗号鍵等)もここに集約可能です。

この構成だと、Mini Shai-Hulud攻撃で何が起きるか

汚染パッケージが npm install で実行されても:

  • .env には SAP_USERNAME も SAP_PASSWORD もそもそも存在しない
  • 環境変数を全部スクレイプしても、SAP認証情報は出てこない
  • 攻撃者が手にするのはせいぜい XSUAA Service Key
  • そこから Destination Service にアクセスする壁がもう一段ある

正しく実装している開発者にとって、SAP認証情報の漏洩は発生しません。

これがCAP + BTPの本来の姿です。

ただし「使える」と「使えている」は違う

ここが今回の記事で一番伝えたい部分です。

SAPは仕組みを用意しています。Destination Service、Principal Propagation、Credential Store、Communication Arrangement、IAS — どれも本来は強力です。

しかし現場で実際に使うには、以下のような壁があります。

  • 権限の壁 — Cloud Foundry Spaceの権限、Global Adminの権限、Communication Arrangementの作成権限が分散している
  • 理解の壁 — Destination Service、IAS、XSUAA、Service Binding、Service Key、Trust Configuration、Communication Scenario、Communication System、Communication Arrangement、Communication User…用語が多すぎる
  • 構成の壁 — Cloud Foundry Spaceにしかインスタンスを作れない、サブアカウント間でService Instance Sharingが必要、proxyプランの共有元を特定しないといけない、など制約が複雑
  • ドキュメントの壁 — SAP公式ドキュメントは膨大だが、「自分の状況に当てはめてどう動けばいいか」がわかりにくい
  • EC2/オンプレ等BTP外の現実 — BTP上で動かしていればService Bindingで自動配線されるが、自前のEC2やKubernetesで動かす場合、Service Keyを発行して外部から呼び出す形になり、結局そのService KeyをEC2のどこかに持つ必要がある

結果として、多くの現場で起こっていること:

  • Destination Service があると知っているが、設定方法がわからず.envに直書き
  • Credential Store のEntitlementは持っているが、Cloud Foundry Spaceの権限調整がつかず使えない
  • proxyプランは割り当てられているが、共有元のインスタンスがどこか誰も知らない
  • 「動けばいい」で.envに直書き、リファクタする時間がない
  • BTP外で動かしているので、結局Service Keyを切り出して環境変数化

つまり「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 CredentialsOAUTH_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_BUSINESS_PARTNER) — 全顧客の名前・住所・与信
  • 仕入先マスタ + 銀行口座情報(API_BUSINESS_PARTNER) — BEC攻撃の温床
  • 価格表・条件マスタ(API_SALES_PRICING) — 競合に渡れば致命的
  • 財務データ(API_OPLACCTGDOCITEMCUBE_SRV など) — 未公開業績
  • 受注・契約情報(API_SALES_ORDER_SRV) — 顧客別売上、未公開受注
  • プロジェクト原価(API_ENTERPRISE_PROJECT_SRV) — 案件採算
  • 給与・人事データ(Workforce/Timesheet) — 個人情報

Readリスク(データ抜き取り)

これは確実に発生します。検知も遅れます。

理由は単純です。API Gatewayは普段から大量データをReadしているシステム。攻撃者の大量Readが「いつもの動き」に紛れます。

抜かれたデータの用途は広い。競合への横流し。ランサム交渉材料。BEC下準備。闇市場。何にでも使えます。

Writeリスク(SAPへの書き込み)

「SAPはCSRFトークンが必要だから書き込みは難しい」。こういう反論を受けることがあります。

確かにOData/SOAPのPOSTは2段階です。事前の x-csrf-token: fetch GETでトークンを取得。同セッションで送る必要がある。

ただしこれはブラウザ起点のCSRF攻撃を防ぐスピードバンプです。認証情報を持った攻撃者には何の障害にもなりません。

攻撃者は自分のサーバーから単にHTTP 2発打てばいい。Cookie Jarでセッションを維持するスクリプトは数行で書けます。

現実的なWriteシナリオ:

  • 静かなBEC — 仕入先マスタの銀行口座を1件だけ書き換える。次の支払サイクルで自動的に攻撃者口座へ着金。気付いた時には手遅れ
  • 不正Journal Entry — 既存仕入先への正規パターンのJE+Clearingを少量作成。月次決算に紛れ込ませる
  • 受注・出荷の改ざん — 偽受注を作って真の顧客に偽物を出荷させる。サプライチェーン攻撃の踏み台

少量・正規フォーマット・営業時間内のWriteは、攻撃者がプロなら検知をすり抜けます。

外部からの侵入経路 — 多段ピボット

最初の侵入は「内側から」です。誰かが npm install を実行した瞬間にペイロードが作動。

しかしその後、攻撃者は外部から自由に出入りできる状態を構築します。

  1. 開発者PCもしくはCIランナーで preinstall フックが起動
  2. 認証情報を全部スクレイプ。AES-256-GCMで暗号化(AESキーはRSA公開鍵でラップ)
  3. 暗号化済みデータを 公開GitHubリポジトリ へコミットしてexfil(リポジトリのdescriptionは A Mini Shai-Hulud has Appeared)
  4. 盗んだGitHubトークンで他のリポジトリへピボット
  5. 横展開先のリポに .vscode/tasks.json、.claude/execution.js、.claude/settings.json などを書き込み
    • コミットメッセージ: chore: update dependencies
    • 作者名: claude <claude@users.noreply.github.com>
  6. 盗んだAWS/Azure/GCP認証情報でクラウドへピボット — ここで 外部侵入が完成

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(コミットメッセージ)
  • リポジトリdescriptionに A Mini Shai-Hulud has Appeared
  • コミッター claude <claude@users.noreply.github.com>
  • 不審な .claude/ や .vscode/setup.mjs の追加

② 応急処置

  • 該当バージョンを即pin downgrade(@cap-js/sqlite@2.2.1 などへ)
  • node_modules を完全削除して npm ci で入れ直し
  • 感染が確定したマシンは OS再インストールを推奨(マルウェアがデーモン化している)

③ 認証情報のローテーション(漏れた可能性のあるもの全部)

「環境変数すべて」が対象です。漏れた可能性のあるものはすべてローテーションします。

  • GitHub PAT、GitHub Actions secrets
  • npmトークン
  • AWS access key / IAMロール
  • Azure Service Principal、Key Vault secrets
  • GCP Service Account key
  • Kubernetes Service Account tokens
  • SAP Communication User のパスワード(.env に書いていた場合)
  • BTP Service Key、Cloud Foundry CLIトークン
  • DB接続情報
  • MCPサーバ連携トークン

④ 監査

  • GitHub Actions実行ログを過去7日分確認
  • AWS CloudTrail / Azure Activity Log / GCP Audit Logで異常アクセス確認
  • 自組織から chore: update dependencies 名義のコミットが出ていないか
  • SAP側のApplication LogとAudit Logで、Communication Userの不審なAPI呼び出しがないか確認

中長期の対策 — 認証情報を .env から外す

応急処置の先、根本的に再発を防ぐ方法はこれです。

A. BTPに乗せられる場合 — Destination Service + Principal Propagation

CAPアプリをBTPで動かすなら、これが本来の正解。

  • BTP Cockpitに Destination を作成
  • 認証方式は Principal Propagation(またはOAuth User Token Exchange)
  • CAPコードは cds.connect.to('S4_DESTINATION') のみ
  • 環境変数にSAPのID/Passwordは一切書かない

B. BTP外(EC2、Kubernetes、オンプレ)で動かしている場合

.env から認証情報を排除する選択肢:

  • AWS Secrets Manager — IAM Roleで取得、CloudTrailで監査
  • HashiCorp Vault — マルチクラウド、Dynamic Secretsが強力
  • AWS Systems Manager Parameter Store — 軽量・低コスト
  • SAP Credential Store — BTPサブスクリプションがあれば利用可能(ただしSpace権限等の調整が必要)

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 漏洩経路は無効化されます。

Communication User を用途別に分離する

「全部入りユーザー」を1つだけ作って使い回すのは、被害範囲を最大化する設定です。

シナリオごとに分離します。

  • FI(Journal Entry)用 Communication User
  • BP(Business Partner)用 Communication User
  • SO(Sales Order)用 Communication User
  • Workforce用 Communication User

各ユーザーが持つCommunication Arrangementは「そのシナリオに必要な最小限のスコープのみ」。万一漏れても被害が一部に限定されます。

統合基盤側のconfigファイル(sap_config.yml など)で シナリオ別にユーザーを分けてログイン する実装にしておく。これで運用上の手間も最小限です。

ネットワークレイヤーでの防御の現実

「Communication Systemに送信元IP制限を掛ける」のは理想ですが、SAP S/4HANA Cloud Public Edition本体には、顧客が直接設定できるIP Allowlist機能はありません。これはSaaSの設計上の制約です。

代替策として現実的なのは:

  • BTP CPI (Integration Suite) を関所にする — EC2/オンプレ → CPI → S/4HANA。CPI側はIP制限可能で、SAP側は「CPIから受け付ける」運用ルールに統一
  • AWS NAT Gateway / 出口IP固定 — 自社統合基盤からの送信元IPを固定し、Audit Logで「想定外IPからの呼び出し」を検知できる前提を作る
  • IAS (Identity Authentication Service) の Risk-Based Authentication — IP範囲によるリスク評価(ただしユーザーログイン用、Communication User経由には限定的)
  • インフラチームとの連動 — 完全な防御ではなく「監査と異常検知の前提条件を作る」防御

完璧な閉じ方は難しいので、認証情報の保護(.envから外す)を最優先で。

SAP Audit Log + 異常検知

Manage Logsで、Communication Userごとの呼び出しパターンを定常的に監視します。

  • 想定外時刻のAPI呼び出し
  • 想定外シナリオへの呼び出し
  • 想定外のボリューム
  • 想定外のSource IP

これらをSplunk/Datadog/CloudWatch Logsへエクスポートしてアラート化。攻撃が始まってから検知までのタイムラグを最小化できます。

それでもSafe Chainは入れた方がいい

ここまで読んで「うちはDestination Service使ってるから関係ない」と思った方。それでもSafe Chainは入れるべきです。

理由は2つ。

理由1: SAP認証以外も全部対象

Destination Serviceで守れるのはSAP認証情報だけです。今回の攻撃で漏れる対象は他にも大量にあります。

  • GitHubトークン(コード全部読まれる、書き込まれる)
  • AWS / Azure / GCP の認証情報(クラウド全体掌握)
  • Kubernetes Service Account(クラスタ侵入)
  • Claude config / MCP config(AIエージェント連携経路)
  • DB接続情報、各種APIキー

SAPは守れても、開発環境全体は守れない。Safe Chainは npm 経由の侵入そのものをブロックします。

理由2: 1コマンドで導入できる無料ツール

Aikido Safe Chain(OSS、無料)は、npm install、npx、yarn、pnpm、pnpx のコマンドに割り込みます。Aikido Intelの脅威フィードに対してパッケージを照合してからインストールを実行する仕組み。

  • 新規公開から48時間のウィンドウ を設定
  • その間にAikidoとセキュリティリサーチャーがチェック
  • 新規公開直後の汚染パッケージは、48時間ウィンドウ中はインストールがブロックされる
  • 既存ユーザーは普段通り 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系の依存ツリーが大きい組織は、購読しておく価値があります。

まとめ

  • 対象パッケージ4つ を即座に検出・隔離。@cap-js/sqlite@2.2.2、@cap-js/postgres@2.2.2、@cap-js/db-service@2.10.0、mbt@1.2.48
  • 「BTPで動かしてないから無関係」は誤り。Side-by-Side開発で mbt をCIに使っていれば射程内
  • 正しく実装していればSAP認証は守られる。Destination Service + Principal Propagationを使えば、.env にSAP認証情報を書く必要はない
  • 正しく実装できていない場合は危険。.env 直書きは今回の攻撃で全部漏れる
  • 「使える」と「使えている」は別問題。SAPの仕組みは存在するが、権限調整・構成理解の壁で現場で使えていないケースが多い。これを機にBTPの仕組みを使う方向で動くべきだが、すぐ使えない場合はAWS Secrets Manager等で代替する
  • 本物の対策は認証情報を.envから外すこと。手段はBTP Destination Service、AWS Secrets Manager、HashiCorp Vault、SAP Credential Store等。組織状況に合わせて選ぶ
  • Safe Chainは全員入れるべき。SAP認証を守れても、GitHub/AWS/Claude config等は別経路で漏れる。1コマンド・無料

SAPは会社の心臓部です。

その心臓に繋がるパイプを npm install 一発で奪い取られる時代に、私たち開発者は入っています。

BTPには本来、こうした攻撃から認証情報を守る仕組みが用意されています。せっかくの機能なので、これを機に正しく使いましょう。

そしてSafe Chainは、その上にもう一枚かけるガードレール。両方やって、ようやく安心です。


参考リンク

キーワード

  • SAP
  • BTP
  • CAP
  • 供給網攻撃
  • npm
  • Side-by-Side
  • Mini Shai-Hulud
  • Safe Chain
A

Aikido Community Japan

公開日: 2026/5/1

最終更新: 2026/5/1

シェア:

ニュースを購読

セキュリティの最新情報をお届けします。

Aikido Community Japan からのお知らせの受信に同意します。詳しくはプライバシーポリシーをご確認ください。