CodeGym /Kursy /ChatGPT Apps /Kontrola dostępu i minimalizacja uprawnień: scopes, segme...

Kontrola dostępu i minimalizacja uprawnień: scopes, segmentacja, per‑tool permissions

ChatGPT Apps
Poziom 15 , Lekcja 0
Dostępny

1. Po co w ogóle myśleć o uprawnieniach w ChatGPT‑App (i na czym polega szczególne ryzyko)

W „zwykłej” aplikacji webowej między użytkownikiem a waszą bazą danych są raptem dwie warstwy: frontend, API, DB. W ChatGPT‑App między użytkownikiem a API pojawia się jeszcze jeden aktywny uczestnik — LLM. I to nie jest tylko „tekstowy filtr”, lecz byt, który:

  • sama wybiera, jakie narzędzia wywołać i z jakimi argumentami;
  • może zostać oszukana wstrzyknięciem promptu w danych;
  • może „pomylić” narzędzia albo wymyślić argumenty, których nie przewidzieliście.

Jeśli dacie LLM zbyt wiele uprawnień, dostajecie klasyczny problem Confused Deputy: model rzetelnie wykonuje to, co jej się wydaje, że prosi użytkownik albo tekst w dokumentach, ale przy tym wywołuje delete_all_orders zamiast get_last_order.

Dlatego nasz cel:

  1. Zminimalizować uprawnienia tokenów auth_token (jakie dane i działania są w ogóle dostępne).
  2. Ograniczyć, jakie narzędzia są dostępne modelowi w konkretnym scenariuszu.
  3. Dodać kontrolę człowieka tam, gdzie konsekwencje są szczególnie krytyczne.

I wszystko to trzeba zrobić bez paranoi i totalnego zakazu wszystkiego, inaczej App stanie się bezużyteczny. Równowaga między wygodą a bezpieczeństwem – to nasz główny cel w tym module.

2. Model dostępu w ekosystemie: kto do czego sięga

Aby się nie pogubić, spójrzmy na system jako całość. Mamy kilka poziomów, każdy ze swoją odpowiedzialnością i własnymi uprawnieniami.

flowchart TD
  U[Użytkownik w ChatGPT] --> C[ChatGPT UI + LLM]
  C --> A["Twój App (plan wizualny + widżet)"]
  A --> G[MCP Gateway / API Edge]
  G --> S[Serwery MCP i mikrousługi]
  S --> D[Bazy danych, kolejki, zewnętrzne API]

Krótko o rolach:

  • ChatGPT UI i LLM: zarządzane przez OpenAI. Wy nadajecie im instrukcje (system‑prompt, opisy narzędzi), ale nie kontrolujecie wewnętrznych tokenów i uprawnień platformy.
  • Wasz App (plan, tools, widżet): decydujecie, jakie narzędzia są dostępne, jak są opisane, jakich potwierdzeń UX wymagają, jakie dane widżet może wyświetlać.
  • MCP Gateway / API Edge: tutaj następuje weryfikacja tokenu, mapowanie userId, tenantId, listy scopes i trasowanie do właściwej usługi.
  • Serwery MCP i mikrousługi: wykonują narzędzia, robią zapytania do DB i zewnętrznych API. Tu powinny być maksymalnie surowe kontrole: scopes, izolacja tenant, walidacja danych wejściowych.
  • Magazyny i zewnętrzne API: ostatnia linia obrony (ograniczenia na poziomie DB, uprawnienia kont zewnętrznych usług).

Kluczowa myśl: LLM nie jest źródłem uprawnień dostępu. Wszystko, co trafia do serwera MCP, traktujemy jako „żądanie od użytkownika, sformułowane przez model”. Decydowanie, czy operację naprawdę wolno wykonać, to obowiązek waszego backendu, a nie promptu.

3. AuthN vs AuthZ: co już umiemy, a co dodajemy

W module o uwierzytelnianiu robiliście już:

  • AuthN (Authentication) — ustalaliście, kim jest użytkownik. Przez OAuth 2.1/PKCE ChatGPT pobierał od IdP token, który potem był dołączany do wywołań MCP. Zawierał on sub, user_id lub analog, czasem tenant_id.
  • Podstawowy AuthZ — być może potrafiliście już rozróżniać role user/admin i sprawdzaliście przynajmniej „czy to user” lub „czy to admin”.

