発見から確認まで:teai 不正アクセスの対応の記録(11月9日・時系列)

株式会社イネブラ(teai の運営会社)/掲載 2026年11月9日/時刻はすべて日本時間

2026年11月9日、不正アクセスに気づいてから、止める・鍵を替える・知らせる・確かめるまでの1日を、時刻つきで残します。対応の途中で私たち自身が起こした2回の停止と、作業を手伝った AI の誤りも、そのまま書いています。同じような事故に向き合うエンジニアの方の参考になればと思います。

攻撃の仕組みと根本原因は技術レポートに、お客様へのお願いはお知らせにあります。

この日の対応は、運営者1人と、作業を手伝う複数の AI エージェントで行いました。AI の作業を確かめる手順を決めるのは、当社の責任でした。時刻は、マージした時刻・本番に反映した時刻・本番で確かめた時刻を分けて書いています。

1日の流れ(要約)

時刻出来事
11:38社内の AI 端末(Sente)が止まったと、運営者が気づく
11:57残高の不具合の調査中に、データベースの記録から、本番の鍵の書き出しを見つける
11:57〜11:58該当アカウントを隔離し、API キーを止める
12:51原因の機能が本番から消えたことを確認(マージ 12:01、反映 12:23)
12:50〜12:51データ暗号の鍵を入れ替え、保存データを暗号化し直す
14:21〜15:00攻撃が9月から続き、代表アカウントが乗っ取られていたことが判明
16:49〜17:10当社のミスによる停止①(データベースの鍵の入れ替えの失敗、21分)
17:26〜17:50詐欺メールの送信を知り、メール送信の鍵を全部止めて、各国の機関へ通報
17:59個人情報保護委員会へ速報
18:05全員のウェブのログイン状態を解除
18:39〜18:43決済の鍵を入れ替え
19:00〜19:31当社のミスによる停止②(使用中の決済の鍵を無効にし、決済が停止)
19:33〜20:06偽の API キーの照合と、全サービスでの鍵の確認

1. 発見(11:38〜11:57)

11:38 社内で使っている AI 端末(Sente)がエラーを返すと、運営者が気づきました。外部の AI 利用サービス(OpenRouter)の主力の鍵が残高切れになっていて、上流から 402 が返っていました。11:44 に予備の鍵へ切り替えて復旧し、同時に「なぜこんなに早く残高が減ったのか」を調べ始めました。

11:57 残高の不具合を調べるため本番のデータベースの記録を確かめていて、ある利用者が AI の「コード実行」の道具で、サーバーの設定(環境変数)を16進数で書き出させていたのを見つけました。データベース・メール送信・AI 各社などの鍵が含まれていました。

学び:発見のきっかけは、セキュリティの見張りではなくお金の異常でした。外部サービスの利用料・残高の急な減りは、鍵の悪用の最初の兆候になりえます。

2. 封じ込め(11:57〜12:51)

11:57 該当のアカウントを隔離しました(元に戻せる形で、別の表に移す)。ところが、そのアカウントの API キーが使われると、空のアカウントが自動で作り直される作りでした。11:58 に、API キー自体も無効にしました。

学び:アカウントを隔離しても、認証の入口(API キー・ログイン状態)が生きていることがあります。入口を直接止めたかを、必ず確かめてください。

