Ревью: запрос с идентификатором организации
Вопрос
Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности.
Читай вслух, как на ревью: сначала что делает код, потом что сломано.
// клиент
const orders = await fetch(`/api/orders?tenantId=${currentTenant.id}`).then((r) => r.json());
// сервер, обработчик
app.get('/api/orders', async (req, res) => {
const orders = await db.order.findMany({ where: { tenantId: req.query.tenantId } });
res.json(orders);
});
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
идентификатор организации приходит от клиента - подменив параметр, видно чужие данные
область видимости должна браться из токена сессии, а не из запроса
нет проверки, что пользователь состоит в этой организации
нет пагинации - выгрузка всей таблицы одним запросом
нет обработки ошибок и кода ответа при отсутствии прав
запрос без авторизации вообще - нет проверки сессии
Что могут спросить следом
- Как бы вы это проверяли в тестах?
- Где ещё в мульти-тенантной системе легко потерять фильтр?
Разбор
Это уязвимость, а не стилистика. tenantId приходит параметром запроса: пользователь
меняет его в адресной строке и получает заказы чужой организации. В мульти-тенантной системе
это утечка данных между клиентами - самый дорогой класс ошибок, вплоть до расторжения
договоров.
Правило: область видимости берётся из сессии, а не из запроса. Сервер достаёт организацию из токена и подставляет сам; параметр от клиента максимум сверяется со списком доступных пользователю.
Отдельно стоит сказать про гварды: если проверка прав выполняется до подстановки области видимости, запрос внутри неё уйдёт без фильтра, и заметно это не сразу.
Второстепенное, но важное: нет пагинации - метод выгружает всю таблицу и ляжет на первом крупном клиенте.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Предыдущий вопрос
Ревью: производное состояние
Следующий вопрос
Ревью: рендер пользовательского контента