Money Forwardのマルチテナント設計 — データ分離とセキュリティ

Money Forwardは個人家計から企業向けクラウドサービスまで幅広いプロダクトを展開しています。多くの顧客が同じインフラを共有するマルチテナント設計は、コスト効率と運用負荷の低減に寄与する一方、データ分離とセキュリティの難しさが常につきまといます。
Money Forward deploys a wide range of products from personal household finance to enterprise cloud services. While multi-tenant design — where many customers share the same infrastructure — contributes to cost efficiency and reduced operational load, the difficulty of data isolation and security always accompanies it.
行レベル分離とスキーマレベル分離
マルチテナントの実装には大きく二つの流派があります。一つは全テナントの行を同じテーブルに入れて、`tenant_id`列で分離する行レベル方式。もう一つはテナント毎にスキーマやデータベースを分けるスキーマレベル方式です。Money Forwardはプロダクトの特性に応じてこの両者を使い分けています。
Multi-tenant implementation has two major schools. One is the row-level approach, where all tenants' rows are placed in the same table and isolated by a `tenant_id` column. The other is the schema-level approach, separating schemas or databases per tenant. Money Forward uses both selectively depending on product characteristics.
テナント認識認証
認証トークンには必ずテナント識別子が埋め込まれており、API層で受け取ったリクエストは最初に現在のテナントコンテキストを確立します。データベースアクセスは全てこのコンテキスト経由で行われ、他テナントの行が誤って読まれることがないようにアプリケーションフレームワーク層で強制されます。
Authentication tokens always have a tenant identifier embedded, and requests received at the API layer first establish the current tenant context. All database access occurs through this context, and the application framework layer enforces that other tenants' rows cannot be mistakenly read.
行レベルセキュリティ(RLS)の活用
PostgreSQLの行レベルセキュリティ機能を使うと、データベース層でも同じ制約を掛けられます。アプリケーションコードに万が一バグがあっても、現在のテナントIDと一致しない行は返されません。「アプリでも、DBでも、両方で守る」という多層防御がテナント分離の基本姿勢です。
Using PostgreSQL's row-level security feature, the same constraint can be applied at the database layer too. Even if there is a bug in application code, rows that do not match the current tenant ID are not returned. "Protect at both the app and the DB" — this multi-layered defense is the basic stance of tenant isolation.
テナント毎の暗号化鍵
特に機密性の高いデータは、テナント毎に異なる暗号化鍵で保存されます。鍵管理サービス(KMS)から取り出したデータキーで保存時に暗号化し、読み出し時に復号します。これによって、万が一データベースのダンプが流出しても、攻撃者は個別のテナント鍵を全て入手しなければ平文にできません。
Particularly confidential data is stored with a different encryption key per tenant. Data keys retrieved from a key management service (KMS) encrypt at storage time and decrypt at read time. As a result, even if a database dump leaks, an attacker cannot reach plaintext without obtaining every individual tenant key.
騒がしい隣人問題
共有インフラの宿命として、一テナントの負荷が他テナントの応答時間に影響を与える「騒がしい隣人」問題があります。Money Forwardは一テナント当たりのリクエスト速度、同時接続数、CPU時間に上限を設けて、特定テナントが全リソースを占有することを防いでいます。
As a fate of shared infrastructure, the "noisy neighbor" problem exists where one tenant's load impacts other tenants' response times. Money Forward sets upper limits on per-tenant request rates, concurrent connection counts, and CPU time to prevent any specific tenant from monopolizing all resources.
大口顧客の専用クラスタへの隔離
特に巨大な利用規模を持つ顧客は、共有クラスタから切り離して専用の実行環境に隔離します。これは他テナントを守ると同時に、大口顧客に安定した性能を届けるためでもあります。「全員に同じ体験」ではなく「規模に応じた配置」が現実解です。
Customers with especially huge usage scale are detached from the shared cluster and isolated in dedicated execution environments. This both protects other tenants and delivers stable performance to large customers. The realistic solution is not "the same experience for everyone" but "placement according to scale."
監査ログとアクセス記録
どのテナントの、どのリソースに、誰が、いつアクセスしたかは全て記録されます。内部運用担当者による操作も例外ではなく、テナントオーナーが後から内部アクセスを確認できる仕組みが整備されています。これはGDPRや個人情報保護法など各国の規制への対応にも直結します。
All accesses — which tenant, which resource, by whom, and when — are recorded. Operations by internal staff are no exception, and a mechanism is established for tenant owners to verify internal accesses afterward. This is also directly tied to compliance with regulations in each country, such as GDPR and personal information protection laws.
共有キャッシュの危険
RedisやMemcachedなど共有キャッシュを使う際、キャッシュキーにテナントIDを含めないと他テナントのデータが誤って返されるという致命的なバグが発生します。Money Forwardでは全てのキャッシュキーにテナントプレフィックスを強制し、ライブラリ層で誤った使い方を防いでいます。
When using shared caches like Redis or Memcached, omitting the tenant ID from the cache key produces a fatal bug where another tenant's data is mistakenly returned. Money Forward enforces tenant prefixes on all cache keys and prevents incorrect usage at the library layer.
おわりに
マルチテナントSaaSの設計は、単純な「データを分ける」作業ではありません。認証、認可、データ層、キャッシュ層、暗号化、リソース制限、監査という全レイヤーに「テナントを意識する」設計を一貫して適用する必要があります。一つでも抜けがあれば、そこから漏洩が発生し、企業の信頼が一夜にして失われるのです。
Multi-tenant SaaS design is not a simple task of "separating data." Tenant-aware design must be consistently applied across every layer — authentication, authorization, data layer, cache layer, encryption, resource limits, and auditing. If even one gap exists, leakage occurs there, and a company's trust can be lost overnight.