InvoiceShelfは、ある会社のオーナーが別の会社のユーザーアカウントを乗っ取ることができてしまう深刻な脆弱性、CVE-2026-55610のパッチをリリースしました。CVSSスケールで8.7と評価されたこの脆弱性は、アプリのLaravelコードにおけるテナントスコープのチェック漏れに起因しています。

脆弱性の仕組み

InvoiceShelfは、Laravelで構築されたSaaSツールであり、企業が単一のダッシュボードからユーザー、請求書、設定を管理できるようにするものです。このプラットフォームは、カスタムヘッダーを読み取ってテナントを識別します。オーナーがユーザーレコードをリクエストすると、コードは「リクエスタは自社のオーナーであるか?」という点のみを確認します。

対象となるユーザーが同じテナントに属しているかどうかは、決して検証されません。Laravelの暗黙的なルートモデルバインディング(route-model binding)は、ユーザーIDをグローバルなusersテーブルの行に解決し、認可ポリシーはリクエスタのロールのみに基づいてリクエストを承認します。

そのため、攻撃者は以下のことが可能になります:

  • リクエストURLに任意の数値ユーザーIDを指定する。
  • メールアドレスを含むユーザーレコードの全情報を取得する。
  • 更新リクエストを発行して、被害者のメールアドレスやパスワードを上書きし、さらにはそのアカウントをスーパー管理者(super-admin)として攻撃者の会社に再割り当てする。

実際には、悪意のあるオーナーが、企業管理ツールを汎用的なアカウント乗っ取り兵器へと変えてしまいました。「オーナー」以上の権限は必要ありませんでした。

影響を受ける対象

バージョン2.4.1より前のすべてのInvoiceShelfユーザーが危険にさらされていました。この脆弱性はリクエスト処理のコアパスに存在するため、規模やセキュリティ体制に関わらず、どのテナントも他のテナントのオーナーから標的にされる可能性がありました。影響には、機密性の喪失(メールアドレス)および完全性の喪失(不正なパスワード変更、スーパー管理者への権限昇格)が含まれます。

パッチ

開発者はバージョン2.4.1をリリースし、ユーザーレコードに対する読み取りまたは書き込み操作の前に、明示的なテナントチェックを追加しました。この修正により、クエリがアクティブな会社識別子にスコープされるようになり、Laravelはリクエスタのテナントに属する行のみを返すよう強制されます。

開発者が学ぶべきこと

  • マルチテナントアプリケーションにおいて、グローバルな主キー(primary keys)に決して依存しないこと。
  • 削除や作成のアクションだけでなく、すべてのデータベースルックアップにテナントフィルタを適用すること。
  • 認可ポリシーにおいて、実行者のロール対象オブジェクトのテナント属性の両方を検証すること。
  • ルートモデルバインディングのようなフレームワークの暗黙的な機能は、明示的なスコープを追加しない限り、セキュリティ上の欠陥を隠してしまう可能性がある「利便性」として扱うこと。

今後の展望

このインシデントは、テーブルを共有しているあらゆるSaaSにおける広範なリスクを示しています。セキュリティ監査では、識別子を受け取るすべてのエンドポイントをレビューし、テナントのスコープが統一的に適用されていることを確認する必要があります。

まとめ: たった一つのテナントチェックの漏れが、特権ユーザーのロールを汎用的なバックドアに変えてしまう可能性があります。適切なスコープ設定はオプションではなく、マルチテナントシステムにおけるデータ分離の基盤です。