CurrentStack
#security#supply-chain#javascript#ci/cd#open-source

見えないコード攻撃(GlassWorm系)に備える:npm/GitHub供給網防御プレイブック

JavaScript供給網で増えている脅威は、「悪意あるパッケージ」だけではありません。最近問題化しているのは、見た目の可読性そのものを攻撃する手法です。Unicode制御文字、同形異字(homoglyph)、双方向制御文字などを使い、レビュー画面では正常に見えるのに実行時だけ危険な分岐へ入るコードが混入します。

いわゆるGlassWorm系の不可視コード攻撃は、静的解析の穴ではなく、レビュー設計・CI順序・公開権限管理の穴を突いてきます。

攻撃の典型シナリオ

  1. 攻撃者が新規パッケージを公開、または既存パッケージを侵害
  2. 差分上は安全そうに見えるコードを投入
  3. 非表示文字や見分けにくい識別子で実行挙動を改変
  4. CIは構文/テスト中心のため通過
  5. 特定条件下で本番だけ不正動作

「PRを人間が見たから安全」という前提を崩すのがこの手法の本質です。

なぜ従来対策だけでは不足するのか

多くの組織はすでに依存ロック、脆弱性スキャン、署名確認を導入しています。これらは重要ですが、次の限界があります。

  • 脆弱性DBは既知問題に強いが、新規難読化には弱い
  • 署名は出所検証であり、可読性改ざんの検出ではない
  • テストは想定経路を確認するが、視覚トリック由来分岐は漏れやすい

つまり「見えないコード」を止めるには、別レイヤーの制御が要ります。

防御モデル:検知・遮断・封じ込め

1. 検知

pre-commit/CIに不可視文字検査を導入します。

  • bidi制御文字を原則禁止(例外は明示許可)
  • 混在スクリプト識別子を警告または拒否
  • 正規化(NFKC等)後の差分比較を実施

2. 遮断

ポリシー違反を確実に止めるゲートを設定します。

  • hidden-character検査失敗時はマージ/公開不可
  • package.json, lockfile, install script 変更はCode Owner必須
  • CI内の未承認install scriptネットワーク通信を禁止

3. 封じ込め

検知漏れを前提に被害半径を制限します。

  • CIトークンを最小権限化
  • ビルド系ジョブとデプロイ資格情報を分離
  • リリース成果物へprovenance attestationを付与

まず1週間でやること

① Unicode衛生ポリシーを明文化

言語・リポジトリ種別ごとに許容文字クラスを定義します。通常のアプリ層コードでは制御文字を全面禁止してよいケースが多いです。

② レビュー画面を強化

非表示文字や不可視制御を可視化するdiff表示を有効化し、依存管理ファイルと初期化スクリプトのレビュー手順を追加します。

③ CI順序を前倒し

不可視コード検査は、依存インストールより前に実行します。インストール後に検査しても遅い、というのが実務の落とし穴です。

④ 公開主体の本人性を強化

社内パッケージ公開やBot公開にはOIDCやハードウェア保護鍵を使い、長寿命トークンを廃止します。公開主体の挙動変化(初回公開先、時刻、IP)も監視対象にします。

エンタープライズ向け運用設計

依存導入を2段階で扱うと効果的です。

  • 検疫(quarantine)層:新規依存を一時隔離
  • 信頼(trusted)層:スキャン履歴・由来証跡・短期観測を満たした依存のみ昇格

初回導入速度は少し落ちますが、大規模障害の発生確率を大きく下げられます。

インシデント対応チェックリスト

  1. 新規依存昇格を一時停止
  2. 影響依存グラフを横断調査
  3. クリーンランナーで再ビルド
  4. 実行分岐の異常挙動を比較
  5. 資格情報ローテーションと不審トークン失効

単一パッケージ除去だけで終えると、根本原因(信頼境界の緩さ)が残ります。

成熟度を測る指標

  • hidden-characterゲートを有効化したrepo比率
  • Unicode違反でブロックしたPR数
  • 検知から検疫までの平均時間
  • 署名付きprovenance付きリリース率

ドキュメント量ではなく、これらの指標改善が防御力そのものです。

まとめ

不可視コード攻撃は、「暗号学的に正しい」だけでは守れないことを示しました。可視化されたレビュー、厳格なCIゲート、由来証跡付きリリースを組み合わせて初めて、供給網リスクに耐える開発基盤になります。

おすすめ記事