Teraz komplikujemy obraz:

  • każdy auth_token powinien nieść zestaw scopes — tekstowe uprawnienia w formie resource:action, na przykład catalog:read, orders:write, payments:create;
  • wasz serwer MCP powinien sprawdzać zgodność tych scopes dla każdej akcji, a nie tylko „raz na wejściu”;
  • różne narzędzia, a nawet różne operacje w jednym narzędziu, mogą wymagać różnych scopes.

W terminologii OAuth 2.1 ChatGPT to „public client”, MCP to „resource server”, a wasz serwer OAuth wie, jakie scopes są wspierane i co dokładnie oznaczają. Metadane zasobu MCP zwykle deklarują scopes_supported, aby ChatGPT mógł poprosić użytkownika dokładnie o potrzebne uprawnienia.

4. Projektujemy scopes dla GiftGenius

Weźmy nasz edukacyjny GiftGenius i zobaczmy, jakie ma domeny danych i akcji. Z funkcjonalności mamy coś w rodzaju:

  • przegląd katalogu i kart produktów (prezentów);
  • rekomendacje na podstawie historii;
  • tworzenie zamówień;
  • uruchomienie checkoutu / obciążenie środków;
  • administrowanie edycją katalogu.

Zamiast robić jeden wszechmocny giftgenius:full_access, lepiej rozbić to na rozsądne scopes.

Konwencja nazewnicza: resource:action

Dobrze sprawdza się strategia resource:action, gdzie:

  • resource opisuje domenę: catalog, recommendations, orders, payments, admin.
  • action opisuje typ działania: read, write, czasem bardziej konkretnie: create, delete, manage.

Przykład dla GiftGenius:

Scope Co umożliwia
catalog:read
Czytać publiczny katalog prezentów
recommendations:read
Czytać historię rekomendacji użytkownika
orders:write
Tworzyć nowe zamówienia
orders:read
Czytać historię zamówień użytkownika
payments:create
Inicjować płatność / checkout
catalog:admin
Edytować katalog (tylko dla admin‑UI/wsparcia)

Zwykły użytkownik GiftGenius będzie potrzebował czegoś w rodzaju (wymieniane spacją): catalog:read recommendations:read orders:write orders:read payments:create. Administratorowi dodamy catalog:admin.

Ważne: nie robimy uniwersalnego *:* lub admin:all. Im bardziej granularnie, tym łatwiej potem odwołać konkretne uprawnienie bez psucia całej aplikacji.

Typy scopes: read vs write vs critical

Warto mentalnie oznaczać scopes kategoriami:

  • bezpieczne (read): nie zmieniają stanu, co najwyżej ujawniają dane;
  • modyfikujące (write): tworzą/zmieniają encje, nabijają liczniki, ale nie dotykają pieniędzy i nie usuwają wszystkiego hurtem;
  • krytyczne (critical): płatności, usunięcie konta, masowe usuwanie danych.

Dla krytycznych uprawnień można stosować podwyższoną kontrolę:

  • przyznawać je minimalnej liczbie użytkowników;
  • prosić użytkownika o osobną zgodę w UI ChatGPT przy wydawaniu tokenu;
  • po stronie MCP wymagać dodatkowego potwierdzenia (np. jednorazowego PIN‑u, to już scenariusze zaawansowane).

Scopes w kodzie: RequestContext i requireScope

Na poziomie MCP wygodnie mieć jeden typ kontekstu:

// mcp/context.ts
export interface RequestContext {
  userId: string;        // kto
  tenantId: string;      // w ramach której organizacji
  scopes: string[];      // jakie uprawnienia ma token
}

// Prosty helper do weryfikacji uprawnień
export function requireScope(
  ctx: RequestContext,
  needed: string
) {
  if (!ctx.scopes.includes(needed)) {
    throw new Error(`Missing scope: ${needed}`);
  }
}

Zakładamy, że RequestContext formujecie w MCP Gateway po weryfikacji tokenu: zdekodowaliście JWT, sprawdziliście podpis/wygaszenie, wyciągnęliście sub, tenant, scope — i dalej dołączacie ten kontekst do wszystkich wywołań narzędzi.

Dalej w handlerze narzędzia:

// mcp/tools/createOrder.ts
import { requireScope, RequestContext } from "../context";

export async function createOrder(
  input: CreateOrderInput,
  ctx: RequestContext
) {
  requireScope(ctx, "orders:write");
  // dalej – logika tworzenia zamówienia
}

Teraz, nawet jeśli model niespodziewanie wywoła createOrder tam, gdzie wg UX nie oczekiwaliście tego, bez orders:write narzędzie po prostu się nie wykona.