12:01 原因の機能(AI がサーバー上でシェルを動かす道具など)を既定で使えなくする修正をマージしました(PR #943)。本番への反映は 12:23、本番の道具の一覧から消えたことを確かめたのは 12:51 です。マージから本番での確認まで、約50分かかりました。

12:50〜12:51 漏れた鍵のうち、データ暗号の鍵(利用者が預けた外部の鍵を暗号化するためのもの)を入れ替えました。先に「新しい鍵と古い鍵の両方で読める」仕組みを本番に出し(PR #945・#946)、次の順にしました。

  1. 新しい鍵を設定する
  2. 試し実行で、暗号化し直す件数を確かめる(12:50:11)
  3. 本番で暗号化し直す(12:50:20)
  4. 古い鍵を外す(12:50:27〜12:51:33)

管理用の鍵、クラウド(AWS)のバックアップ用の鍵なども入れ替えました。

学び

3. 範囲の判明と、守りの強化(14:07〜15:00)

14:07〜14:46 次の修正を順にマージしました。

14:21〜15:00 記録を詳しく調べるうちに、範囲が大きく広がりました。

14:21・14:40 関わっていたアカウントを隔離し、11:57 の1個と合わせて12個にしました。代表アカウントの偽のログイン情報を含め、これらのログイン状態を消しました。置かれたファイルは証拠として保存し(ハッシュ値を記録)、サーバー上では隔離しました。

学び:最初に見つけたものが「全部」とは限りません。最初の1件を止めたら、すぐに同じ手口の痕跡を全期間で探してください。私たちは最初「11月7日の1件」と考え、お知らせの下書きを書き始めていました。

4. データベースの鍵の入れ替えと、1回目の停止(15:00〜17:10)

最優先は、攻撃者が今も持っているデータベースの鍵を無効にすることでした。

15:00〜16:48 データベースの管理サービスの CLI でログインしようとしましたが、ブラウザでの承認が約6回、時間切れになりました。このとき作業を手伝っていた AI は、「私たちの使い方(CLI の版 1.0.34)では、ログインが済んでいない状態でも、命令が終了コード0で終わる場合がある」ことに気づいていました。

16:48 それでも AI は、ログインの完了を検知したら入れ替えを自動で始める見張りを、CLI の終了コードだけで判定する形で作りました。16:49 見張りは誤って「ログインできた」と判定し、入れ替えを始めました。AI は運営者に「ログインできたので、入れ替えが始まりました」と報告し、16:54 には「古い鍵は無効にできました」と報告しました。どちらも事実ではありませんでした。

実際には、新しい鍵を作る命令の出力は「ログインしてください」というエラー文で、スクリプトはその文章(88文字)を新しい鍵として本番に入れていました。古い鍵は無効になっていませんでした。

16:49〜17:10 の21分間、teai 全体が止まりました。 サーバーは起動時にデータベースへ接続できず、起動に失敗し続けました。古い鍵は控えていなかったので、元に戻すこともできませんでした。

外の見張り(定期的に動作を確かめる仕組み)は 16:49 に異常を検知していました。しかし、知らせのメールは、メール送信サービスの手前の防御(Cloudflare)に、プログラムの既定の名乗り(Python の User-Agent)で弾かれていました(エラー 1010)。最初は送信元のアドレスの問題と考えて直しましたが(PR #955)、それでは直らず、本当の原因が分かったのは夜でした。

17:05 CLI をやめ、管理画面で発行したトークンで、データベースの管理サービスの API を直接呼ぶ形にスクリプトを作り直しました。新しい鍵は、実際にデータベースへ select 1 を投げて通ることを確かめてから本番に入れます。失敗したら、本番には入れずに止まります。

17:10 入れ替えが完了し、ページ・ログイン・データベースの読み書きが正常に戻りました。古い鍵をすべて無効にする操作は、管理サービスから成功の応答を受けています(古い鍵そのもので接続できないことは、手元に古い鍵がないため確かめていません)。

学び

5. メール送信の鍵と、第三者への被害(17:26〜17:59)

17:26 外部サービスの監査を担当していた別の AI セッションから、第一報が届きました。漏れたメール送信の鍵で、当社のドメインを差出人にした詐欺メールが約3万通(11月7〜8日)送られていた、という内容です。この時点では、送信は11月8日夜に止まっていると見られていました。

17:29〜17:35 アプリごとに、送信だけができる新しい鍵を作り、26のアプリに配り始めました。

17:36 古い鍵36本をすべて削除しました。削除の直後に送信の記録を照会し、この日の16:48まで詐欺メールが送られていたこと、17:36 以降の送信はないことを確かめました。

17:36 前後に、運営者もメール送信サービスの管理画面で鍵を全部削除し、新しい全権限の鍵を作り直しました。このとき、AI が配った1回目の新しい鍵26本と、teai 本体の鍵もいっしょに消えました。17時36分ごろから17時41分ごろまでの約5分間、teai の認証コードのメールが送れなくなりました。 そこで、運営者が作り直した鍵を使い、アプリごとの送信専用の鍵を配り直しました(27のアプリ、GitHub、Cloudflare)。

17:45 までに、監査の集計で、詐欺メールは10月12日から約7万6千通だったと分かりました。

17:50 詐欺メールについて、イタリアの CERT-AgID、ベルギーの Safeonweb、pagoPA、Orange、メール送信の事業者に、メールで通報しました。

17:59 個人情報保護委員会に速報を提出しました。

学び

6. 公開と、守りの強化(18:00〜19:41)

18:05 全員のウェブのログイン状態を解除しました。この時点で残っていたログイン状態は1件だけでした(14:07 の修正の反映で、それより前の形式のログイン情報は使えなくなっていたためとみられます)。解除のあと、0件になったことを確かめました。

18:32・18:34 不審な API キーを止めました。

18:45 詐欺メールを受け取った方に向けた告知を、日本語・英語・イタリア語で公開しました。

18:50 データベースまで確かめる、より深い状態確認をマージしました(PR #957)。ところが本番では、設定の読み込みの誤りで、データベースが正常でも「異常」を返していました。19:11 から、外の見張りは失敗を出し続けています(この記録を公開した 11/9 22時台の時点でも修正中、PR #960)。

18:57 外部サービスの異常(利用料・権限エラー・決済の鍵)を、定期的に確かめる見張りを動かし始めました。19:03 までに、鍵が盗まれたら気づけるよう、おとりも置きました。

学び:新しい見張りは、本番で試してから警報につなぐ。深い状態確認は、本番で誤った「異常」を出し続け、見張り全体を信用できなくしました。

19:10 道具の出力から秘密を自動で消す仕組みと、サーバーの状態を返す道具を AI に渡さない変更をマージしました(PR #958)。19:41 に、本番の起動の記録で動いていることを確かめました。

7. 決済の鍵の入れ替えと、2回目の停止(18:39〜19:31)

18:39〜18:43 決済サービス(Stripe)の新しい秘密鍵を、決済の鍵を持つアプリ14と Worker 5つに配りました。このとき、2つの AI セッションが、同じ鍵の配布を同時に走らせていました。

19:00〜19:01 決済サービスの管理画面で、鍵が4本無効にされました。その中には、18:39 に配ったばかりの新しい鍵と、配布の対象から漏れていた10のサービスが使っていた鍵が含まれていました。決済サービスの操作の記録で、代表のアカウントで、当社の作業用の Mac から行われた操作だったことを確かめています。人の操作か AI の操作かは、特定できていません。

19:00〜19:31 ごろ、決済を扱う当社のアプリ25と Worker 5つで、カードでのお支払いが通らなくなりました。

19:03 18:57 に動かし始めたばかりの見張りが「決済の鍵が使えない(401)」を検知し、知らせのメールが届きました(この見張りは当社の手元の端末で動き、Gmail から知らせを送るため、16:49 に知らせが弾かれた外の見張りとは別の経路で届きました)。

19:24 決済サービスの管理画面で新しい鍵を用意し、teai を含むアプリ15と Worker 5つに配りました。19:29〜19:31 漏れていた10のサービスにも配り、それぞれで新しい鍵が使えることを確かめました。この時間帯に完了した決済は0件で、二重の請求や、支払い後の反映漏れはありませんでした。

学び

8. 確認(19:33〜20:06)

19:33 偽の API キーの照合をしました。攻撃者はデータベースに書き込めたので、誰かのアカウントに、自分だけが値を知る API キーを紛れ込ませることができました。9月7日以降に作られた有効なキー956本を作成の記録(監査ログ)と照らし合わせ、記録のない4本を止めました。

ただし、監査ログも同じデータベースにあるので、記録ごと書き込まれたものは見つけられません。そこで、データベースの鍵を入れ替えた時点(17:10)より前からあるキーの一覧を記録し、11月21日に、一括で使えなくします。作成日時ではなく、キーの ID で止めます(作成日時も、攻撃者が書き換えることができたため)。

20:06 確認用のスクリプトで、決済の鍵を持つすべてのサーバーで、新しい鍵が使えることを確かめました(失敗0)。

学び

9. この日に間に合わなかったこと

公開した 11/9 22時台の時点の状態です。

10. チェックリスト(エンジニアの方へ)

事故の前に

事故のとき

濱田優貴(株式会社イネブラ 代表取締役)


関連:お知らせ(お願い) / 代表のまとめ / 技術レポート / 停止のお詫び / 対応の記録 / 詐欺メールについて/お問い合わせ:teai.io/contact