Выводите владение на сервере
ID записи что-то идентифицирует; он не доказывает, кто может её читать. supastack ограничивает записи пользователей по workspaceId. Сервер разрешает воркспейс аутентифицированного пользователя и включает эту область при чтении или изменении записи.
Не считайте ID воркспейса, отправленный браузером, доказательством владения. Браузер может запросить действие, но доверенный серверный контекст должен установить, чьё это действие. Это правило должно работать для статуса задач, сохранённой конфигурации провайдеров, записей использования и каждого продуктового ресурса. Лейауты и компоненты могут фильтровать отображаемое, но рендеринг — не авторизация; проверка принадлежит тому месту, где запись реально читается или пишется.
Всё остальное в этом посте следует из этого правила: владение выводится, а не запрашивается. В момент, когда присланный клиентом workspaceId где-то получает доверие, любая другая проверка становится декорацией. Пост об аутентификации покрывает, как сама сессия устанавливается выше этой границы.
Членство — часть границы
Ограничение области отвечает, какому воркспейсу принадлежит запись; членство отвечает, принадлежит ли этот пользователь тому воркспейсу. Разрешайте оба на сервере. supastack моделирует личные воркспейсы и воркспейсы организаций с ролями owner, admin и member — руководство по воркспейсам документирует модель.
Роли добавляют вторую ось: участник может читать то, что менять вправе только администратор. Держите две проверки раздельными — «принадлежит этому воркспейсу» и «может выполнить это действие», — чтобы понижение или удаление действовало на той же границе, что и всё остальное. Приглашения и удаления — часть того же жизненного цикла, а не административная запоздалая мысль.
Изменения членства также должны действовать немедленно. Следующий запрос удалённого участника должен падать на границе, а не когда закешированный токен случайно истечёт, — ещё одна причина, по которой проверка живёт там, где читаются записи, а не внутри выданного ранее креденшела.
Делайте восстановление полезным без выдачи доступа
URL задачи позволяет пользователю вернуться после обновления. Он полезен именно потому, что сохраняет ссылку на работу. Аутентифицированный API всё равно должен проверить, что запрошенная задача принадлежит текущему воркспейсу, прежде чем возвращать статус или результаты.
Тот же принцип применяется к восстановлению отправки. Ключ идемпотентности помогает идентифицировать повторный запрос и предотвратить дублирование работы. Это ни сессия, ни токен доступа. Повтор сохранённой отправки всё равно требует аутентификации, владения воркспейсом и соответствующего права доступа.
Удобные пути — это то, где изоляция тихо размывается. Ссылка, помогающая легитимному пользователю возобновить работу, — после обновления страницы, из письма о завершении — должна проходить ту же аутентифицированную проверку, что и любой другой запрос. Путь от запроса к результату использует ключи идемпотентности в области задачи ровно по этому принципу.
Тестируйте запросы, которые должны падать
Тест, позволяющий владельцу получить запись, доказывает удачный путь. Он не доказывает изоляцию. Соедините его со вторым воркспейсом, запрашивающим ту же запись, анонимным запросом и записью, пытающейся подставить другого владельца.
Проверяйте также внешнее поведение, а не только сохранённый результат. Отклонённый запрос не должен мутировать данные чужого воркспейса, а ответ об ошибке не должен утекать секреты или сырые детали базы. Эти проверки делают границу проверяемой при изменении реализации.
- Второй воркспейс запрашивает тот же ID записи — отклонено.
- Анонимный запрос бьёт в тот же эндпоинт — отклонено.
- Участник, удалённый из воркспейса, повторяет — отклонено.
- Запись называет другого владельца или воркспейс — отклонено, ничего не мутировано.
- Ответы об ошибках остаются общими — никаких секретов и деталей схемы.
Перенесите правило в свой продукт
Добавляя новую продуктовую таблицу, определите её владение до построения экрана. Включите workspaceId в модель персистентности, разрешайте воркспейс через существующую серверную границу и используйте этот контекст в операциях списка и деталей.
Фоновые работники нуждаются в той же заботе. Поставленная в очередь задача должна нести достаточно доверенного контекста для загрузки правильной записи, пока креденшелы провайдеров остаются на сервере. Запланированная задача, загружающая строки по голому ID, всё ещё внутри границы. Последовательное правило владения проще поддерживать, чем набор маршрутно-специфичных исключений.
Поверхность API принадлежит той же границе. Ключи API воркспейса и подписанные исходящие вебхуки несут с собой область воркспейса — ключ никогда не должен становиться способом читать через тенантов, а полезная нагрузка вебхука никогда не должна нести записи соседа. Руководство по модулю API покрывает форму этой поверхности.
Частые вопросы об изоляции воркспейсов
Мультитенантная изоляция поднимает одну и ту же горсть вопросов в каждом проекте. Вот те, которые стоит решить рано.
- Достаточно ли ограничения области на уровне приложения или нужна безопасность на уровне строк? — Серверно обеспечиваемое ограничение области на каждом чтении и записи покрывает модель угроз большинства SaaS. RLS базы добавляет эшелонированную защиту, но не заменяет проверки границы — работникам и ключам API нужно то же правило.
- Как должен работать обмен между воркспейсами? — Моделируйте его явно — приглашением, строкой членства, — а не ослаблением области. «Любой со ссылкой» должен быть спроектированной функцией со своими проверками, а не случайностью.
- Что ломается первым в мультитенантных приложениях? — Удобные пути: ссылки восстановления, административные инструменты, фоновые работники, загружающие записи по голому ID. Аудируйте каждый путь кода, который трогает запись в обход обычной области.
- Как добавить ограничение области в существующую таблицу? — Добавьте
workspaceId, заполните его задним числом, обеспечьте его в чтениях и записях, затем троньте UI. Поставьте сначала негативные тесты, чтобы граница была доказана, а не предполагалась. - Нужны ли серверным действиям проверки, если страница защищена? — Да. Защита страниц скрывает экраны; она не авторизует операции. Каждое действие разрешает сессию и воркспейс независимо.