Код-ревью Senior Авторизация: IDOR, RBAC, ABAC обновлено

Ревью: мультиарендный обработчик на Go

Вопрос

Найдите проблемы изоляции арендаторов и авторизации.

func listInvoices(w http.ResponseWriter, r *http.Request) {
	tenant := r.Header.Get("X-Tenant-ID")
	status := r.URL.Query().Get("status")
	rows, err := db.Query("SELECT id, amount, customer FROM invoices WHERE tenant_id = $1 AND status = $2", tenant, status)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	writeJSON(w, scan(rows))
}

func getInvoice(w http.ResponseWriter, r *http.Request) {
	id := chi.URLParam(r, "id")
	row := db.QueryRow("SELECT id, amount, customer FROM invoices WHERE id = $1", id)
	writeJSON(w, scanOne(row))
}

Коротко

Список защищён фильтром, но фильтр берёт значение у клиента, а детальный просмотр фильтра не имеет. Типичная картина: проверку сделали в одном месте и забыли в другом.

Что должен найти ревьюер

  1. Безопасностьстрока 2
    Арендатор берётся из заголовка, который задаёт клиент: подставив чужой X-Tenant-ID, можно читать счета любой организации. Брать tenant из проверенного токена или сессии.
  2. Безопасностьстрока 6
    Текст ошибки БД уходит клиенту.
  3. Безопасностьстрока 14
    Детальный просмотр не фильтрует по арендатору вовсе: BOLA по id счёта.
  4. Ошибкастрока 8
    rows не закрываются (defer rows.Close()).
  5. Стильстрока 14
    Нет проверки прав пользователя внутри организации (роль на просмотр финансов).

Разбор

Список защищён фильтром, но фильтр берёт значение у клиента, а детальный просмотр фильтра не имеет. Типичная картина: проверку сделали в одном месте и забыли в другом. Надёжнее единый слой данных с tenant из контекста и RLS.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.