Inżynieria

Autoryzacja w SaaS nie może kończyć się w kontrolerze

Jak chronić operacje wielotenantowego SaaS niezależnie od tego, czy wywołuje je kontroler, job, import czy konsola.

authorize! w kontrolerze to dobry początek. W wielotenantowym SaaS nie jest jednak ostateczną granicą bezpieczeństwa.

Ta sama operacja może zostać uruchomiona przez żądanie HTTP, zadanie w tle, import, konsolę administracyjną albo inną komendę biznesową. Jeżeli reguła istnieje tylko w kontrolerze, bezpośrednie wywołanie serwisu może ją ominąć.

Pełny kontekst dostępu

Krytyczna komenda powinna otrzymać jawny kontekst wykonania:

  • aktora,
  • organizację,
  • członkostwo i rolę organizacyjną,
  • rolę lub uprawnienie modułowe,
  • informację o impersonacji,
  • źródło wywołania.

Nie chodzi o przekazywanie luźnego hasha przez całą aplikację. Warto użyć małego obiektu wartości, który opisuje zweryfikowany dostęp i ma jednoznaczny kontrakt.

Controller / Job / Import
           ↓
Verified Access Context
           ↓
     Business Command
           ↓
      Domain Decision

Trzy warstwy, trzy odpowiedzialności

UI pokazuje tylko działania dostępne dla użytkownika. To redukuje frustrację i prowadzi przez proces, ale nie stanowi zabezpieczenia.

Policy chroni endpoint oraz zakres zapytania. Powinna szybko odrzucić niedozwolone żądanie i nie dopuścić, aby rekord z innej organizacji został w ogóle załadowany.

Komenda biznesowa ponownie weryfikuje warunki istotne dla operacji. Nie ufa temu, że wywołujący wcześniej zrobił wszystko poprawnie. Dzięki temu zasada działa również poza HTTP.

To nie musi oznaczać potrójnego kopiowania logiki. Wspólny obiekt dostępu może odpowiadać na pytania o członkostwo i uprawnienia, a policy i komenda używają tego samego kontraktu w różnych rolach.

Tenant scope przed identyfikatorem

Organizacja nie powinna być wywnioskowana z dowolnego parametru przesłanego przez użytkownika. Najpierw wyznaczamy organizacje dostępne dla aktora. Dopiero w tym zakresie szukamy rekordu wskazanego w URL.

current_organization.records.find(params[:id])

Taki porządek ma znaczenie. Poprawny identyfikator obiektu należącego do innego tenantu nadal kończy się bezpiecznym not found, zamiast ujawniać istnienie rekordu albo polegać na późniejszym sprawdzeniu.

W jobach sytuacja jest podobna. Nie wystarczy zapisać samo record_id. Job powinien dostać stabilny kontekst organizacji oraz aktora lub jawnie działać jako system. Po deserializacji ponownie ładuje dane w prawidłowym zakresie.

Impersonacja wymaga dwóch tożsamości

Gdy operator wspiera użytkownika przez impersonację, zdarzenie ma co najmniej dwie osoby: aktora biznesowego oraz rzeczywistego operatora. Autoryzacja musi uwzględniać ograniczenia sesji impersonowanej, a audyt zachować obie tożsamości.

Nie każda operacja dozwolona administratorowi powinna być dostępna podczas impersonacji. Eksporty, zmiany zabezpieczeń lub działania między organizacjami często wymagają bezpośredniej sesji operatora.

Ostateczna zasada

Nie ufaj miejscu, z którego przyszło wywołanie. Ufaj jawnie zweryfikowanemu kontekstowi operacji.

Kontroler, policy i komenda nie konkurują ze sobą. Razem tworzą warstwową ochronę, w której UX jest czytelny, zapytania są ograniczone do tenantu, a domeny nie można ominąć alternatywną ścieżką wykonania.

To podejście jest częścią szerszych standardów technologicznych Thinqcraft. Jeśli porządkujesz autoryzację w istniejącym systemie Rails, zacznij rozmowę.

Powiązane usługi

Usługi powiązane z tym artykułem.

ModernizacjaModernizacja Rails bez zatrzymywania produkcji.

Upgrade Rails, migracje React/Vite, tuning PostgreSQL, Linux, NGINX, SSL, Puma, workery, kolejki i powtarzalny deployment.