securitySchemes na poziomie narzędzia

Specyfikacja MCP pozwala każdemu narzędziu wskazać, jakich schematów autoryzacji i scopes potrzebuje. W oficjalnych przykładach securitySchemes podpinane są bezpośrednio do opisu narzędzia.

Przykład poglądowy:

// mcp/server.ts
server.registerTool(
  "createOrder",
  {
    title: "Create order",
    description: "Creates a new order for current user",
    inputSchema: {/*...*/},
    securitySchemes: [
      { type: "oauth2", scopes: ["orders:write"] }
    ]
  },
  async ({ input }, ctx: RequestContext) => {
    requireScope(ctx, "orders:write");
    // ...
  }
);

Mamy tu dwa poziomy ochrony:

  • deklaratywny: ChatGPT wie, że dla tego narzędzia potrzebny jest orders:write i przy braku uprawnień zainicjuje flow autoryzacji (albo o tym poinformuje użytkownika);
  • imperatywny: wasz kod jeszcze raz wszystko sprawdza przed realnym działaniem.

Jeśli token jest, ale brakuje scopes, serwer powinien zwrócić błąd z WWW-Authenticate: Bearer error="insufficient_scope", scope="orders:write" — a ChatGPT będzie mógł poprosić użytkownika o rozszerzenie uprawnień (step‑up authorization).

Insight

W oficjalnych przykładach używa się securitySchemes. Nie została ona jednak zatwierdzona w oficjalnej specyfikacji w takiej formie, w jakiej jest pokazana w przykładach ChatGPT Apps SDK. Dlatego trzeba ją oznaczać jako rozszerzenie oficjalnego protokołu — opakowując w _meta. Działający wariant powyższego przykładu:

// mcp/server.ts
server.registerTool(
  "createOrder",
  {
    title: "Create order",
    description: "Creates a new order for current user",
    inputSchema: {/*...*/},
    _meta: {										// w ten sposób
      securitySchemes: [
        { type: "oauth2", scopes: ["orders:write"] }
      ]          
    }
  },
  async ({ input }, ctx: RequestContext) => {
    requireScope(ctx, "orders:write");
    // ...
  }
);

5. Per‑tool permissions i „niebezpieczne” narzędzia

Scopes odpowiadają na pytanie „co ten auth_token może robić w zasadzie”. Ale w tokenie jest też lista narzędzi, z których model może korzystać. Też trzeba je uważnie projektować.

Klasyfikacja narzędzi

Umownie dzielimy narzędzia na:

  • informacyjne (informational / read‑only): czytają dane, budują raporty, liczą coś bez efektów ubocznych;
  • wykonawcze (consequential): zmieniają stan, pobierają pieniądze, coś usuwają.

W dokumentacji ChatGPT Apps zaleca się, by dla narzędzi read‑only wyraźnie oznaczać, że są bezpieczne, a dla niebezpiecznych — opisywać konsekwencje i włączać dodatkowe potwierdzenia UX.

Można to robić:

  • przez adnotacje do narzędzia (umowne pola typu readOnlyHint, destructiveHint);
  • przez opis tekstowy: „To narzędzie nieodwracalnie usuwa zamówienia”;
  • przez osobną flagę confirmation_required, którą wasz plan App wykorzystuje, by wstawić krok potwierdzenia do dialogu.

Potwierdzenia UX dla działań krytycznych

Na przykład GiftGenius ma narzędzie chargeCustomer (inicjuje pobranie środków). Oczywiście nie chcecie, by model wywoływał je bez zgody użytkownika.

Jak może to wyglądać na poziomie planu App:

// app/plan/tools.ts (pseudokod)
export const tools = [
  {
    name: "giftgenius.list_catalog",
    description: "Pokaż katalog prezentów",
    annotations: { readOnlyHint: true }
  },
  {
    name: "giftgenius.create_order",
    description: "Utwórz zamówienie bez płatności",
    annotations: { consequential: true }
  },
  {
    name: "giftgenius.charge_customer",
    description: "Pobrać środki za zamówienie",
    annotations: {
      consequential: true,
      destructiveHint: true,
      confirmationRequired: {
        title: "Pobrać środki z karty?",
        message: "Zostanie wykonana płatność za zamówienie N."
      }
    }
  }
];

Konkretne nazwy pól zależą od wersji SDK, ale idea pokrywa się z rekomendacjami: narzędzia read‑only oznaczamy jako bezpieczne, niebezpieczne — jako wymagające wyraźnego potwierdzenia i dobrego wyjaśnienia w opisie.

