Forest Book
リソースを分けただけでは分離にならない ― マルチテナントShopifyアプリのIAM設計とアカウント分離

リソースを分けただけでは分離にならない ― マルチテナントShopifyアプリのIAM設計とアカウント分離

2026年7月20日

複数のストア向けにカスタムアプリを作り、それを自社のクラウド基盤でホスティングしていくと、必ず「テナント分離をどうするか」という論点が出てきます。ここで「アプリごとにLambdaもテーブルも別々に作っているから分離できている」と考えるのは、半分正しくて半分危険です。リソースが分かれていることと、越境できないことは別の話だからです。本記事では、リソース分離とIAM分離の違い、そしてどこまでやればテナント分離と言えるのかを整理します。カスタムアプリを複数ストアへ提供しているベンダーの技術者や、外部ベンダーのセキュリティ体制を評価する立場のEC事業者に向けた内容です。

「アプリごとに作っている」は分離の第一層でしかない

アプリごとにユニークな名前でLambda・テーブル・キュー・イベントルールを作る。これは正しい骨格で、実際そこが分離設計の出発点です。同じテーブルに複数ストアのアクセストークンを同居させるような設計と比べれば、はるかに健全です。

しかし、ここで止まると穴が残ります。問題はIAMです。

アプリごとにテーブルが分かれていても、各Lambdaの実行ロールが共有だったり、テーブルへの権限をワイルドカードで許可していたりすると、アプリAのLambdaはアプリBのテーブルを技術的には読めてしまいます。読まないのは「コードがそう書かれていないから」であって、インフラが禁止しているからではありません。

この区別が重要なのは、カスタムアプリが保持しているものの性質を考えると分かります。各アプリはストアの管理APIへのアクセストークンを持っています。1件の越境がそのまま、そのストアの注文・顧客情報へのフルアクセスを意味します。「コードがそう書かれていない」という担保だけで守るには、リスクが大きすぎるのです。

コード品質に依存しない防衛線を引く

通常の受託開発なら、「コードがそう書かれていない」を人間のレビューで担保できます。しかし、アプリの数が増えるほど全コードを常時レビューし続けることは現実的ではなくなりますし、AIによるコード生成を実装に組み込むなら、なおさら「レビューがあるから安全」とは言えなくなります。生成コードのバグ、テーブル名の取り違え、予期しない挙動。どれが起きても越境できないという保証は、コードの品質ではなくインフラで作るしかありません。

リソース分離は「規約による分離」です。IAM境界があって初めて「強制による分離」になります。この2つは名前が似ているだけで、守れるものがまったく違います。

解決はすでにやっている設計の延長線上にある

幸い、アプリごとにユニークな命名で構築しているなら、あと一歩です。当社が採っているのは次の構成です。

命名規則を app-{アプリID}-* のようなプレフィックスに統一します。そのうえで、アプリごとにLambda実行ロールを1つ作り、そのアプリのプレフィックスに一致するリソースだけを許可します。テーブルへの権限も table/app-{アプリID}-* のみ、という形です。これをデプロイテンプレートに一度組み込んでしまえば、以後生成される全アプリに自動適用され、個別の手作業はゼロになります。

アクセストークンについても、各アプリのテーブルに置くならこのIAM制限で守れますし、秘密情報の管理サービスにアプリ単位のパスで置いて同様の条件を掛ける形でも構いません。

デプロイロール自体を締めるという発想

もう一段踏み込む方法もあります。インフラ定義そのものを人が書かない(あるいはAIに書かせる)場合、デプロイに使うロールの側に制約を持たせるやり方です。

具体的には、デプロイロールが操作できるリソース名をプレフィックスに限定し、さらにロールの作成やポリシーの付与に対しては境界ポリシーの添付を必須条件にします。こうしておくと、どんなインフラ定義が書かれても、境界を超える権限は物理的に作成できません。加えて、デプロイ前の検証ゲートで「使用しているリソースタイプが許可リスト内か」「命名規約に沿っているか」「キューの可視性タイムアウトが関数のタイムアウトの2倍以上あるか」といったルールを機械的にチェックします。

つまり、安全性を「記述形式の制限」で担保するのではなく「デプロイ時のガードレール」で担保する、という考え方です。前者は表現力を犠牲にしますが、後者は書き方の自由を残したまま結果だけを保証できます。

IAMを締めても残る2つの問題

