2026年5月末、OpenAI Codex CLI利用者を狙ったnpmサプライチェーン攻撃が明るみに出た。偽パッケージcodexui-androidが、Codexの認証ファイル~/.codex/auth.jsonから無期限のrefresh tokenを含む認証情報一式を窃取していた。GitHub上のソースはクリーンに保たれ、npmに公開されたパッケージにだけマルウェアが仕込まれるという手口で、約1か月にわたり気づかれずに動作していた。
AI開発ツールを狙う攻撃の全体像と脅威モデルはAIセキュリティとは?LLM時代の脅威モデル・代表的リスク・OSS対策ツールを体系解説する入門ガイドで俯瞰しています。
30秒で理解する
- ・偽npmパッケージ
codexui-androidがOpenAI Codex CLIユーザーの~/.codex/auth.jsonを窃取していた。 - ・GitHubはクリーン、npm版のみマルウェア。「読み手はGitHubを確認するから安心」という心理を逆手に取った戦術。
- ・窃取対象は無期限のrefresh tokenを含む認証情報一式で、攻撃者は永続的になりすまし可能になる。
- ・Aikidoが2026-05-27に検知、2026-06-02に各メディアが一斉報道。週27,000〜29,000ダウンロード、モバイル併せて60,000超のインストール規模。
- ・対応:該当パッケージの有無を確認し、入れていたらOpenAIアカウントのパスワード変更+全セッションサインアウトでrefresh tokenを失効させる。APIキーのrotateだけでは不十分。
本記事は攻撃コードやペイロードの詳細は載せない。検知・対応・予防に振り切って解説する。
何が起きたか
事の発端はAikido Securityの研究者Charlie Eriksen氏による検知だ。2026-05-27、同氏はcodexui-androidというnpmパッケージが認証情報を外部サーバーへ送信していることを突き止めた。その後2026-06-02にThe Hacker News、CSO Online、TechRadar、hackreadなど複数メディアが一斉に報じ、事案が広く知られることになった。
このパッケージはGitHubとnpmの両方で「OpenAI Codex向けのリモートWeb UIツール」として宣伝され、週あたり27,000〜29,000ダウンロードを集めていた。Aikidoによれば、紐づくAndroidアプリのインストールを合わせると60,000を超える規模に達していた。
重要なのは時間軸だ。悪性コードはパッケージ公開の当初から入っていたわけではない。Aikidoの解析では、最初のバージョン公開から約1か月後に悪性コードが追加されている。攻撃インフラのドメインは2026-04-12に登録されており、これは最初のバージョン(0.1.72)がnpmにアップロードされた直後のタイミングと一致する。先に正規ツールとして信頼とダウンロード数を稼ぎ、後から牙を剥く――典型的な「信頼の醸成→裏切り」型の供給網攻撃だ。
この事案がとりわけ刺さるのは、標的が「AI開発ツールの利用者」だからだ。Codex CLIのようなエージェント型ツールは、コード生成・実行・外部API呼び出しを一手に担う。その認証情報を握られることは、単なるチャット履歴の漏洩にとどまらず、攻撃者がそのアカウントの権限でAIを動かし続けられることを意味する。OpenAI自身もドキュメントで「ファイルベース保存を使う場合、~/.codex/auth.jsonはパスワードと同等に扱うこと。中にはアクセストークンが含まれる」と明記しており、今回の攻撃はまさにその警告どおりの急所を突いた。
- ・Aikidoの一次解析では、悪性挙動が確認されたのはバージョン
0.1.82以降とされる。 - ・一部メディアは異なるバージョン番号を挙げており、確定リストは時間とともに更新される。
- ・自分の環境で固定しているバージョンが該当するかは、必ず一次ソース(Aikido)とlockファイルで突き合わせて確認すること。
同じ「正規publishルート/組織スコープを悪用する」系統のnpm侵害としては、直前に起きたMicrosoftが警告したnpmサプライチェーン侵害|redhat-cloud-services 31パッケージに自己増殖ワーム「Miasma」がある。手口は異なるが、「npm経由で認証情報を抜く」という到達点は共通している。
攻撃手口の詳細
今回の手口の核心は、GitHubのソースとnpmの配布物の二面性にある。攻撃者は3つのレイヤーを使い分けていた。
・なりすましの命名
codexui-androidという名前は「OpenAI Codexの公式UI/Android連携ツール」を連想させる。READMEも正規ツールらしく整えられ、実際に動く機能を備えていたため、利用者は疑いを持ちにくい。
・GitHubクリーン/npm汚染
公開GitHubリポジトリには悪性コードが一切含まれていない。一方、npmにnpm publishされた配布物にだけマルウェアが混入していた。多くの開発者が「インストール前にGitHubのソースを確認すれば安全」と考える習慣を、逆手に取った設計だ。
・無期限トークンという標的選定
窃取対象を~/.codex/auth.jsonに絞ったのは、そこにOpenAIアカウントのOAuthトークンが平文で保存されるからだ。とりわけrefresh tokenは失効しない設計のため、一度盗めば長期にわたり悪用できる。
npm install / アプリ経由"] -->|"モジュールロード時に自動実行"| B["起動フックが発火
アプリ本体コードより前"] B -->|"認証ファイルを読み取り"| C["~/.codex/auth.json
access / refresh / id token + account ID"] C -->|"XOR + base64 で難読化"| D["Sentryテレメトリを装ったHTTPS POST"] D -->|"User-Agent偽装で正規通信に偽装"| E["攻撃者管理の外部サーバー
sentry.anyclaw.store/startlog"] E -->|"refresh tokenは無期限"| F["永続的ななりすまし
アカウント権限の悪用"]
技術的には、パッケージはモジュールのロード時点(アプリケーション本体のコードが走る前)に起動フックを発火させる。~/.codex/auth.json(環境変数CODEX_HOMEが設定されていればそのパス)を読み取り、中身をXORで難読化したうえでbase64エンコードし、Sentryのテレメトリ通信を装ったHTTPS POSTで外部へ送信する。送信先はsentry.anyclaw.store/startlogで、User-Agentを正規のエラーレポート通信に偽装することで検知を逃れていた。
Androidアプリ側の経路も見逃せない。Aikidoは「OpenClaw Codex Claude AI Agent」(パッケージID gptos.intelligence.assistant)というアプリが、内部のPRoot sandbox上でNode.jsを起動し、pnpm add codexui-android@latestでバージョン未固定のまま最新版を取得・実行していたと報告している。バージョンをピン留めしていないため、npm側を差し替えれば端末は自動で悪性版を引き込む構造だった。
- ・npmパッケージ名:
codexui-android(npm公開アカウントは「friuns」名義とAikidoが特定)。 - ・送信先ドメイン:
sentry.anyclaw.store(エンドポイント/startlog)。 - ・関連Androidアプリ:
gptos.intelligence.assistant(「OpenClaw Codex Claude AI Agent」)。 - ・難読化:認証ファイルをXOR+base64で加工してから送出。
- ・本記事はペイロード本体のコードやハッシュは掲載しない。ブロックリスト適用前に必ず一次ソースで再確認すること。
なぜrefresh tokenが「永続なりすまし」になるのか
被害の深刻さを理解するには、OAuthのトークン設計を押さえる必要がある。窃取されたauth.jsonには複数種類のトークンが入っているが、それぞれ寿命と役割が違う。
・access token
APIへ実際にアクセスするための短命トークン。通常は数十分〜数時間で失効するため、単体での悪用期間は限られる。
・refresh token
access tokenの有効期限が切れたときに、再ログインなしで新しいaccess tokenを発行し直すためのトークン。性質上、寿命が長く設定される。
・id token
利用者の身元情報(誰としてログインしているか)を表すトークン。
問題はrefresh tokenが無期限である点だ。攻撃者はこれを握っていれば、access tokenが切れるたびに自動で再発行でき、利用者がログインし直す必要すらない。つまり「一度盗まれたら、明示的に失効させない限り永遠に有効」という状態になる。
- ・access tokenのみ窃取:失効までの数十分〜数時間が悪用可能期間。被害は時間で自然減衰する。
- ・refresh token窃取:access tokenを無限に再発行できるため、被害が自然減衰しない。利用者が気づいてアカウント側で失効操作をするまで継続する。
- ・だからこそ対応の起点は「該当パッケージの削除」ではなく「refresh tokenの失効」になる。
なぜrefresh tokenが無期限なのかは、CLIやデスクトップアプリのUX設計上の都合が大きい。毎回ログインを求めると体験が悪化するため、長寿命のrefresh tokenで「入れっぱなし」を実現している。利便性とリスクのトレードオフであり、今回はその利便性側が突かれた格好だ。トークンを多層で守る考え方はトークン窃取を多層で止める——セッション・AI推論・課金まで含めた防御パターンも参考になる。
もう一点、被害の見えにくさにも注意したい。refresh tokenでの再発行はバックグラウンドで静かに行われるため、利用者の画面には何の異常も出ない。請求やAPI使用量に不自然なスパイクが出て初めて気づく、というケースが多い。逆に言えば、access tokenだけが盗まれた場合は時間経過で勝手に無効化されるが、refresh tokenが盗まれた場合は「自分で失効させるまで時計が進まない」。この非対称性こそが、本事案を単なる情報漏洩でなく「アカウント乗っ取り」級のインシデントとして扱うべき理由になる。
⚡対応確認の手順
まずは「自分が影響を受けたか」を切り分ける。以下のコマンドは読み取り専用で、システムを変更しない。
Step 1: 該当パッケージのインストール有無を確認
# グローバルnpmに入っていないか
npm ls -g codexui-android 2>/dev/null
# プロジェクトのpackage.jsonを横断検索
find . -name "package.json" -not -path "*/node_modules/*" \
| xargs grep -l "codexui-android" 2>/dev/null
# pnpm / yarn のグローバルストアも確認
ls ~/.npm-packages/lib/node_modules/codexui-android 2>/dev/null
pnpm list -g 2>/dev/null | grep codexui-android
Step 2: Codex CLIの認証ファイルを確認
# 認証ファイルの存在と更新時刻
ls -la ~/.codex/auth.json 2>/dev/null
stat ~/.codex/auth.json 2>/dev/null
# CODEX_HOME を別パスに設定している場合
ls -la "${CODEX_HOME:-$HOME/.codex}/auth.json" 2>/dev/null
身に覚えのないアクセス時刻(自分がログインしていない時間帯のatime更新)がないかを見る。ファイルが存在し、かつ不審パッケージを入れていた場合は、侵害前提で次の対応へ進む。
Step 3: 過去のnpm install履歴を遡及
# 直近のnpmログを確認
ls -t ~/.npm/_logs/ 2>/dev/null | head
# 全ログから codexui を検索
grep -l "codexui" ~/.npm/_logs/*.log 2>/dev/null
# シェル履歴からも痕跡を探す
grep -nE "codexui-android" ~/.zsh_history ~/.bash_history 2>/dev/null
Step 4: OpenAI側の異常使用を確認
・platform.openai.com/usage で想定外の使用スパイクがないか確認する。
・ChatGPT/OpenAIアカウントの「ログイン履歴・接続済みデバイス」に見覚えのないセッションがないか確認する。
・Codex CLIのセッションログに、自分が実行していないリクエストが残っていないか確認する。
- ・パッケージ未インストール+auth.jsonに不審アクセスなし+使用ログ正常 → 影響可能性は低い。ただしAndroidアプリ経路は別途確認。
- ・いずれか1つでも該当 → 侵害前提で次章のrotationへ進む。「たぶん大丈夫」で止めない。
⚙️ 対応手順(侵害が疑われる場合)
順序が重要だ。トークンの失効を最優先し、その後にパッケージ除去・スキャンを行う。逆順にすると、除去作業中も盗まれたrefresh tokenが生きたままになる。
A. refresh tokenの失効(最優先・最重要)
ここが今回の事案で最も誤解されやすいポイントだ。Codex CLIのChatGPTログインで発行されるrefresh tokenは、APIキーとは別系統のOAuthトークンである。
・① APIキーのrotateだけでは不十分:platform.openai.com/api-keys でAPIキーを再発行しても、OAuthのrefresh tokenは無効化されない。
・② アカウントのパスワードを変更:OpenAI/ChatGPTアカウントのパスワード変更が、OAuthトークン失効のトリガーになる。
・③ 全セッションをサインアウト:アカウント設定の「すべてのデバイスからログアウト」「接続済みアプリの取り消し」を実行し、既存セッションを無効化する。
・④ Codex CLIを入れ直す:codex logout 後に codex login で再認証し、ローカルのauth.jsonを新しいトークンで上書きする。
- ・盗まれたのはOAuthのrefresh tokenであり、API Keysページで管理するキーとは別物。
- ・refresh tokenを確実に失効させるには、パスワード変更+全セッションのサインアウトが必要。
- ・APIキーも別経路で漏れている可能性があるため、両方rotateするのが安全。
B. 該当パッケージのアンインストール
# グローバル / ローカルから削除
npm uninstall -g codexui-android 2>/dev/null
npm uninstall codexui-android 2>/dev/null
# npmキャッシュをクリア
npm cache clean --force
# pnpmのストアをprune
pnpm store prune 2>/dev/null
Androidアプリ経由が疑われる場合は、gptos.intelligence.assistantを含む不審なCodex関連アプリを端末からアンインストールする。
C. システムスキャン
・他の*ui-android系・codex*系の偽パッケージが依存ツリーに混ざっていないか確認する。
・sentry.anyclaw.storeへの通信ログがファイアウォール/プロキシのログに残っていないか確認する。
・クリーンインストールでlifecycle scriptの自動実行を止める。
rm -rf node_modules package-lock.json
npm install --ignore-scripts
同様な「GitHubクリーン/npm汚染」パターン
今回の手口は新発明ではない。「公開ソースは無害に保ち、配布物にだけ毒を仕込む」という二面性は、近年のnpm攻撃で繰り返し使われている。
- ・多くの開発者の「安全確認」はGitHubのコードを読むことで止まる。
- ・しかし実際に端末で動くのはnpmにpublishされた配布物であり、両者が一致する保証はない。
- ・ソースレビューとパッケージ監査は別の検査であり、前者だけでは配布物の改ざんを検出できない。
直近の関連事案を時系列で並べると、攻撃の系譜が見えてくる。
| 時期 | 事案 | 手口の特徴 |
|---|---|---|
| 2026-05 | Miasma(redhat-cloud-services) | 組織スコープ31パッケージにpreinstallフックで認証情報窃取ワーム |
| 2026-05 | TanStack / @antv 等 Mini Shai-Hulud系 | 正規publishルートを悪用、自己増殖 |
| 2026-04〜05 | codexui-android | GitHubクリーン/npm汚染、Codexの無期限トークン窃取 |
| 2026-06 | Vitest RCE(CVE-2026-47429ほか) | テストランナー経由のRCE。依存に潜む実行経路 |
共通する教訓は「正規に見える流通経路ほど、利用者の検証が甘くなる」という点だ。Vitestの事例のように、開発ツールチェーンそのものが攻撃面になる流れは続いている。
SCAでの予防的検知
人手のレビューでGitHub/npm差分まで毎回追うのは現実的でない。SCA(Software Composition Analysis)ツールで「配布物の挙動」を機械的に検査するのが本筋になる。今回のような事案で効くのは、ソースとの差分検知とpreinstall/起動時の動作分析だ。
| ツール | 強み | 今回型への有効性 |
|---|---|---|
| Socket | パッケージの挙動分析(ネットワーク送信・ファイル読取・install hook) | 高:起動時のauth.json読取+外部送信を検出しやすい |
| Aikido | 今回の発見元。配布物の悪性挙動を解析 | 高:実際に検知した実績 |
| Snyk | 既知脆弱性+ライセンス+一部マルウェア検知 | 中:シグネチャ依存だと未知の新規手口は取りこぼし得る |
| Phylum | publish時点のリスクスコアリング、振る舞い分析 | 高:新規publishの異常を早期に評価 |
| GitGuardian | シークレット検知が主軸 | 中:漏洩トークンの早期発見に寄与 |
- ・GitHub vs npmコンテンツ差分:公開ソースに無い処理が配布物に含まれていないかを照合する。
- ・preinstall/起動時の動作分析:インストール直後やモジュールロード時に、ホームディレクトリの認証ファイルを読んで外部送信していないかを見る。
- ・単一指標ではなく「認証ファイル読取+外部通信+難読化」の複合シグネチャで精度を上げる。
開発者のCLI使用ベストプラクティス
事後対応だけでなく、平時の運用で被害確率を下げる。CLIツールは認証情報をホーム配下に置くものが多く、ここを守ることが効く。
・導入前チェック:GitHub Stars数とnpm DL数のバランス、READMEが他プロジェクトの丸写しでないか、メンテナの実体があるかを確認する。
・バージョンピン留め:@latestでの自動追従を避け、lockファイルでバージョンを固定する。今回のAndroidアプリ経路は未固定が被害を広げた。
・auth.jsonのパーミッション最小化:chmod 600 ~/.codex/auth.jsonで本人のみ読取可能にする。
・CLI実行のサンドボックス化:信頼度の低いCLIはコンテナや専用ユーザーで隔離し、認証ファイルへの到達を制限する。
・組織別キー分離:個人・本番・検証でOpenAIのキー/アカウントを分け、1つの漏洩の被害範囲を限定する。
# 認証ファイルのパーミッションを本人のみ読取に絞る
chmod 600 ~/.codex/auth.json
ls -la ~/.codex/auth.json # -rw------- になっていることを確認
よくある落とし穴
実際に対応するチームが踏みやすい穴を挙げておく。
・「OpenAI公式CLI」を騙る命名トリック:codexを含むそれらしい名前に引っかかる。公式は@openai/codexであることを基準に照合する。
・GitHubだけ確認してnpmコンテンツ未確認:ソースの安全=配布物の安全ではない。
・refresh tokenの長期保存リスク軽視:「APIキーをrotateしたから対応済み」で止めてしまう。OAuthトークンは別系統。
・複数CLIの認証ファイル混在:OpenAI/Anthropic/Googleの認証情報を同一環境に同居させ、1つの侵害で芋づる式に漏れる。
・チームメンバーへの周知漏れ:自分の環境だけ対処しても、同じツールを入れた同僚の端末が残る。共有チャンネルでの一斉周知が要る。
FAQ
Q. OpenAI公式のCodex CLIは安全ですか?
汚染されたのは公式パッケージではなく偽のcodexui-androidです。ただし公式CLIが作る~/.codex/auth.jsonが窃取対象なので、同一環境に偽パッケージや不審アプリを入れていた場合は公式利用者でも認証情報が抜かれている可能性があります。
Q. インストールした記憶がなければ完全に安心ですか?
npmで明示的に入れていなくても、Google Play上のAndroidアプリがPRoot sandbox内で内部的に同パッケージを実行していました。スマホ側の関連アプリにも注意してください。
Q. 盗まれたrefresh tokenはAPIキーをrotateすれば無効化できますか?
いいえ。refresh tokenはOAuthトークンでAPIキーとは別系統です。失効にはアカウントのパスワード変更と全セッションのサインアウトが必要です。
Q. 他のCLI(Anthropic claude / google gemini cli)でも同種の事案はありますか?
今回はCodexが標的でしたが、手口はCLI共通の弱点(ホーム配下の認証ファイル)を突いています。各CLIで認証ファイルのパーミッション最小化を推奨します。
参照ソース
・Aikido — Legitimate-Looking Codex Remote UI Secretly Steals Your AI Tokens(一次・発見元)
・The Hacker News — OpenAI Codex Authentication Tokens Stolen in codexui-android npm Supply Chain Attack
・CSO Online — Attack targeting OpenAI Codex users exposes AI software supply chain risks
・Hackread — 27,000-Download Codex UI Tool Secretly Stole OpenAI Refresh Tokens
・OpenAI Codex 公式ドキュメント