見えないコード攻撃(GlassWorm系)に備える:npm/GitHub供給網防御プレイブック
JavaScript供給網で増えている脅威は、「悪意あるパッケージ」だけではありません。最近問題化しているのは、見た目の可読性そのものを攻撃する手法です。Unicode制御文字、同形異字(homoglyph)、双方向制御文字などを使い、レビュー画面では正常に見えるのに実行時だけ危険な分岐へ入るコードが混入します。
いわゆるGlassWorm系の不可視コード攻撃は、静的解析の穴ではなく、レビュー設計・CI順序・公開権限管理の穴を突いてきます。
攻撃の典型シナリオ
- 攻撃者が新規パッケージを公開、または既存パッケージを侵害
- 差分上は安全そうに見えるコードを投入
- 非表示文字や見分けにくい識別子で実行挙動を改変
- CIは構文/テスト中心のため通過
- 特定条件下で本番だけ不正動作
「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)層:スキャン履歴・由来証跡・短期観測を満たした依存のみ昇格
初回導入速度は少し落ちますが、大規模障害の発生確率を大きく下げられます。
インシデント対応チェックリスト
- 新規依存昇格を一時停止
- 影響依存グラフを横断調査
- クリーンランナーで再ビルド
- 実行分岐の異常挙動を比較
- 資格情報ローテーションと不審トークン失効
単一パッケージ除去だけで終えると、根本原因(信頼境界の緩さ)が残ります。
成熟度を測る指標
- hidden-characterゲートを有効化したrepo比率
- Unicode違反でブロックしたPR数
- 検知から検疫までの平均時間
- 署名付きprovenance付きリリース率
ドキュメント量ではなく、これらの指標改善が防御力そのものです。
まとめ
不可視コード攻撃は、「暗号学的に正しい」だけでは守れないことを示しました。可視化されたレビュー、厳格なCIゲート、由来証跡付きリリースを組み合わせて初めて、供給網リスクに耐える開発基盤になります。