Następnie wasz widżet może zareagować: jeśli model proponuje wywołać charge_customer, pokazujecie użytkownikowi okno modalne z jasną formułą i dopiero po kliknięciu „Potwierdź” faktycznie wykonujecie wywołanie narzędzia.

Przykładowy komponent w widżecie (uproszczony):

// widget/components/ConfirmCharge.tsx
export function ConfirmCharge(props: {
  orderId: string;
  onConfirm: () => void;
}) {
  return (
    <div>
      <p>Pobrać środki za zamówienie {props.orderId}?</p>
      <button onClick={props.onConfirm}>
        Tak, potwierdzam płatność
      </button>
    </div>
  );
}

Model inicjuje pomysł „czas płacić”, ale ostateczny przycisk naciska człowiek. To właśnie human‑in‑the‑loop, który tak cenią specjaliści od bezpieczeństwa.

Narzędzia tylko dla agentów/back‑office

Jeszcze jeden częsty przypadek: macie narzędzia, z których mogą korzystać tylko agenci (w sensie Agents SDK) lub wewnętrzne admin‑pane, ale nie „zwykły” ChatGPT App użytkownika.

Na przykład rebuildSearchIndex lub syncCatalogFromERP. Najlepiej:

  • nie włączać ich do ogólnej listy tools dla zwykłego App;
  • skonfigurować w osobnym agencie/orkiestratorze;
  • zabezpieczyć osobnymi scopes i ewentualnie odrębnym konturem Auth.

Jeśli po prostu dodacie je do listy dostępnych narzędzi App, zwiększacie ryzyko, że model nagle postanowi: „A może przebuduję indeks już teraz, może pomoże znaleźć prezent”.

6. Segmentacja sieci i granice zaufania

Uprawnienia to nie tylko scopes na tokenie. Druga duża oś to segmentacja sieci i usług.

Idealny obraz:

  • macie dokładnie jedno publiczne wejście do backendu — MCP Gateway/Edge API;
  • wszystko, co przechowuje PII i pieniądze, żyje w prywatnej sieci/VPC i jest dostępne tylko przez ten gateway;
  • ruch wychodzący z backendu jest ograniczony listą dozwolonych domen (allowlist: provider płatności, CRM, własne mikrousługi).

Schematycznie:

flowchart LR
  ChatGPT -- HTTPS --> Edge[API Gateway / MCP Endpoint]
  Edge -- private network --> MCP[Serwer MCP]
  MCP -- private --> DB[(Baza danych z PII)]
  MCP -- private --> SVC[Wewnętrzne mikrousługi]
  MCP -- HTTPS (allow) --> Stripe[Payments API]

Ważnych jest tu kilka zasad:

  1. DB i wewnętrzne serwisy nie są wystawione bezpośrednio do internetu. Dostęp do nich tylko z prywatnej sieci i tylko z tych usług, którym jest to rzeczywiście potrzebne.
  2. Edge/Gateway realizuje auth i rate‑limiting. To on weryfikuje token i scopes, ogranicza zbyt częste żądania i zapisuje główne logi audytowe.
  3. Kontrola egress. Serwer MCP nie powinien móc wychodzić do dowolnych URL w internecie (ataki SSRF, wycieki danych). Listę zewnętrznych hostów lepiej jawnie ograniczyć.

W praktyce, jeśli wdrażacie MCP na Vercel, Render albo w klastrze Kubernetes, części tych rzeczy nie konfiguruje się ręcznie, ale nawet tam można rozdzielać:

  • osobne projekty/klastry dla dev/staging/prod;
  • różne zmienne środowiskowe i klucze dla każdego środowiska;
  • osobną usługę „edge” (HTTP‑owa otoczka MCP) i osobne prywatne serwisy.

Sumując, mamy już dwie osie ochrony: uprawnienia na tokenie (scopes) i granice sieci. Dodajmy do tego jeszcze jedną — wielodzierżawczość (multi‑tenant), gdy jedno i to samo App obsługuje wiele organizacji.

7. Multi‑tenant / kontekst organizacyjny

Dotąd myśleliśmy o jednym użytkowniku. Jednak wiele aplikacji ChatGPT jest wielodzierżawczych (multi‑tenant): jeden App obsługuje dziesiątki firm. GiftGenius łatwo przerobić na usługę B2B dla korporacji: każdy dział ma własne katalogi, budżety, zamówienia.

Co to jest tenant i skąd go wziąć