ここまでやっても、単一アカウント内では2つの問題が残ります。

同時実行数のプールが全テナント共有であること。Lambdaの同時実行数の上限はアカウントとリージョンの単位なので、1アプリの暴走(無限ループやWebhookの嵐)が全テナントのアプリを詰まらせます。IAMは越境を防ぎますが、障害の波及は防げません。アプリごとの同時実行数の予約やAPIのスロットリングで障害半径を区切る必要があります。

アカウントのソフトリミットがスケールの天井になること。IAMロール数やテーブル数にはアカウント単位の上限があり、アプリごとに複数リソースを作る設計だと、数百アプリのオーダーで引き上げ申請が必要になります。今すぐの問題ではありませんが、認識はしておくべきです。

「全部自分のアカウント上だから検討不要」ではない

余談ですが、当社が最初に整理を誤りかけたのがここです。「すべて自社のクラウドアカウント上に構築するから、マルチテナントの分離は検討しなくてよい」という発想は、問題を消すのではなく、むしろマルチテナント問題そのものです。この論点の本質は「誰のアカウントか」ではなく「同一アカウント内で複数クライアントの資産をどう隔てるか」にあります。ベンダー経由で納品する場合、クライアント側のセキュリティチェックで必ず聞かれる項目でもあります。

アカウント分離という選択肢

テナント数が増えてきたら、その先にはマーチャント単位でクラウドアカウントを分離する方式(サイロモデル)があります。これはクラウドベンダー自身が推奨するSaaSの標準パターンの一つで、アカウントの量産は正式にサポートされた運用形態です。組織内アカウント数のデフォルト上限は小さい値ですが、引き上げ申請が可能で、数百から数千規模も一般的に行われています。ルートメールにプラスアドレス(name+テナント名@example.com の形式)を使うのも定番のやり方で、アカウント生成自体もAPIで自動化できます。

この方式にすると、先ほどの残課題が2つとも自動的に解決します。同時実行数の上限はアカウント単位なのでテナントの暴走が他テナントに波及しなくなり、ソフトリミットの天井もアカウントごとにリセットされます。副次的な利点として、テナント別の原価計測が計装なしで手に入るのも大きい。請求がアカウント単位で自動集計されるので、料金プランの閾値を実データで調整できるようになります。

一方で注意点もあります。運用の複雑さが一段上がり、デプロイはクロスアカウントになり、ログや監視の集約、アカウントのベースライン設定の整備が必要です。解約時の制約も見落としがちで、閉鎖したアカウントもクォータにカウントされ続けるうえ、一定期間内に閉鎖できるアカウント数にも上限があります。解約と同時に閉鎖する運用にすると、解約が集中したときに詰まります。当社では、リソース削除後にいったん休眠プールへ移し、同一マーチャントの再訪時に再利用しつつ、定期バッチで古いものから閉鎖する運用にしています。

アカウント作成が数分かかる非同期処理である点も、フローに組み込むうえでは効いてきます。実装に時間がかかる工程の裏で並行して作成する、あるいは事前にプールしておくといった設計が要ります。

移行は後からでもできる

初期から大規模なアカウント分離体制を組むのは過剰投資です。ただ、ここで述べたアプリ単位のリソース分離とプレフィックスIAM、そしてデプロイテンプレートはすべてコードとして管理されるので、後からアカウント単位に載せ替えることは可能です。まずは単一アカウント+アプリ単位のIAM分離で始め、テナント数や要求水準が上がった段階でアカウント分離へ移行する。大口の顧客だけ先行して専用アカウントにする、というハイブリッドも成立します。

なお、アカウントを分離しても、その中のアプリ単位IAM分離は残すのが筋です。多層防御として、片方が破れてももう片方が効く構造にしておくためです。

まとめ

マルチテナントのカスタムアプリで問うべきは「リソースを分けたか」ではなく「越境できない仕組みになっているか」です。アプリごとのリソース分離は第一層にすぎず、アプリ単位のIAM最小権限ロールをデプロイテンプレートで自動適用して初めて、コード品質に依存しない防衛線になります。そのうえで、同時実行数の共有とアカウントのソフトリミットという2つの残課題を解きたくなったとき、テナント単位のアカウント分離が次の一手として視野に入ります。分離は一度に完成させる必要はなく、コードで管理されている限り段階的に深めていける、というのが実務上の救いです。