Tenant to zwykle:

  • organizacja/firma (Acme Corp);
  • przestrzeń robocza (workspace);
  • czasem projekt lub środowisko.

Najważniejsza właściwość: dane jednego tenanta nie mogą być widoczne dla innego.

W auth‑flow tenant zwykle trafia do:

  • claimu tokenu (tenant, org_id);
  • osobnego parametru w żądaniu autoryzacji (ale to mniej niezawodne niż claim podpisany przez IdP).

Ważne: ufamy tylko tenantId z weryfikowanego tokenu, a nie z argumentów narzędzi. Jeśli model wygeneruje {"tenantId": "acme"}, a w tokenie użytkownika jest tenantId: "globex", należy to traktować jako próbę włamania.

Tenant w kontekście żądania

Dodajmy tenantId do naszego RequestContext (już to zrobiliśmy wyżej) i nie pozwalajmy nadpisywać go z danych wejściowych.

Podstawowa kontrola:

// mcp/tenant.ts
import { RequestContext } from "./context";

export function enforceTenant<TInput>(
  input: TInput & { tenantId?: string },
  ctx: RequestContext
) {
  if (input.tenantId && input.tenantId !== ctx.tenantId) {
    throw new Error("Tenant mismatch");
  }
  return { ...input, tenantId: ctx.tenantId };
}

Następnie w narzędziu:

// mcp/tools/listOrders.ts
export async function listOrders(
  input: { limit?: number; tenantId?: string },
  ctx: RequestContext
) {
  const safe = enforceTenant(input, ctx);
  return db.order.findMany({
    where: { tenantId: safe.tenantId },
    take: safe.limit ?? 20
  });
}

Ignorujemy tenant z argumentów i twardo podstawiamy go z kontekstu. Dzięki temu, nawet jeśli LLM lub atakujący spróbuje „podłożyć” cudzy tenant, nic z tego nie wyjdzie.

Izolacja tenant na poziomie DB

Architektonicznie są różne warianty:

  • oddzielna DB na tenanta;
  • oddzielne schematy;
  • jedna DB z tenant_id w każdej tabeli i twardą filtracją.

Niezależnie od wyboru, złota zasada jest jedna: żadne zapytanie do DB nie może być wykonane bez filtracji po tenant_id z kontekstu. Szczególnie ważne jest to w RAG/wyszukiwaniu wektorowym: jeśli zapomnieć o filtrze po tenancie, model może zacząć szukać w dokumentach cudzych organizacji.

8. Jak to spiąć z naszym Next.js/Apps SDK

Zbierzmy to teraz razem i zobaczmy, jak scopes, tenant i granice sieci lądują w naszym projekcie Next.js na Apps SDK. Dodajmy więcej konkretu i spójrzmy na kod Next.js i Apps SDK.

Gdzie żyją scopes i tenant w naszym projekcie

Typowy układ dla projektu edukacyjnego:

  • W aplikacji Next.js (Apps SDK) macie konfigurację App/konektora i strony dla callbacków OAuth.
  • W serwerze MCP — kod, który przyjmuje żądania HTTP/SSE z ChatGPT, weryfikuje token i wywołuje właściwe narzędzie.

Przenosimy tam wszystko, o czym była mowa:

  1. W ustawieniach OAuth zasobu MCP deklarujemy scopes_supported dla GiftGenius (catalog:read, orders:write, itd.).
  2. W konfiguracji Apps SDK opisujemy App z wyliczeniem narzędzi i ich adnotacjami (read‑only, consequential, confirmation‑flows).
  3. W serwerze MCP implementujemy:
    • parsowanie i weryfikację tokenu;
    • formowanie RequestContext { userId, tenantId, scopes };
    • helpery requireScope, enforceTenant itd.;
    • zapytania do DB zawsze z tenantId z kontekstu.

Przykład „izolowanej” ścieżki tworzenia zamówienia

Prześledźmy jeden scenariusz end‑to‑end.

  1. Użytkownik pisze: „Złóż zamówienie na ten zestaw z budżetem 50 $”.
  2. Model uznaje, że trzeba wywołać giftgenius.create_order z argumentami { productId, budget, ... }.
  3. ChatGPT sprawdza: czy App ma narzędzie create_order, jakie ma dla niego scopes i jakie securitySchemes są wpisane. Rozumie, że potrzebny jest orders:write.
  4. Jeśli token już jest i zawiera orders:write, żądanie idzie dalej; jeśli nie — ChatGPT inicjuje autoryzację OAuth z prośbą o wymagany scope.
  5. MCP Gateway przyjmuje żądanie, sprawdza token, formuje RequestContext z userId=123, tenantId="acme", scopes=["catalog:read","orders:write",...].
  6. createOrder wewnątrz MCP:
    • robi requireScope(ctx, "orders:write");
    • przez enforceTenant utrwala tenant;
    • tworzy zamówienie wyłącznie w ramach tenantId="acme".
  7. Jeśli zamówienie wymaga natychmiastowej płatności, model lub sam backend dalej inicjuje charge_customer, gdzie:
    • narzędzie w planie jest oznaczone jako confirmationRequired;
    • widżet renderuje ConfirmCharge i prosi użytkownika o jawne potwierdzenie obciążenia.

W ten sposób mamy obronę w głąb: zbyt szerokie prompty, wstrzyknięcia promptu lub nawet błędy w UX nie doprowadzą do niekontrolowanych działań, bo na dole piramidy i tak stoją twarde kontrole scopes, tenant i ręczne potwierdzenia dla krytycznych akcji.

9. Typowe błędy przy projektowaniu uprawnień i segmentacji

Błąd nr 1: Jeden gruby scope w rodzaju app:full_access.
Takie podejście jest wygodne na demo, ale niebezpieczne w produkcji. Utracicie jeden token — tracicie wszystko. Nie da się odwołać lub zabronić jednej operacji bez psucia pozostałych. Rozbijajcie uprawnienia po domenach i typach operacji (read/write/critical).

Błąd nr 2: Sprawdzanie uprawnień tylko „na wejściu” i brak kontroli wewnątrz narzędzi.
Czasem robi się tak: „skoro ChatGPT dostał token, to już wszystko umie”. A dalej narzędzie createOrder jest po prostu wywoływane, nawet jeśli dla tego konkretnego tokenu nie było nadane orders:write. Właściwe podejście — sprawdzać scopes w każdym narzędziu (albo przynajmniej scentralizowanym middleware dla wszystkich operacji modyfikujących).

Błąd nr 3: Brak oznaczania niebezpiecznych narzędzi i brak wymogu potwierdzenia.
Jeśli narzędzie pobiera pieniądze, usuwa dane lub zmienia dostępy, nie powinno wyglądać dla modelu tak samo jak listCatalog. Brak wyraźnych adnotacji i potwierdzeń UX zwiększa szansę, że model wywoła je „bo tak logicznie”. Minimum to rozdzielać narzędzia read‑only i destrukcyjne oraz wyraźnie oznaczać te drugie.

Błąd nr 4: Ufanie tenantId z argumentów narzędzia.
Bardzo częsty anty‑wzorzec: narzędzie getOrders({ tenantId }), gdzie tenantId przychodzi od modelu. Jeśli użyć go bezkrytycznie, użytkownik z tenantA może uzyskać dostęp do danych tenantB, po prostu podając inny identyfikator. Tenant musi pochodzić z weryfikowanego tokenu i być narzucony wszystkim zapytaniom do DB i zewnętrznych usług, a wartości od użytkownika są ignorowane lub walidowane co do zgodności.

Błąd nr 5: MCP/DB dostępne bezpośrednio z internetu.
Czasem w prostych prototypach serwer MCP i DB wystają do internetu na gołym HTTP/5432. W świecie prod tak nie robimy: cały dostęp powinien iść przez jeden zabezpieczony gateway/proxy, a DB mieszkać w prywatnej sieci. W przeciwnym razie dowolny znaleziony podatny endpoint czy dziurawy webhook — prosta droga do danych.

Błąd nr 6: Używanie tych samych scopes/sekretów w dev i prod.
Ulubiony sposób na niespodziewane usunięcie danych produkcyjnych podczas demonstracji lokalnego dev‑środowiska. Dla każdego środowiska powinny być własne klucze, scopes i DB. Nawet jeśli ktoś zdobędzie dostęp do dev‑tokenu, nie zaszkodzi danym produkcyjnym.

Błąd nr 7: Niechęć do „odmawiania modelowi”.
Czasem deweloperzy się martwią: „Jeśli będę często zwracał błędy insufficient_scope lub forbidden, model będzie działał gorzej”. W praktyce to normalne i oczekiwane zachowanie: model uczy się, jakie działania są dostępne, a jakie wymagają dodatkowych uprawnień lub potwierdzenia. Gorzej, jeśli „sukcesem” wykona to, czego nie powinien — na przykład przeprowadzi drugą płatność.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION