Mérnöki kézikönyv · 04 / 09 · Biztonság és adatvédelem

Adatbiztonság és adatvédelem

Fejlesztőknek, CTO-knak, IT és compliance vezetőknek szól, akik személyes vagy üzleti adatot kezelő rendszert építenek vagy felügyelnek. Az ötven éves biztonsági modellektől a GDPR, a NIS2 és az AI Act fejlesztői kontrolljaiig minden módszer lépésekkel, kódpéldával és tipikus hibával szerepel.

Kalmár Dániel · kb. 66 perc olvasás · A sorozat része, Mérnöki kézikönyvek

Tartalom

A módszerek EU környezetre (GDPR, NIS2, AI Act, DORA) és Supabase alapú, AI asszisztált fejlesztésre vannak lefordítva, a mögöttük álló elvek viszont bármely technológiai környezetben érvényesek. Minden módszernél megvan a lépéssor, az alkalmazási terület, a tipikus hiba és a mai jelentése. A szabályozási részek fejlesztői kontrollokat neveznek meg, jogi értelmezést nem adnak.

A 2026 októberi állapot szerint a NIST SP 800-63B-4 végleges (2025. augusztus). Az OAuth 2.1 még Internet-Draft (16. revízió, 2026. szeptember), a gyakorlatban mégis ez a viszonyítási alap. A NIST FIPS 203, 204 és 205 szabványok 2024 augusztusa óta véglegesek.

Klasszikus gyökerek, mai módszerként

A hatvanas és hetvenes évek biztonsági irodalma ma is élő tudás, mert ugyanazokat a kérdéseket tette fel, amelyeket egy mai Supabase RLS policy megválaszol. Ki az alany, mi az objektum, milyen műveletről van szó, ki dönt, és hogyan bizonyítható, hogy a döntés mindig lefut.

Dennis és Van Horn, capability (1966)

A gondolat. A jogosultság egy nem hamisítható token, amelynek birtoklása maga a jog. A rendszer ilyenkor azt vizsgálja, mit tart a hívó a kezében, és nem azt, hogy kicsoda. Egy capability egyszerre nevezi meg az objektumot és a megengedett műveleteket.

A módszer ma.

  1. Számba vesszük azokat a műveleteket, ahol a megosztás egyetlen linkkel vagy tokennel történik (meghívó, fájlmegosztás, jelszó visszaállítás, webhook).
  2. A token kriptográfiailag erős véletlenből készül (legalább 128 bit), soha nem szekvenciális azonosítóból.
  3. A tokenbe vagy a hozzá tartozó rekordba belekötjük a hatókört (melyik objektum, milyen művelet) és a lejáratot.
  4. A tokent hash formában tároljuk, mint egy jelszót, így adatbázis szivárgáskor nem használható.
  5. A token visszavonható (revoked_at oszlop), és lehetőleg egyszer használatos.
import { randomBytes, createHash } from "node:crypto";

export function newCapability() {
  const token = randomBytes(32).toString("base64url"); // 256 bit
  const tokenHash = createHash("sha256").update(token).digest("hex");
  return { token, tokenHash }; // a token megy a linkbe, a hash az adatbázisba
}

Mikor érdemes. Megosztás olyan féllel, akinek nincs fiókja, időkorlátos delegálás, gépek közötti hívás szűk hatókörrel.

Tipikus hiba. A capability szivárog (Referer fejléc, logok, böngésző előzmények), és nincs lejárata. A Supabase storage signed URL is capability, ezért rövid lejárattal érdemes kiadni (percekre, nem hetekre).

Mai kapcsolat. Az OAuth access token, a signed URL, a macaroon és a biscuit token mind capability. LLM ügynököknél a capability szemlélet a legtisztább védelem, az ügynök csak olyan tokent kap, amely egyetlen feladatra, rövid időre szól. Az ügynökök jogosultságkezeléséről az AI ügynökök és vibecoding kézikönyv ír részletesen.

Ware jelentés (1970)

Willis Ware RAND jelentése (Security Controls for Computer Systems) volt az első rendszerszintű felmérés. Fő tanulsága szerint a biztonságot nem lehet utólag ráhegeszteni egy megosztott, több felhasználós rendszerre, mert a hardver, a szoftver, a kommunikáció, a személyzet és a fizikai környezet mind támadási felület. A jelentés már kimondta, hogy a rendszernek a legkevésbé megbízható felhasználóval szemben is védenie kell az adatot.

Mai módszer. Minden új projekt elején egyoldalas lista készül a támadási felületről (kliens, API, adatbázis, storage, edge function, harmadik fél, emberek, CI). Ebből indul a későbbi fenyegetésmodellezés, amelyet a Kiberbiztonság kézikönyv dolgoz fel.

Lampson, Protection (1971), access matrix

A modell. A sorok az alanyok (felhasználók, folyamatok), az oszlopok az objektumok (fájlok, táblák, sorok), a cellákban a jogok állnak (read, write, own). Két tárolási mód létezik. Oszloponként tárolva access control list (ACL), soronként tárolva capability lista lesz belőle.

Lépések.

  1. A szerepek és az erőforrások táblázata elkészül, mielőtt egyetlen policy születne.
  2. A cellákba a műveletek kerülnek (select, insert, update, delete), a feltételükkel együtt.
  3. Minden üres cella tiltás. Ez a fail safe default.
  4. Az RLS policykat a táblázatból generáljuk vagy azzal vetjük össze, a táblázat pedig verziózva a repóban él, a kód mellett.
Alany \ Objektumprojects (saját tenant)projects (más tenant)invoicesaudit_log
ownerCRUDnincsCRUDR
memberR, U ha szerkesztőnincsRnincs
anonnincsnincsnincsnincs
service (cron)RRU (státusz)I

Tipikus hiba. A mátrix csak a fejekben él, és a policyk ellentmondanak egymásnak. AI asszisztált fejlesztésnél ez gyakori, mert a kódgeneráló modell táblánként ír policyt, a teljes kép ismerete nélkül.

Anderson jelentés (1972), a referencia monitor

James Anderson USAF jelentése vezette be a referencia monitor fogalmát, amelyhez három követelménynek kell teljesülnie.

  1. Teljes közvetítés (always invoked). Minden hozzáférés áthalad rajta, nincs megkerülő út.
  2. Hamisításbiztosság (tamperproof). Az alanyok nem módosíthatják.
  3. Ellenőrizhetőség (verifiable). Elég kicsi és egyszerű ahhoz, hogy átvizsgálható és tesztelhető legyen.

Supabase fordítás. A Postgres RLS a referencia monitor. Teljes közvetítés akkor van, ha minden táblán be van kapcsolva az RLS, és nincs olyan út (security definer függvény, view, service_role kulcs a kliensben), amely megkerüli. Hamisításbiztosságról akkor beszélhetünk, ha a felhasználó nem tudja módosítani azokat az adatokat, amelyekre a policy épít (például a saját role oszlopát a profiles táblában). Ellenőrizhető a rendszer, ha a policyk rövidek, egy helyen vannak (migrációban), és automatizált teszt fut rájuk.

Bell és LaPadula (1973), titkosság

Szabályok. Az alanyok és az objektumok biztonsági szinttel rendelkeznek (például nyilvános, belső, bizalmas, szigorúan titkos).

Mai alkalmazás. A no write down a legtöbbet használt szabály, csak más néven. Bizalmas táblából nem kerülhet adat nyilvános cache-be, analitikába, logba vagy LLM promptba. A szinteket az adatosztályozás adja (lásd lent), a star property-t pedig a pipeline ellenőrzések valósítják meg.

Tipikus hiba. Egy riport view a bizalmas táblából aggregál egy mindenki által olvasható táblába. Ez write down, és senki nem vette észre, mert a view security definer jogokkal fut.

Biba (1977), integritás

A Bell és LaPadula modell tükörképe, integritásra. No read down (megbízható folyamat nem olvas megbízhatatlan inputot ellenőrzés nélkül) és no write up (megbízhatatlan alany nem ír megbízható adatba).

Mai alkalmazás. A prompt injection Biba sértés. Az LLM megbízhatatlan inputot olvas (weboldal, email, feltöltött dokumentum), majd magas integritású műveletet végez (adatbázis írás, email küldés). A módszer szerint az ilyen adatból származó kimenet alacsony integritású, és csak emberi jóváhagyás vagy szigorú validáció után léphet magasabb szintre. Ugyanez igaz a webhookokra, mert aláírás ellenőrzés nélkül a webhook payload nem írhat a fizetési táblába.

Denning lattice model (1976)

Dorothy Denning az információáramlást hálóként (lattice) formalizálta. Minden adat címkét kap, két adat kombinációja a két címke legkisebb felső korlátját kapja. Ebből következik, hogy egy bizalmas és egy nyilvános mezőből számolt érték is bizalmas.

Mai módszer. Az oszlopok címkét kapnak (Postgres COMMENT vagy security label), a származtatott táblák, view-k és exportok címkéje pedig a forrásaik maximuma. Ezt egy egyszerű CI szkript ellenőrizheti a migrációkon.

Saltzer és Schroeder (1975), a nyolc elv

A The Protection of Information in Computer Systems című cikk nyolc tervezési elve ma is a legjobb ellenőrzőlista. Mindegyikhez mai példa és Supabase vonatkozás tartozik.

ElvJelentésMai példaEllenőrzés
Economy of mechanismA védelmi mechanizmus kicsi és egyszerűEgy is_member(tenant_id) függvény minden policyban, nem tíz eltérő joinPolicy hossz, ismétlődés a migrációkban
Fail safe defaultsA hozzáférés alapja az engedély, nem a kizárásRLS bekapcsolva, policy nélkül semmi sem látszikSupabase advisor, rls_enabled minden public táblán
Complete mediationMinden hozzáférés ellenőrzött, cache sem kerülheti megAz edge function is a user JWT-vel hív, nem service_role-lalKódkeresés service_role kulcsra
Open designA biztonság nem a titkos terven múlikAz anon kulcs nyilvános, a védelem az RLS-ben vanKiindulópont, hogy a támadó ismeri az API-t
Separation of privilegeKritikus művelethez több feltétel kellKifizetés jóváhagyása két külön felhasználótólA négy szem elv tesztje
Least privilegeMinden alany csak a feladatához szükséges jogot kapjaCron job saját Postgres role-lal, csak egy tábláraGRANT lista áttekintése
Least common mechanismA több felhasználó által osztott közös mechanizmus minimálisBérlőnként külön storage prefix, közös cache kulcsban tenant_idCache kulcs séma
Psychological acceptabilityA védelem könnyen, helyesen használhatóPasskey a jelszó helyett, értelmes hibaüzenetFelhasználói teszt, support jegyek

A cikk két további elvet is említ, amelyeket a szerzők gyengébbnek tartottak. A work factor (a támadás költsége legyen nagyobb a nyereségnél) ma a kriptográfiai paraméterválasztás alapja. A compromise recording (a betörés legyen észlelhető) mai megfelelője az audit log és a riasztás.

Használat. Kódáttekintésnél a nyolc soron végigmegyünk. AI által generált kódnál a fail safe defaults és a complete mediation sérül leggyakrabban, mert a modell a működő demóra optimalizál.

Clark és Wilson (1987), kereskedelmi integritás

A katonai titkossági modellekkel szemben Clark és Wilson a könyvelés világából indult ki.

Fogalmak.

Megvalósítás Postgresben.

  1. A CDI táblákra a kliens nem kap közvetlen insert, update, delete jogot.
  2. A módosítás egyetlen jól definiált függvényen (RPC) keresztül történik, amely tranzakcióban ellenőriz és ír.
  3. Minden TP auditnaplót ír.
  4. Az IVP egy ütemezett lekérdezés, amely invariánsokat ellenőriz (például hogy a főkönyv kiegyenlített).
-- a kliens nem írhat közvetlenül
revoke insert, update, delete on public.ledger_entries from authenticated;

create or replace function public.approve_payout(p_payout_id uuid)
returns void
language plpgsql
security definer
set search_path = ''
as $$
declare
  v_requested_by uuid;
begin
  select requested_by into v_requested_by
  from public.payouts where id = p_payout_id for update;

  if v_requested_by = auth.uid() then
    raise exception 'separation of duty: a kérelmező nem hagyhatja jóvá';
  end if;

  if not public.has_role(auth.uid(), 'finance_approver') then
    raise exception 'nincs jogosultság';
  end if;

  update public.payouts set status = 'approved', approved_by = auth.uid(), approved_at = now()
  where id = p_payout_id and status = 'pending';

  insert into public.audit_log(actor, action, object_id)
  values (auth.uid(), 'payout.approve', p_payout_id);
end;
$$;

revoke execute on function public.approve_payout(uuid) from public, anon;
grant execute on function public.approve_payout(uuid) to authenticated;

Tipikus hiba. A TP létezik, de a táblán közben nyitva maradt egy update policy, így a kliens megkerüli.

Orange Book (TCSEC, 1983), röviden

Az amerikai védelmi minisztérium értékelési kritériumai négy osztályt határoztak meg. D (minimális védelem), C1 és C2 (diszkrecionális védelem, C2-ben már egyéni elszámoltathatóság és audit), B1, B2, B3 (kötelező, címke alapú védelem, egyre szigorúbb architektúrával), A1 (formálisan igazolt terv). Ma a Common Criteria (ISO/IEC 15408) váltotta, de két gondolata maradandó. A trusted computing base legyen minél kisebb, az assurance szintje pedig külön kérdés a funkcionalitástól. Egy rendszer tudhat sokat, és közben mégis alacsony bizonyosságot nyújthat.

Morris és Thompson, Password Security (1979)

A Unix jelszótárolás esettanulmánya három módszert vezetett be, amelyek ma is alapok.

  1. Só. Minden jelszóhoz egyedi véletlen érték tartozik, így az azonos jelszavak eltérő hasht adnak, és az előre számolt táblák (később rainbow table) használhatatlanok.
  2. Lassú függvény. A DES alapú crypt szándékosan lassú volt, hogy a próbálgatás drága legyen.
  3. Jelszóválasztás ösztönzése. A szerzők kimutatták, hogy a felhasználói jelszavak nagy része rövid vagy szótári.

A mai megfelelő az argon2id vagy a bcrypt (lásd lent, a jelszótárolásnál). A tanulmány alapján a só nem titok, a lassúság a védelem lényege, a gyenge jelszavakat pedig a regisztrációkor kell kiszűrni.

Diffie és Hellman (1976), RSA (1977)

A New Directions in Cryptography bevezette a nyilvános kulcsú kriptográfiát és a kulcscserét, amellyel két fél nyilvános csatornán állapít meg közös titkot. Rivest, Shamir és Adleman egy évvel később gyakorlati sémát adott titkosításra és aláírásra.

Mai jelentés fejlesztőnek. A DH ma X25519 formában él (TLS 1.3, Signal, WireGuard). Az RSA még elterjedt, de új rendszerbe aláírásra Ed25519, kulcscserére X25519, hosszabb távra hibrid X25519 plusz ML-KEM a javasolt. RSA-t saját implementációban sosem írunk. A textbook RSA padding nélkül törhető, a PKCS#1 v1.5 titkosítás pedig padding oracle támadásra érzékeny (Bleichenbacher, 1998).

Ross Anderson, Security Engineering, 3. kiadás (2020)

Anderson könyve a biztonságot mérnöki szakterületként írja le, ahol a technika, az emberek és a pénz egyszerre számít.

A négyes keret

ElemKérdésSupabase projektben
PolicyMit kell elérni?Access matrix, adatosztályozás, megőrzési szabályok
MechanismMilyen eszközzel?RLS, Auth, titkosítás, Vault, rate limit
AssuranceMennyire bízhatunk a mechanizmusban?pgTAP tesztek, két felhasználós teszt, pentest, advisor
IncentiveKinek mi az érdeke?Ki fizet a betörésért, ki dönt, ki tartja karban

Használat. Minden biztonsági döntésnél mind a négy elemet leírjuk, és a hiányzó elem mutatja a hiba helyét. Leggyakrabban az assurance hiányzik (van policy, nincs teszt), vagy torz az incentive (a fejlesztő a határidőért felel, a betörésért nem).

Pszichológia

Gazdaságtan

Hozzáférés szabályozási modellek

Áttekintés

ModellKi döntErősségGyengeségTipikus megvalósítás
DACAz objektum tulajdonosaRugalmas, természetesSzivárgás megosztással, nehéz auditálniUnix jogok, Google Drive megosztás, owner_id oszlop
MACA rendszer, címkék alapjánErős, központiMerev, drága üzemeltetniSELinux, Bell és LaPadula, adatosztály címke
RBACSzerep hozzárendelésEgyszerű audit, szervezetben jól skálázódikSzerep robbanás, kontextus nélküliNIST RBAC, Postgres role, app_role oszlop
ABACAttribútumokon futó szabályFinom, kontextusfüggőNehéz átlátni és tesztelniNIST SP 800-162, AWS IAM feltételek, Cedar
ReBACKapcsolati gráfMegosztás, hierarchia természetesenGráf bejárás költségeGoogle Zanzibar, OpenFGA, SpiceDB

RBAC a NIST modell szerint

A NIST RBAC (Ferraiolo és Kuhn, 1992, később ANSI INCITS 359) négy szintet ír le. Core RBAC (felhasználó, szerep, jogosultság, session), hierarchikus RBAC (szerepek öröklése), statikus separation of duty (két szerep nem adható ugyanannak a személynek), dinamikus separation of duty (egy sessionben nem aktiválható egyszerre).

Lépések.

  1. A jogosultság (permission) a legkisebb egység, például invoice.approve.
  2. A szerep jogosultságok csoportja. A kód mindig jogosultságot ellenőriz, nem szerepet, így a szerepek átszervezése nem igényel kódmódosítást.
  3. A szerep hozzárendelése bérlőn belül történik (tenant_id, user_id, role).
  4. A szerep nem a felhasználó által írható táblában tárolódik.
create table public.memberships (
  tenant_id uuid not null references public.tenants(id) on delete cascade,
  user_id   uuid not null references auth.users(id) on delete cascade,
  role      text not null check (role in ('owner','admin','member','viewer')),
  primary key (tenant_id, user_id)
);

create table public.role_permissions (
  role text not null,
  permission text not null,
  primary key (role, permission)
);

create or replace function public.has_permission(p_tenant uuid, p_perm text)
returns boolean
language sql stable security definer set search_path = ''
as $$
  select exists (
    select 1
    from public.memberships m
    join public.role_permissions rp on rp.role = m.role
    where m.tenant_id = p_tenant
      and m.user_id = (select auth.uid())
      and rp.permission = p_perm
  );
$$;

Tipikus hiba. Szerep robbanás (admin_eu_readonly_finance_v2). Ha húsznál több szerep kell, az az ABAC vagy a ReBAC jele.

ABAC

Az ABAC (NIST SP 800-162, 2014) négy attribútumcsoportot értékel. Alany (osztály, beosztás), erőforrás (adatosztály, tulajdonos), művelet és környezet (idő, IP, eszköz állapota). A szabály logikai kifejezés.

Postgres példa. Bizalmas dokumentumot csak a saját osztály láthat, és csak MFA-val belépett session.

create policy "confidential docs by department with mfa"
on public.documents for select to authenticated
using (
  classification <> 'confidential'
  or (
    department_id = (select public.current_department())
    and (select auth.jwt() ->> 'aal') = 'aal2'
  )
);

Az aal claim a Supabase JWT-ben jelzi, hogy a session MFA-val emelt szintű (aal2). Ez a legegyszerűbb ABAC környezeti attribútum.

ReBAC és Google Zanzibar (2019)

A Zanzibar cikk (USENIX ATC 2019) a Google egységes jogosultsági rendszerét írja le (Drive, YouTube, Calendar). Alapegysége a relation tuple, olvasva objektum#reláció@alany, például doc:42#viewer@user:anna vagy doc:42#viewer@group:eng#member. A jogosultság egy névtérkonfigurációban leírt szabály, amely relációkat kombinál (a szerkesztő egyben néző is, a mappa nézője a benne lévő dokumentum nézője). A konzisztenciát a zookie token biztosítja, amely megakadályozza, hogy az új tartalmat régi ACL alapján értékelje a rendszer (new enemy problem).

Kis léptékben, Postgresben.

  1. Egy relation_tuples tábla (object_type, object_id, relation, subject_type, subject_id, subject_relation).
  2. A szabályok kiértékelése rekurzív CTE-vel vagy előre materializált hozzáférési táblával történik.
  3. Nagy gráfnál (mély mappahierarchia, sok csoport) dedikált szolgáltatás a jobb választás, például OpenFGA vagy SpiceDB, és az RLS csak a materializált eredményt nézi.

Mikor érdemes. Ha a termékben van megosztás, mappa, csapat, csoporton belüli csoport. Ha csak bérlő és szerep van, az RBAC elég.

Policy as code (OPA, Cedar)

permit (
  principal,
  action == Action::"viewInvoice",
  resource
) when { principal.tenant == resource.tenant };

forbid (principal, action, resource)
when { resource.classification == "restricted" && !context.mfa };

Döntési szempont. Ha az adat Postgresben van és a kliens közvetlenül éri el (Supabase), az RLS a referencia monitor, mert csak az biztosít teljes közvetítést. Policy motor akkor kell mellé, ha több szolgáltatás és több adattár osztozik a szabályokon. Ilyenkor a policy a repóban él, verziózott, és tesztek futnak rajta, mint bármely más kódon.

Multi tenant minták összevetése

SzempontKözös tábla, tenant_id oszlopSchema per tenantAdatbázis per tenant
Elválasztás erősségeLogikai, az RLS-en múlikKözepes, search_path és jogokErős, fizikai
KöltségAlacsonyKözepesMagas
MigrációEgyszer futBérlőnként fut, lassulBérlőnként fut, orkesztrálás kell
Bérlők közötti riportEgyszerűNehézkesKülön ETL
Zajos szomszédIgenIgenNem
Bérlő törlése (GDPR)Kaszkád törlés, tesztelni kelldrop schemaAdatbázis törlés
Supabase illeszkedésNatív, ez a fő mintaKorlátozott (az API csak kitett sémákat lát)Projekt ügyfelenként, drága
MikorSaaS, sok kis bérlőKevés, nagyobb bérlő, testreszabott sémaSzabályozott ügyfél, adatrezidencia, szerződéses elvárás

A közös tábla minta lépései.

  1. Minden bérlőhöz tartozó táblában tenant_id not null, idegen kulccsal.
  2. Index a tenant_id-re (és összetett indexek tenant_id első oszloppal).
  3. Egyetlen segédfüggvény a tagságra, minden policy ezt hívja.
  4. Az insert és update policy with check része is ellenőrzi a tenant_id-t, különben a felhasználó más bérlőbe írhat.
  5. Idegen kulcsoknál biztosítani kell, hogy a hivatkozott sor ugyanabban a bérlőben legyen (összetett idegen kulcs a tenant_id, id párra).
-- összetett kulcs, hogy a task csak a saját tenant projectjére mutathasson
alter table public.projects add constraint projects_tenant_id_id_key unique (tenant_id, id);

alter table public.tasks
  add constraint tasks_project_same_tenant
  foreign key (tenant_id, project_id) references public.projects (tenant_id, id);

A sématervezés tágabb kérdéseit (kényszerek, normalizálás, sémaevolúció) az Adat és adatbázisok kézikönyv tárgyalja.

IDOR és BOLA elkerülése

Az IDOR (insecure direct object reference) és az API-k világában használt BOLA (broken object level authorization, 2019 és 2023 óta az OWASP API Security Top 10 első helye) ugyanaz a hiba. A szerver elfogadja a kliens által küldött azonosítót, és nem ellenőrzi, hogy az objektum a hívóé-e.

Módszer.

  1. Az objektumszintű ellenőrzés a lekérdezésben történik, nem utána. Supabase-ben ezt az RLS automatikusan adja.
  2. Saját API-nál minden lekérdezés tartalmazza a hívó kontextusát (where id = $1 and tenant_id = $2).
  3. A nem kitalálható azonosító (UUID) nem jogosultság, csak nehezíti a felderítést. Az UUIDv7 időrendezett, részben kitalálható, ezért különösen nem védelem.
  4. Hiány esetén a válasz 404, nem 403, így a létezés sem derül ki.
  5. A teszt mindig két felhasználóval fut (lásd lent, a kétfelhasználós tesztelésnél).
// rossz: service_role kliens, a felhasználótól jövő id-vel
const { data } = await admin.from("invoices").select("*").eq("id", req.params.id);

// jó: a felhasználó JWT-jével hívunk, az RLS dönt
const supabase = createClient(url, publishableKey, {
  global: { headers: { Authorization: req.headers.authorization! } },
});
const { data } = await supabase.from("invoices").select("*").eq("id", req.params.id).maybeSingle();
if (!data) return res.status(404).end();

Rokon hiba a BOPLA (broken object property level authorization), vagyis a mass assignment. A kliens olyan mezőt is küld (role, tenant_id, is_paid), amelyet nem írhatna. Ellenszere az oszlopszintű GRANT vagy egy trigger, amely tiltja az érzékeny oszlop változását.

revoke update on public.profiles from authenticated;
grant update (display_name, avatar_url) on public.profiles to authenticated;

Hitelesítés

NIST SP 800-63B-4, a fejlesztőt érintő változások

A 2025 augusztusában véglegesített negyedik revízió a jelszószabályokat szigorúbb kötelezésekké tette (should helyett shall), és a phishing rezisztenciát állította középpontba.

TémaElőírás a 4. revízióbanMit jelent a kódban
Minimális hosszEgyedüli faktorként használt jelszónál legalább 15 karakter, MFA részeként legalább 8Regisztrációs validáció, hibaüzenet a hosszra
Maximális hosszLegalább 64 karaktert el kell fogadniLevágás nélkül, a bcrypt 72 bájtos korlátjára figyelve
Összetételi szabályTilos (nem kötelezhető kis és nagybetű, szám, speciális karakter)A regex szabályok törlése
Kötelező periodikus csereTilos, csere csak kompromittálódás jeléreNincs 90 napos lejárat
TiltólistaKötelező ellenőrzés ismert kiszivárgott, gyakori és kontextusfüggő jelszavak ellenHIBP k-anonymity API vagy helyi lista
KarakterkészletUnicode és szóköz elfogadása, normalizálás (NFKC vagy NFKD)Normalizálás hash előtt
Jelszó emlékeztető, biztonsági kérdésTilosNincs anyja leánykori neve mező
Beillesztés, jelszókezelőEngedélyezni kellA beillesztés nem tiltható
Próbálkozás korlátozásKötelező rate limitFiókonként és IP-nként
Szinkronizált passkeyAAL2-n elfogadott (syncable authenticator)A passkey lehet elsődleges MFA
Phishing rezisztenciaAAL2-n ajánlott opció, AAL3-on kötelezőWebAuthn, nem SMS
SMS, hangalapú OTPKorlátozott (restricted) hitelesítő, kockázatelemzés és alternatíva kellNem lehet az egyetlen második faktor

Regisztrációkor.

  1. A jelszót Unicode NFKC formára normalizáljuk.
  2. Ellenőrizzük a hosszt (15 vagy 8 karakter, attól függően, van-e kötelező MFA).
  3. Ellenőrizzük a tiltólistát. A Have I Been Pwned range API a hash első öt hexa karakterét kéri, így a jelszó nem hagyja el a szervert.
  4. A jelszót argon2id függvénnyel hasheljük.
import { createHash } from "node:crypto";

export async function isPwned(password: string): Promise<boolean> {
  const sha1 = createHash("sha1").update(password.normalize("NFKC")).digest("hex").toUpperCase();
  const prefix = sha1.slice(0, 5);
  const suffix = sha1.slice(5);
  const res = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {
    headers: { "Add-Padding": "true" },
  });
  const body = await res.text();
  return body.split("\n").some((line) => line.startsWith(suffix));
}

A Supabase Auth beállításaiban a leaked password protection kapcsoló ugyanezt végzi (a 2026 októberi állapot szerint a Pro csomagtól, ellenőrizendő), és a minimális hossz is beállítható. Saját kód írása helyett ezt érdemes bekapcsolni.

Jelszótárolás

AlgoritmusAjánlott paraméter (OWASP Password Storage Cheat Sheet)Megjegyzés
argon2idm = 19 MiB, t = 2, p = 1 (alternatíva m = 46 MiB, t = 1)Első választás, memóriaigényes, GPU ellen jó
scryptN = 2^17, r = 8, p = 1Ha argon2 nem elérhető
bcryptcost legalább 10, inkább 1272 bájtnál levágja a bemenetet, ezért hosszú jelszónál előtte HMAC vagy argon2
PBKDF2 HMAC SHA256600 000 iterációCsak ha FIPS megfelelés kell
MD5, SHA1, SHA256 egyszerűenTilosGyors hash, nem jelszóra való

Lépések.

  1. A hash idejét a saját szerveren mérjük. Bejelentkezésenként 100 és 500 ms között legyen, terhelés alatt is.
  2. A hash stringben (PHC formátum) benne van az algoritmus és a paraméter. Ha belépéskor a tárolt paraméter elavult, sikeres ellenőrzés után újrahasheljük (rehash on login).
  3. Opcionális a pepper, egy szerveroldali titok (KMS-ben), amellyel a hash előtt HMAC számolható. Adatbázis szivárgáskor a hashek pepper nélkül nem támadhatók, de a pepper rotációja nehéz.
  4. Az összehasonlítás konstans idejű függvénnyel történik (a könyvtárak ezt elvégzik).
import argon2 from "argon2";

const OPTS = { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 };

export const hashPassword = (pw: string) => argon2.hash(pw.normalize("NFKC"), OPTS);

export async function verifyPassword(stored: string, pw: string) {
  const ok = await argon2.verify(stored, pw.normalize("NFKC"));
  const needsRehash = ok && argon2.needsRehash(stored, OPTS);
  return { ok, needsRehash };
}

Supabase Auth használatakor a jelszó hashelését (bcrypt) a GoTrue végzi, mellé saját jelszótábla nem kell.

MFA és passkey (WebAuthn)

Az MFA erősségi sorrendje. Első helyen a hardveres biztonsági kulcs és a passkey áll (phishing rezisztens), utána a TOTP alkalmazás, majd a push értesítés számegyeztetéssel, végül az SMS és az email kód. Az MFA fáradtság (push bombázás) ellen számegyeztetés kell.

A passkey bevezetése.

  1. Szerveroldali könyvtárra építünk (TypeScripthez SimpleWebAuthn), saját implementáció nem készül.
  2. Regisztrációkor a szerver challenge-et generál (véletlen, egyszer használatos, sessionhöz kötött), a böngésző a navigator.credentials.create hívással kulcspárt hoz létre, a szerver pedig eltárolja a credential id-t és a nyilvános kulcsot.
  3. Belépéskor új challenge készül, a navigator.credentials.get hívás után a szerver ellenőrzi az aláírást, az origint, az rpId-t és a signCount értéket (szinkronizált passkeynél ez gyakran 0, ami nem hiba).
  4. A phishing rezisztencia forrása, hogy a böngésző az origint beleírja az aláírt adatba. Hamis domainen a passkey nem használható.
  5. Felhasználónként legalább két hitelesítő marad meg, és erős helyreállítási út is van.

Tipikus hiba. A passkey mellett megmarad a gyenge helyreállítás (email link, SMS), így a támadó azon jön be. A helyreállítás a leggyengébb láncszem.

A Supabase Auth natívan támogatja a TOTP MFA-t, az újabb verziók a WebAuthn faktort is (a 2026 októberi állapot szerint, a dokumentációban ellenőrizendő). Az aal szint a JWT-ben megjelenik. Kritikus műveletnél (fizetés, adatexport, szerepváltoztatás) az aal2 ellenőrzése RLS-ben vagy RPC-ben történik.

Sessionkezelés

ElemBeállításMiért
Cookie HttpOnlyMindig a session cookie-nA JavaScript, és így az XSS sem olvassa
SecureMindigCsak HTTPS-en megy
SameSiteAlapból Lax, admin felületen StrictCSRF ellen
__Host- előtag__Host-sessionKikényszeríti a Secure, Path=/ és domain nélküli cookie-t
Idle timeoutÉrzékeny alkalmazásban 15 és 30 perc közöttElhagyott gép
Abszolút lejárat8 és 24 óra között, vagy hosszabb, ha a kockázat engediEllopott session korlátozása
RotációÚj session id belépéskor és jogosultság emeléskorSession fixation ellen
KijelentkezésSzerveroldali érvénytelenítés, nem csak cookie törlésAz ellopott token ne éljen tovább
Session listaA felhasználó látja és visszavonhatjaGyanús eszköz kizárása

Supabase-ben az access token rövid életű JWT (alapértelmezés szerint egy óra), a refresh token rotálódik, és újrahasználat detektálással rendelkezik. SSR alkalmazásban (Next.js) a @supabase/ssr csomag cookie-ba teszi a sessiont. Szerveroldalon a felhasználót a getUser() vagy a getClaims() hívás ellenőrzi. A getSession() eredményére nem építünk, mert az a cookie tartalmát ellenőrzés nélkül adja vissza.

OAuth 2.0, OAuth 2.1, OIDC, PKCE

Szerepek. Resource owner (felhasználó), client (alkalmazás), authorization server (például Supabase Auth, Entra ID), resource server (API).

Az OAuth 2.1 változásai a 2.0-hoz képest (draft 16, a 2026 októberi állapot szerint).

Ezek a 2.0 Security Best Current Practice (RFC 9700, 2025. január) ajánlásai is, így 2.0 alatt is kötelezőnek tekintendők.

A PKCE lépései.

  1. A kliens generál egy code_verifier értéket (43 és 128 karakter közötti véletlen).
  2. Kiszámolja a code_challenge = BASE64URL(SHA256(code_verifier)) értéket.
  3. Az authorize kérésben elküldi a challenge-et és a method=S256 paramétert.
  4. A token kérésben elküldi a verifiert, a szerver pedig ellenőrzi, hogy a hash egyezik.
  5. Így egy elfogott authorization code önmagában értéktelen.
const verifier = crypto.getRandomValues(new Uint8Array(32));
const verifierStr = btoa(String.fromCharCode(...verifier)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifierStr));
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
  .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

A Supabase JS kliens a flowType pkce értékével ezt elvégzi (az SSR csomagban ez az alapértelmezett).

OIDC. Az OAuth az engedélyezésről szól, az OIDC hitelesítési réteget tesz rá (ID token, userinfo, discovery). Az ID token a kliensnek szól (ki lépett be), az access token az API-nak (mit tehet). ID token nem mehet API hívásban hozzáférési tokenként. Az iss, aud, exp és nonce mezők ellenőrzése kötelező.

Backend for Frontend (BFF). Böngészős alkalmazásnál a legbiztonságosabb minta, ha a tokeneket egy vékony szerveroldali réteg tartja, és a böngésző csak HttpOnly session cookie-t kap. Az IETF OAuth for Browser-Based Apps dokumentuma is ezt ajánlja első helyen.

JWT csapdák

CsapdaMi történikVédelem
alg noneA token aláírás nélkül is elfogadottA könyvtár explicit elfogadott algoritmuslistát kap, a fejlécben érkezőt nem fogadjuk el
Algoritmus csere (RS256 helyett HS256)A nyilvános kulccsal mint HMAC titokkal aláírt token átmegyKulcsonként rögzített algoritmus
Hosszú lejáratAz ellopott token napokig él, nem vonható visszaAccess token 5 és 60 perc között, refresh token rotációval
localStorage tárolásXSS-sel kiolvashatóHttpOnly cookie vagy BFF
Érzékeny adat a payloadbanA JWT csak base64, bárki olvassaCsak azonosító és claimek, semmi személyes adat
kid manipulációA kid alapján a szerver tetszőleges fájlt vagy URL-t tölt be kulcskéntA kid csak egy előre ismert JWKS listából jöhet
Hiányzó aud ellenőrzésMás szolgáltatásnak kiadott token itt is átmegyAz aud és az iss kötelező ellenőrzése
Visszavonás hiányaKijelentkezés után a token még élRövid lejárat, kritikus műveletnél session ellenőrzés az adatbázisban

A Supabase 2025 óta aszimmetrikus JWT signing kulcsokat kínál (ES256 vagy RS256, JWKS végponttal). Ezekkel az edge function és a saját backend helyben, hálózati hívás nélkül ellenőrizheti a tokent, a kulcs pedig rotálható a régi tokenek azonnali érvénytelenítése nélkül. Új projektben ez a javasolt út a megosztott HS256 titok helyett.

import { createRemoteJWKSet, jwtVerify } from "jose";

const JWKS = createRemoteJWKSet(new URL(`${SUPABASE_URL}/auth/v1/.well-known/jwks.json`));

export async function verify(token: string) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: `${SUPABASE_URL}/auth/v1`,
    audience: "authenticated",
    algorithms: ["ES256", "RS256"],
  });
  return payload;
}

API kulcsok

  1. A formátum felismerhető előtagot kap (például app_live_ és app_test_), így a secret scanner megtalálja, a fejlesztő pedig látja, éles kulcsról van-e szó.
  2. Legalább 128 bit véletlen, base62 vagy base64url kódolással.
  3. A kulcsot hash formában tároljuk (SHA-256 elég, mert a kulcs nagy entrópiájú, nem jelszó). Az első néhány karakter azonosításra külön tárolható.
  4. A kulcs csak létrehozáskor, egyszer jelenik meg.
  5. Minden kulcsnak van hatóköre (scope), bérlője, lejárata és utolsó használati időpontja.
  6. Rotáció átfedéssel, az új kulcs már él, mielőtt a régit visszavonjuk.
  7. Rate limit kulcsonként.

A Supabase 2025-ben új kulcsrendszert vezetett be. Kliensbe a publishable kulcs (sb_publishable_ előtag) való, ez az anon szerepet kapja. Szerveroldalra a secret kulcs (sb_secret_ előtag) tartozik, az RLS-t megkerülő jogokkal, és a böngészőből érkező kérést elutasítja. A régi anon és service_role JWT kulcsok új projektben nem javasoltak, meglévő projektben a migrációt érdemes megtervezni.

Gyakorlati kriptográfia

A fő források Ferguson, Schneier és Kohno Cryptography Engineering (2010) című könyve, valamint Jean-Philippe Aumasson Serious Cryptography második kiadása (2024). Közös tanulságuk szerint a kriptográfiai hibák szinte mindig az illesztésnél születnek (kulcskezelés, nonce, módválasztás, hibaüzenet), és ritkán az algoritmusban.

Mit használjunk, mit ne

CélJavasoltKerülendő
Szimmetrikus titkosításAES-256-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305AES-ECB, AES-CBC MAC nélkül, DES, 3DES, RC4
Nonce kezelés kevés kontrollalXChaCha20-Poly1305 (192 bites nonce, véletlen is biztonságos), AES-GCM-SIVAES-GCM véletlen 96 bites nonce-szal nagy volumenben (2^32 üzenet felett veszélyes)
AláírásEd25519, ECDSA P-256 (determinisztikus nonce-szal)RSA PKCS#1 v1.5 új rendszerben, DSA
KulcscsereX25519, hibridként X25519MLKEM768Statikus RSA kulcscsere, saját DH paraméter
HashSHA-256, SHA-512, SHA-3, BLAKE2b, BLAKE3MD5, SHA-1
MACHMAC-SHA256, Poly1305 (AEAD-ben)Saját hash(kulcs + üzenet) konstrukció (length extension)
Kulcsderiválás kulcsbólHKDFEgyszerű hash
Kulcsderiválás jelszóbólargon2idPBKDF2 alacsony iterációval
Véletlencrypto.getRandomValues, crypto.randomBytes, /dev/urandomMath.random
Könyvtárlibsodium (libsodium-wrappers, sodium-native), Web Crypto, Node cryptoSaját implementáció, elavult könyvtár
TranszportTLS 1.3, legalább TLS 1.2 erős suite-talSSL, TLS 1.0, TLS 1.1

Három szabály.

  1. Mindig AEAD (hitelesített titkosítás). Az integritás nélküli titkosítás módosítható.
  2. A nonce soha nem ismétlődhet ugyanazzal a kulccsal. AES-GCM-nél egyetlen ismétlés a hitelesítés teljes összeomlását okozza.
  3. A rejtjelezett adathoz associated data formában hozzákötjük a kontextust (rekord id, tenant id, oszlopnév). Így egy titkosított mezőt nem lehet átmásolni egy másik sorba.
import sodium from "libsodium-wrappers";

await sodium.ready;

export function encryptField(plain: string, key: Uint8Array, aad: string) {
  const nonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
  const ct = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plain, aad, null, nonce, key);
  return { nonce: sodium.to_base64(nonce), ct: sodium.to_base64(ct) };
}

export function decryptField(enc: { nonce: string; ct: string }, key: Uint8Array, aad: string) {
  return sodium.to_string(
    sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
      null, sodium.from_base64(enc.ct), aad, sodium.from_base64(enc.nonce), key,
    ),
  );
}
// aad példa: `${tenantId}:${tableName}:${rowId}:${column}`

Envelope encryption és KMS

A minta. Az adatot egy adatkulccsal (DEK, data encryption key) titkosítjuk, a DEK-et pedig egy kulcstitkosító kulccsal (KEK), amely a KMS-ben marad, és sosem hagyja el. A DEK titkosított formában az adat mellett tárolódik.

Lépések.

  1. A KMS-ben (AWS KMS, Google Cloud KMS, Azure Key Vault, HashiCorp Vault Transit) létrehozzuk a KEK-et, bérlőnként vagy adatosztályonként.
  2. Íráskor a KMS GenerateDataKey hívása visszaad egy nyílt és egy titkosított DEK-et.
  3. A nyílt DEK-kel helyben AEAD titkosítás történik, utána a nyílt DEK törlődik a memóriából.
  4. A titkosított DEK, a nonce és a rejtjel kerül az adatbázisba.
  5. Olvasáskor a KMS Decrypt visszaadja a DEK-et, a visszafejtés helyben történik.

Előnyök. A KEK rotációja csak a DEK-ek újratitkosítását igényli, az adatét nem. A KMS naplózza a hozzáférést. A bérlőnkénti KEK törlése kriptográfiai törlést (crypto shredding) tesz lehetővé, ami a mentésekben lévő másolatokat is olvashatatlanná teszi. GDPR törlési kérelemnél a mentésekre gyakran ez az egyetlen megvalósítható út.

Supabase. A Supabase Vault (pgsodium alapú, ma a vault.secrets tábla és a vault.decrypted_secrets view) titkok tárolására jó, például harmadik féltől kapott API kulcsokhoz. Oszlopszintű titkosításra a pgsodium Transparent Column Encryption funkcióját a Supabase új projektben már nem ajánlja. Érzékeny mezőkhöz az alkalmazásrétegbeli titkosítás (a fenti kódpélda) és egy külső KMS a tisztább út. A Supabase a lemezt AES-256-tal, a forgalmat TLS-sel titkosítja. Ez a GDPR 32. cikke szerinti alapszint, de az adatbázishoz hozzáférő szolgáltatótól és a service_role kulcs birtokosától nem véd.

Kulcsrotáció

Kulcs típusaRotációMódszer
KEK (KMS)Évente automatikusanA KMS verziókat tart, a régi verzió visszafejtésre él
DEKRekordonként vagy időablakonként újAz envelope minta miatt olcsó
JWT signing kulcsÉvente, gyanú esetén azonnalJWKS-ben átfedéssel, kid alapján
API kulcs, harmadik fél90 naptól egy évig, gyanúra azonnalKettős kulcsos időszak
Adatbázis jelszóSzemélycserénél, gyanúraSecret store, újraindítás nélküli betöltés
TLS tanúsítványAutomatikusan (ACME), 2026-tól a CA/B Forum döntése szerint fokozatosan rövidülő élettartammalCsak automatizálva

Szabály. Minden kulcs mellett van azonosító (kid, key_version), mert enélkül nem dönthető el, melyik kulccsal kell visszafejteni, és a rotáció lehetetlen.

Post quantum átállás

Állás 2026 októberében.

Harvest now, decrypt later. A támadó ma rögzíti a titkosított forgalmat, és akkor fejti vissza, amikor lesz elég nagy kvantumszámítógép. A hosszú ideig titkos adat (egészségügyi, jogi, pénzügyi, állami) forgalmát ezért már most hibrid kulcscserével kell védeni. Az aláírás kevésbé sürgős, mert a hamisításhoz a kvantumgépnek az aláírás idején kell léteznie.

Fejlesztői teendők.

  1. Kriptográfiai leltár (cryptographic bill of materials). Hol van RSA, ECDH, ECDSA, milyen könyvtárral, milyen adatot véd, és meddig kell az adatnak titokban maradnia.
  2. Crypto agility. Az algoritmus konfigurálható, a titkosított adat mellett verziómező áll.
  3. Hibrid TLS. A hibrid X25519MLKEM768 a Chrome, a Firefox, a Cloudflare és az OpenSSL 3.5 óta alapértelmezett vagy elérhető. Saját szervernél ellenőrizni kell, hogy a TLS könyvtár támogatja.
  4. Szimmetrikus kriptográfia. Az AES-256, a SHA-256 és az erősebb változatok kvantum ellen is megfelelők, ott a 128 bites kulcsok 256 bitesre emelésén túl nincs teendő.
  5. Auditált könyvtár. Saját PQC implementáció nem készül. A választás liboqs, a Go és a Node crypto beépített ML-KEM támogatása, a BoringSSL vagy az AWS-LC.

Titokkezelés

Szabályok

  1. Titok soha nem kerül a repóba, a kliens bundle-be, a logba, a hibajelentésbe vagy az LLM promptba.
  2. Fejlesztésben .env.local fájl, amely a .gitignore-ban szerepel. Élesben a platform secret store (Vercel environment variables, Supabase edge function secrets, GitHub Actions secrets OIDC-vel, Doppler, Infisical, 1Password, cloud secret manager).
  3. A NEXT_PUBLIC_, VITE_ és hasonló előtagú változó a böngészőbe kerül. Titok sosem kaphat ilyen előtagot.
  4. CI-ban hosszú életű felhőkulcs helyett OIDC federáció működik (GitHub Actions és AWS, GCP, Azure között), így nincs tárolt titok.
  5. Minden titoknak van gazdája, létrehozási dátuma és rotációs ideje.
  6. Pre-commit hook és CI scan kötelező.

Szivárgás felderítése

EszközErősségHasználat
gitleaksGyors, regex és entrópia alapú, pre-commit és CIgitleaks git --redact, gitleaks protect --staged
trufflehogTöbb száz detektor, élő ellenőrzés (verified), git történet, S3, Docker imagetrufflehog git file://. --only-verified
GitHub secret scanning és push protectionPartnerprogram, a szolgáltató automatikusan visszavonhatA repó beállításaiban kapcsolható be
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.24.0
    hooks:
      - id: gitleaks

Teendők kiszivárgott kulcsnál

A lépések sorrendje kötött, mert az idő számít. A nyilvános GitHub repóba került kulcsot automatizált botok perceken belül megtalálják.

  1. Azonnali visszavonás és rotáció. A szolgáltatónál érvénytelenítjük a kulcsot, újat generálunk, és telepítjük. Ez megelőzi a git történet tisztítását.
  2. A hatókör felmérése. Mit érhetett el a kulcs (service_role esetén a teljes adatbázist, RLS nélkül).
  3. A naplók átnézése. A szivárgás pillanatától kezdve szokatlan használatot keresünk a szolgáltató naplóiban (Supabase API logok, felhő audit log, számlázás).
  4. A git történet tisztítása. git filter-repo vagy BFG, force push, a forkokról és a cache-ekről a GitHub supporttal egyeztetve. A tisztítás nem helyettesíti a rotációt, mert a kulcs már másolódhatott.
  5. Az incidens rögzítése. Ha személyes adathoz férhettek hozzá, a GDPR 33. cikke szerint 72 órán belül értesíteni kell a felügyeleti hatóságot (Magyarországon a NAIH-ot), hacsak a kockázat nem valószínűtlen. NIS2 hatály alatt jelentős incidensnél 24 órás korai figyelmeztetés kell a CSIRT felé. A döntést a felelős vezető hozza, a fejlesztő a tényeket adja.
  6. Az okok kezelése. Pre-commit hook, push protection, rövidebb élettartamú kulcs, OIDC.

Az incidenskezelés teljes folyamatát a Kiberbiztonság kézikönyv írja le.

Privacy by Design, Cavoukian hét elve

Ann Cavoukian, Ontario adatvédelmi biztosa a kilencvenes években fogalmazta meg, 2009-ben hét elvként publikálta a gondolatot. A GDPR 25. cikke jogi kötelezettséggé tette.

ElvFejlesztői fordításKonkrét kontroll
1. Proaktív, nem reaktív, megelőző, nem helyreállítóAdatvédelmi kérdések a tervezésnél, nem a launch előttLINDDUN a design reviewban, adatfolyam ábra
2. Adatvédelem mint alapbeállításFelhasználói beavatkozás nélkül is védettOpt-in analitika, privát alapértelmezett profil, minimális mezők
3. Beépített adatvédelemA rendszer része, nem kiegészítőRLS, pszeudonim azonosítók, megőrzési job a sémával együtt
4. Teljes funkcionalitás (pozitív összeg)Nem kell a funkciót feláldozniAggregált riport nyers adat helyett, kliensoldali feldolgozás
5. Végponttól végpontig biztonság, teljes életciklusGyűjtéstől törlésigTitkosítás, megőrzési idő, igazolt törlés, mentések kezelése
6. Láthatóság és átláthatóságEllenőrizhető, mit csinál a rendszerA valós működéssel összhangban lévő adatkezelési tájékoztató, audit log
7. A felhasználó tiszteleteÉrintett központúÖnkiszolgáló export, törlés, hozzájárulás kezelés, érthető szöveg

GDPR fejlesztői szemmel

Az 5. cikk elvei és a fejlesztői teendők

ElvKérdés a kódhozTeendő
Jogszerűség, tisztességesség, átláthatóságMelyik jogalapon gyűjtjük ezt a mezőt?Mezőszintű jogalap a nyilvántartásban, hozzájárulás esetén consent tábla időbélyeggel és verzióval
Célhoz kötöttségCsak arra használjuk, amire gyűjtöttük?Új cél (például modell tanítás) új jogalapot és elemzést kér, nem kódmódosítást
AdattakarékosságKell ez a mező egyáltalán?Születési dátum helyett nagykorúság jelző, teljes cím helyett irányítószám
PontosságJavítható az adat?Felhasználói szerkesztés, forrás frissítése
Korlátozott tárolhatóságMeddig kell?Megőrzési idő táblánként, automatikus törlés vagy anonimizálás
Integritás és bizalmas jellegVédett illetéktelen hozzáféréstől?RLS, titkosítás, hozzáférés naplózás
ElszámoltathatóságTudjuk bizonyítani?Migrációk, tesztek, nyilvántartás, DPIA, döntési napló a repóban

A 25. cikk, beépített és alapértelmezett adatvédelem

Az adatkezelőnek a tervezéskor és a kezelés során is megfelelő technikai és szervezési intézkedéseket kell hoznia (például pszeudonimizálást), és alapértelmezésként csak a célhoz szükséges adatot kezelheti. Fejlesztői fordításban minden új funkció pull requestje rövid adatvédelmi blokkot tartalmaz.

## Adatvédelmi hatás
- Új személyes adat mező: igen/nem, melyik
- Adatosztály: (lásd adatosztályozás)
- Jogalap és cél: hivatkozás a nyilvántartás sorára
- Megőrzés: X nap, utána törlés/anonimizálás
- Ki fér hozzá (RLS policy neve):
- Harmadik félhez kerül-e (LLM, analitika, email):

A 32. cikk, az adatkezelés biztonsága

A cikk kockázatarányos intézkedéseket kér, és négyet név szerint is említ. Ezek fejlesztői megfelelői a következők.

DPIA, mikor és hogyan

Mikor kötelező (35. cikk és a NAIH listája alapján). Ha az adatkezelés valószínűleg magas kockázattal jár. Jellemző esetek a nagy léptékű különleges adat (egészségügy, biometria), a rendszeres, kiterjedt megfigyelés, a joghatással járó döntést hozó profilalkotás, az új technológia (az AI rendszerek jellemzően ide tartoznak), a sérülékeny érintettek (gyerekek, munkavállalók) és az adatkészletek összekapcsolása. A NAIH közzétette a kötelező DPIA esetek magyar listáját, ezt az adott projektnél ellenőrizni kell.

Lépések.

  1. Az adatkezelés leírása (cél, adatkörök, érintettek, címzettek, adatfolyam ábra, megőrzés).
  2. A szükségesség és az arányosság értékelése (jogalap, adattakarékosság, alternatívák).
  3. A kockázatok azonosítása az érintettek szemszögéből, nem a cégéből. Ehhez a lent ismertetett LINDDUN jól használható.
  4. Kockázatkezelő intézkedések, maradó kockázat.
  5. Az adatvédelmi tisztviselő (DPO) véleménye, ha van ilyen.
  6. Magas maradó kockázatnál előzetes konzultáció a NAIH-hal (36. cikk).
  7. Felülvizsgálat változáskor.

A fejlesztő az első, a harmadik és a negyedik lépéshez ad érdemi bemenetet (adatfolyam, technikai intézkedések). AI rendszernél az AI Act alapjogi hatásvizsgálata (FRIA, 27. cikk, bizonyos telepítőknek) összevonható a DPIA-val.

Adatkezelési nyilvántartás (30. cikk)

A nyilvántartás táblázatos. A technikai oszlopokat a fejlesztőcsapat tartja naprakészen, lehetőleg a kóddal együtt verziózott, gépileg olvasható formában a repóban, amelyből emberi olvasásra alkalmas változat generálható.

- tevekenyseg: Ügyfélfiók kezelése
  cel: Szolgáltatás nyújtása
  jogalap: szerződés teljesítése, 6. cikk (1) b)
  erintettek: ügyfelek felhasználói
  adatkorok: [név, email, cég, belépési napló]
  tablak: [public.profiles, auth.users, public.audit_log]
  cimzettek: [Supabase (adatfeldolgozó, EU régió), Resend (email), LLM szolgáltató (zero retention)]
  harmadik_orszag: USA, adatvédelmi keretrendszer és SCC
  megorzes: fiók törlése után 30 nap, napló 1 év
  biztonsag: [RLS, MFA adminnak, TLS, titkosított mentés]

Érintetti jogok technikai kiszolgálása

JogCikkTechnikai megvalósításHatáridő
Hozzáférés15.Export függvény, amely minden táblából összegyűjti a felhasználó adataitEgy hónap, indokoltan hosszabbítható
Helyesbítés16.Profil szerkesztés, admin felületEgy hónap
Törlés17.Törlési folyamat kaszkáddal, storage fájlokkal, harmadik felekkelEgy hónap
Korlátozás18.restricted jelző, amelyet az RLS és a feldolgozó jobok figyelnekEgy hónap
Adathordozhatóság20.Géppel olvasható export (JSON, CSV)Egy hónap
Tiltakozás21.Marketing és profilalkotás kikapcsolásaMarketingnél haladéktalanul
Automatizált döntés22.Emberi felülvizsgálat kérése, magyarázatEgy hónap

Az export lépései.

  1. Adattérkép készül arról, melyik tábla melyik oszlopa köti az adatot a felhasználóhoz.
  2. Az export egy security definer függvény vagy edge function, amely a hívó saját azonosítójára szűr, és csak a felhasználó saját adatait adja ki (más felhasználók személyes adatait egy közös szálban maszkolni kell).
  3. A kimenet JSON, egy ZIP-ben a storage fájlokkal, rövid lejáratú signed URL-lel.
  4. A kérés és a teljesítés naplózva van.

A törlés lépései.

  1. Táblánként eldöntjük, hogy törlés, anonimizálás vagy jogi kötelezettség miatti megőrzés következik (például számla 8 évig a számviteli törvény szerint).
  2. Az idegen kulcsoknál az on delete cascade vagy set null tudatos döntés.
  3. A storage objektumokat külön kell törölni. Az adatbázis kaszkád ezt nem végzi el, és a storage.objects sor törlése sem törli megbízhatóan a fájlt, a Storage API-t kell hívni.
  4. Harmadik felek (email szolgáltató, analitika, CRM) törlési API-ja.
  5. A mentésekből nem törlünk egyenként. Dokumentáljuk, hogy a mentés lejár (például 30 nap után), és visszaállításkor a törlési listát újra lefuttatjuk. Envelope encryption esetén a felhasználói kulcs törlése (crypto shredding) a mentésben lévő másolatot is olvashatatlanná teszi.
  6. Igazolás a kérelmezőnek.
create or replace function public.delete_my_account()
returns void
language plpgsql security definer set search_path = ''
as $$
declare v_uid uuid := (select auth.uid());
begin
  if v_uid is null then raise exception 'not authenticated'; end if;

  -- jogi kötelezettség miatt megőrzött adat anonimizálása
  update public.invoices set customer_name = 'törölt felhasználó', customer_email = null
  where user_id = v_uid;

  insert into public.deletion_queue(user_id, requested_at) values (v_uid, now());
  -- a storage és harmadik fél törlését egy edge function dolgozza fel a queue-ból,
  -- végül az auth.users sort az admin API törli, ez kaszkádol a public táblákra
end;
$$;

Adatmegőrzés automatizálása

  1. Minden személyes adatot tartalmazó táblának van megőrzési szabálya, a táblakommentben vagy egy retention_policies táblában.
  2. Egy pg_cron job naponta lefut, és törli vagy anonimizálja a lejárt sorokat.
  3. Nagy táblánál időalapú particionálás segít, a régi partíció egészben eldobható (detach, drop), ami olcsóbb a soronkénti törlésnél.
  4. A job eredményét (hány sor, melyik tábla) naplózzuk, mert ez az elszámoltathatóság bizonyítéka.
  5. A logok és az LLM interakciók is személyes adatok, ezekre is megőrzési szabály vonatkozik.
select cron.schedule(
  'retention-daily',
  '15 3 * * *',
  $$
    delete from public.audit_log where created_at < now() - interval '365 days';
    delete from public.llm_interactions where created_at < now() - interval '30 days';
    update public.leads set email = null, phone = null, anonymized_at = now()
      where last_contact_at < now() - interval '2 years' and anonymized_at is null;
  $$
);

LINDDUN, adatvédelmi fenyegetésmodellezés

A LINDDUN a KU Leuven kutatócsoportjának módszertana, a STRIDE adatvédelmi párja. Betűszava hét fenyegetéskategóriát jelöl.

KategóriaJelentésPélda SaaS-banTipikus ellenszer
LinkingAdatok összekapcsolása ugyanahhoz a személyhezAnalitika és CRM közös azonosítóvalCélonként eltérő pszeudonim azonosító
IdentifyingSzemély azonosítása az adatbólAnonimnak hitt export ritka kombinációkkalk-anonimitás, aggregálás
Non-repudiationA személy nem tudja letagadni (adatvédelmi kár)Whistleblower bejelentés aláírva, IP-velAnonim csatorna, az IP nem tárolása
DetectingMár a létezés kikövetkeztethetőElfelejtett jelszónál a válasz elárulja, van-e fiókEgységes válasz
Data disclosureFelesleges vagy túlzott adatközlésTeljes profil az API válaszbanAdattakarékos DTO, mezőszintű szűrés
Unawareness and unintervenabilityAz érintett nem tud róla, nem tud beavatkozniRejtett LLM feldolgozás, nincs exportÁtláthatóság, önkiszolgáló jogok
Non-complianceSzabályozási, szabályzati eltérésNincs megőrzési idő, nincs DPIANyilvántartás, folyamat

Lépések (a LINDDUN GO gyors kártyás és a LINDDUN PRO rendszerezett változatának közös magja).

  1. Rendszermodell. Adatfolyam diagram (DFD) készül külső entitásokkal, folyamatokkal, adattárakkal és adatfolyamokkal, jelölve rajta a személyes adatot és a bizalmi határokat.
  2. Fenyegetések azonosítása. A DFD minden elemén végigmegyünk a hét kategórián. A LINDDUN GO kártyái kérdés formában segítik ezt egy workshopon.
  3. Dokumentálás. Fenyegetés, érintett elem, forgatókönyv, súlyosság az érintett szemszögéből.
  4. Priorizálás. Valószínűség és hatás az érintettre.
  5. Ellenszer választása. Privacy enhancing technology (PET) vagy tervezési változtatás. A LINDDUN megoldási katalógusa stratégiákat ad (minimalizálás, elrejtés, elválasztás, absztrakció, tájékoztatás, kontroll).
  6. Ellenőrzés és ismétlés minden jelentős változásnál.

AI asszisztált használat. Egy kódgeneráló modell jól tud DFD-t készíteni a kódból (táblák, edge functionök, külső API hívások), és kategóriánként kérdéseket generál. A súlyosság értékelését és a kockázat elfogadását ember végzi.

Pszeudonimizálás, anonimizálás és rokon technikák

Fogalmak

TechnikaGDPR státuszVisszafordíthatóPélda
PszeudonimizálásTovábbra is személyes adat (4. cikk 5. pont)Igen, a kiegészítő információvalEmail helyett HMAC(kulcs, email)
AnonimizálásNem személyes adat, ha valóban nem azonosíthatóNemAggregált statisztika, megfelelő k mellett
TokenizációA pszeudonimizálás egy formájaIgen, a token vault általKártyaszám helyett token (PCI DSS)
TitkosításSzemélyes adat maradIgen, kulccsalAES-GCM mező
MaszkolásMegjelenítési kontrollA forrás megmaradA kártyaszám utolsó négy jegye

EU Bíróság, 2025. Az EU Bíróság 2025 szeptemberi, EDPS kontra SRB ügyben hozott ítélete szerint a pszeudonimizált adat annál a címzettnél, aki ésszerűen nem tudja újraazonosítani, nem feltétlenül személyes adat. Az adatkezelőnél viszont az marad. Fejlesztői szempontból ebből annyi következik, hogy a kiegészítő információt (kulcs, leképező tábla) szigorúan el kell választani, és dokumentálni kell, ki fér hozzá. A minősítés jogászi feladat.

Pszeudonimizálás helyesen

  1. Kulcsolt függvény kell (HMAC-SHA256 titkos kulccsal), nem egyszerű hash. Egy email cím SHA-256 hashe szótárral visszafejthető.
  2. Célonként eltérő kulcs, így az analitikai és a support azonosító nem kapcsolható össze (a Linking ellen).
  3. A kulcs a KMS-ben vagy a Vaultban van, az analitikai csapat nem fér hozzá.
  4. Ha visszafordíthatóság kell, tokenizáció leképező táblával, külön jogosultsággal.
import { createHmac } from "node:crypto";

export const pseudonym = (purposeKey: Buffer, email: string) =>
  createHmac("sha256", purposeKey).update(email.trim().toLowerCase()).digest("base64url");

k-anonimitás, l-diverzitás, t-közelség

Ellenőrzés SQL-lel.

-- legkisebb csoportméret a kvázi azonosítókon
select min(cnt) as k
from (
  select left(zip, 2) as zip2, (age / 10) * 10 as age_band, gender, count(*) as cnt
  from export_candidates
  group by 1, 2, 3
) g;

-- l-diverzitás: csoportonként hány eltérő diagnózis
select left(zip, 2), (age / 10) * 10, gender, count(distinct diagnosis) as l
from export_candidates group by 1, 2, 3
order by l asc limit 10;

Ha k kisebb a vállalt küszöbnél (gyakran 5 vagy 10), általánosítunk (rövidebb irányítószám, szélesebb korcsoport), vagy elhagyjuk a ritka csoportokat.

A differenciális adatvédelem alapjai

Cynthia Dwork és társai (2006) definíciója szerint egy mechanizmus akkor ε differenciálisan privát, ha bármely egy személy hozzáadása vagy elvétele az adathalmazhoz legfeljebb e^ε szorzóval változtatja bármely kimenet valószínűségét. A gyakorlatban zajt adunk az aggregátumhoz.

Laplace mechanizmus számlálásra. Egy count érzékenysége 1 (egy személy legfeljebb eggyel változtatja). A zaj Laplace eloszlású, a skálája 1/ε.

function laplace(scale: number) {
  const u = Math.random() - 0.5; // demóhoz; élesben kriptográfiai véletlen és auditált könyvtár
  return -scale * Math.sign(u) * Math.log(1 - 2 * Math.abs(u));
}
export const dpCount = (trueCount: number, epsilon: number) =>
  Math.max(0, Math.round(trueCount + laplace(1 / epsilon)));

Gyakorlati szabályok.

Maszkolás logban és LLM promptban

Logban.

  1. Strukturált logolás (JSON) és egy allowlist arról, mely mezők mehetnek ki. A denylist mindig lyukas.
  2. A redaction a logger szintjén történik (pino redact opció), nem hívásonként.
  3. Token, jelszó, Authorization fejléc, cookie és teljes request body nem kerül a logba.
  4. A felhasználó pszeudonim azonosítóval szerepel a logban, nem email címmel.
import pino from "pino";
export const log = pino({
  redact: {
    paths: ["req.headers.authorization", "req.headers.cookie", "*.password", "*.token", "*.email", "*.phone"],
    censor: "[REDACTED]",
  },
});

LLM promptban.

  1. A döntés az adatosztályozáson alapul. Korlátozott (restricted) adat nem mehet külső LLM-hez, a bizalmas csak szerződéses garanciával (DPA, zero data retention, EU adatfeldolgozás, ha elérhető).
  2. A prompt összeállítása előtt PII felismerés és helyettesítőkre csere történik (Microsoft Presidio, saját regex magyar mintákra, például adóazonosító, TAJ szám, magyar telefonszám formátum, IBAN).
  3. A helyettesítők egy kérésen belül konzisztensek (SZEMÉLY_1, SZEMÉLY_2), a válaszban a szerver cseréli vissza őket. A leképező tábla nem hagyja el a szervert.
  4. Csak a feladathoz szükséges mezők kerülnek át (adattakarékosság), a teljes rekord soha.
  5. Az LLM interakciók rövid megőrzéssel tárolódnak, és szerepelnek a nyilvántartásban.
  6. A modell kimenete alacsony integritású (Biba), ezért adatbázisba írás előtt sémavalidáción megy át.
const PATTERNS: [RegExp, string][] = [
  [/[\w.+-]+@[\w-]+\.[\w.]+/g, "EMAIL"],
  [/\b(?:\+36|06)[\s-]?\d{1,2}[\s-]?\d{3}[\s-]?\d{3,4}\b/g, "TELEFON"],
  [/\bHU\d{2}(?:\s?\d{4}){6}\b/g, "IBAN"],
  [/\b\d{3}[\s-]?\d{3}[\s-]?\d{3}\b/g, "TAJ"],
];

export function maskForLlm(text: string) {
  const map = new Map<string, string>();
  let i = 0;
  let out = text;
  for (const [re, label] of PATTERNS) {
    out = out.replace(re, (m) => {
      const key = `[${label}_${++i}]`;
      map.set(key, m);
      return key;
    });
  }
  return { masked: out, map }; // a map a szerveren marad
}

A regex alapú maszkolás csak az első réteg. Neveket és szabad szöveges azonosítókat egy NER modell jobban megtalál, a maradék kockázatot pedig a szerződéses kontroll fedi.

Adatosztályozás

Séma

A legtöbb projektben négy szint elég. Több szintet az emberek nem tudnak következetesen alkalmazni.

OsztályDefinícióPélda
NyilvánosKözzétételre szántMarketing szöveg, publikus árlista, blogcikk
BelsőNem titkos, de nem nyilvánosBelső dokumentáció, nem személyes üzleti mutató
BizalmasSzemélyes adat, üzleti titok, ügyféladatNév, email, ügyfélprojekt tartalma, szerződés
KorlátozottKülönleges adat, hitelesítési titok, pénzügyi azonosító, súlyos kárt okozó szivárgásEgészségügyi adat, jelszó hash, API kulcs, bankszámla, személyi igazolvány, TAJ

Osztályhoz rendelt kontrollok

KontrollNyilvánosBelsőBizalmasKorlátozott
HozzáférésBárkiBejelentkezett munkatársRLS, szükséges szerepRLS, név szerinti jog, aal2
Titkosítás tároláskorNem kellPlatform szintPlatform szint, mentések titkosítvaAlkalmazás szint (AEAD, envelope, KMS)
Titkosítás átvitelkorTLSTLSTLS 1.2 vagy újabbTLS 1.3, belső hálózaton is
NaplózásNem kellVáltozásÍrás és tömeges olvasásMinden hozzáférés
Logba kerülhetIgenIgenPszeudonim formábanSoha
Külső LLMIgenIgen, DPA mellettCsak DPA, zero retention, maszkolásNem
Fejlesztői környezet, seedIgenIgenSzintetikus vagy maszkoltCsak szintetikus
ExportSzabadBelsőJóváhagyással, naplózvaTilos vagy négy szem elv
MegőrzésKorlátlanSzabályzat szerintMeghatározott, automatikus törlésMinimális, automatikus törlés
Szivárgás eseténNincs teendőBelső vizsgálatIncidenskezelés, GDPR értékelésIncidenskezelés, valószínűleg bejelentés

Bevezetés

  1. A címkézés oszlopszinten, a migrációban történik.
  2. Egy CI szkript ellenőrzi, hogy minden új oszlop kapott címkét.
  3. A címkéből generálható a nyilvántartás adatkör oszlopa, a log redaction lista és az LLM maszkolási szabály.
comment on column public.customers.email is 'class=confidential;pii=true;retention=account+30d';
comment on column public.customers.tax_id is 'class=restricted;pii=true;encrypt=app';

-- címke nélküli oszlopok listája a CI-ban
select c.table_name, c.column_name
from information_schema.columns c
left join pg_catalog.pg_description d
  on d.objoid = (quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass
 and d.objsubid = c.ordinal_position
where c.table_schema = 'public' and (d.description is null or d.description not like 'class=%');

Supabase specifikus minták

Alapszabályok

  1. Minden public sémabeli táblán be van kapcsolva az RLS, már a létrehozó migrációban.
  2. A Supabase ma az új tábláknál alapból kéri az RLS-t, SQL migrációban viszont ezt külön meg kell írni.
  3. A policy mindig szerepre szűkített (to authenticated, to anon), nem a public pszeudo szerepre szól.
  4. A select, insert, update és delete külön policyt kap. A for all policy kerülendő, mert az insert és update with check feltételét könnyű kifelejteni.
  5. Az auth.uid() hívás select-be csomagolva szerepel, a policy oszlopai indexeltek.
  6. A service_role vagy secret kulcs csak szerveroldalon létezik.

RLS minták

Owner minta.

alter table public.notes enable row level security;

create policy "notes owner select" on public.notes
  for select to authenticated using ((select auth.uid()) = user_id);

create policy "notes owner insert" on public.notes
  for insert to authenticated with check ((select auth.uid()) = user_id);

create policy "notes owner update" on public.notes
  for update to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);

create policy "notes owner delete" on public.notes
  for delete to authenticated using ((select auth.uid()) = user_id);

create index on public.notes (user_id);

Tenant minta. A tagságot egy security definer segédfüggvény kérdezi le. Így a memberships táblán futó RLS nem rekurzál, és a lekérdezés egyszer fut.

create or replace function public.user_tenant_ids()
returns setof uuid
language sql stable security definer set search_path = ''
as $$
  select tenant_id from public.memberships where user_id = (select auth.uid());
$$;

revoke execute on function public.user_tenant_ids() from public, anon;
grant execute on function public.user_tenant_ids() to authenticated;

create policy "projects tenant select" on public.projects
  for select to authenticated
  using (tenant_id in (select public.user_tenant_ids()));

create policy "projects tenant insert" on public.projects
  for insert to authenticated
  with check (tenant_id in (select public.user_tenant_ids()));

Role minta. Bérlőn belüli szerep alapján működik. A szerepet tartalmazó tábla update joga csak az owneré, vagy csak RPC-n keresztül érhető el.

create policy "projects admin delete" on public.projects
  for delete to authenticated
  using (public.has_permission(tenant_id, 'project.delete'));

Shared minta. Egyedi megosztás egy felhasználóval, a tulajdonos mellett.

create table public.document_shares (
  document_id uuid references public.documents(id) on delete cascade,
  user_id uuid references auth.users(id) on delete cascade,
  permission text check (permission in ('view','edit')),
  primary key (document_id, user_id)
);

create or replace function public.can_view_document(p_doc uuid)
returns boolean language sql stable security definer set search_path = ''
as $$
  select exists (select 1 from public.documents d where d.id = p_doc and d.owner_id = (select auth.uid()))
      or exists (select 1 from public.document_shares s where s.document_id = p_doc and s.user_id = (select auth.uid()));
$$;

create policy "documents view" on public.documents
  for select to authenticated using (public.can_view_document(id));

Ha a can_view_document nagy táblán soronként fut, érdemes halmaz alapúra cserélni (id in (select ...)), mert a soronkénti függvényhívás lassú.

Security definer csapdák

CsapdaMi a bajJavítás
Hiányzó set search_pathEgy támadó által létrehozott azonos nevű objektum (például függvény a public sémában) a tulajdonos jogaival fut leset search_path = '' és minden objektum sémával minősítve
A függvény a public sémában van, és bárki hívhatjaA PostgREST RPC-ként kiteszi, az anon is hívhatjarevoke execute from public, anon, explicit grant, vagy privát séma
Paraméterként kapott user_idA hívó bárki nevében kérdezhetA függvényen belül mindig auth.uid()
Security definer viewA view a létrehozó jogaival fut, megkerüli az RLS-tPostgres 15 óta with (security_invoker = true)
Dinamikus SQL format nélkülSQL injection tulajdonosi jogokkalformat() %I és %L, vagy paraméterezett execute using
Túl széles visszatérésA függvény teljes sort ad vissza, benne érzékeny oszloppalCsak a szükséges oszlopok
create view public.project_overview
with (security_invoker = true)
as select id, tenant_id, name, status from public.projects;

A Supabase security advisor (a Dashboardon, a CLI-ból vagy MCP szerveren keresztül) jelzi a security definer view-kat, a kikapcsolt RLS-t, a módosítható search_path-ot és a kitett auth.users hivatkozást. Minden migráció után érdemes lefuttatni.

service_role és secret kulcs

// supabase/functions/x/index.ts
import { createClient } from "jsr:@supabase/supabase-js@2";

Deno.serve(async (req) => {
  const supabase = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_ANON_KEY")!, {
    global: { headers: { Authorization: req.headers.get("Authorization")! } },
  });
  const { data: { user }, error } = await supabase.auth.getUser();
  if (error || !user) return new Response("unauthorized", { status: 401 });
  // innen minden lekérdezés RLS alatt fut a felhasználó nevében
  return new Response("ok");
});

Storage policy

A Storage a storage.objects táblán futó RLS-sel véd. Bevett minta, hogy a fájl útvonalának első szegmense a bérlő vagy a felhasználó azonosítója.

create policy "tenant files read" on storage.objects
  for select to authenticated
  using (
    bucket_id = 'tenant-files'
    and (storage.foldername(name))[1]::uuid in (select public.user_tenant_ids())
  );

create policy "tenant files upload" on storage.objects
  for insert to authenticated
  with check (
    bucket_id = 'tenant-files'
    and (storage.foldername(name))[1]::uuid in (select public.user_tenant_ids())
  );

Csapdák. A public bucket policytól függetlenül, URL-lel mindenki számára olvasható, ezért személyes adat sosem kerül public bucketbe. Minden signed URL capability, rövid lejárattal adjuk ki. A bucketen be kell állítani a megengedett MIME típusokat és a maximális fájlméretet. Feltöltött fájlt nem szolgálunk ki ugyanarról az originről, ahol a session cookie él (HTML vagy SVG fájlon keresztüli XSS veszélye miatt).

auth.uid() és auth.jwt()

RLS teljesítmény

TechnikaMiért gyorsít
(select auth.uid()) a nyers auth.uid() helyettA planner initPlanként egyszer értékeli ki, nem soronként
Index a policy oszlopain (user_id, tenant_id)A szűrés indexet használ
Szerep megadása (to authenticated)Az anon kérésekre a policy ki sem értékelődik
Join helyett in (select ...) halmazA soronkénti allekérdezés helyett egyszeri halmaz
Security definer segédfüggvény a tagsághozElkerüli a memberships táblán futó RLS rekurzióját
A kliens is szűr (.eq('tenant_id', x))Explicit feltétellel a planner jobb tervet készít

A mérés az explain analyze futtatásával, a felhasználó szerepében történik.

begin;
set local role authenticated;
set local request.jwt.claims = '{"sub":"11111111-1111-1111-1111-111111111111","role":"authenticated"}';
explain analyze select * from public.projects;
rollback;

Tesztelés két felhasználóval

Minden RLS policy tesztje legalább két felhasználóval, két bérlőben fut. Az első felhasználó látja a sajátját, a másodikét viszont nem látja és nem módosíthatja. Anonim hívó semmit sem lát.

pgTAP az adatbázisban (supabase test db).

begin;
select plan(4);

-- tesztadat két bérlővel
insert into auth.users (id, email) values
  ('aaaaaaaa-0000-0000-0000-000000000001', '[email protected]'),
  ('bbbbbbbb-0000-0000-0000-000000000002', '[email protected]');
insert into public.tenants (id, name) values
  ('10000000-0000-0000-0000-000000000001', 'A kft'),
  ('20000000-0000-0000-0000-000000000002', 'B kft');
insert into public.memberships values
  ('10000000-0000-0000-0000-000000000001', 'aaaaaaaa-0000-0000-0000-000000000001', 'owner'),
  ('20000000-0000-0000-0000-000000000002', 'bbbbbbbb-0000-0000-0000-000000000002', 'owner');
insert into public.projects (tenant_id, name) values
  ('10000000-0000-0000-0000-000000000001', 'A projekt'),
  ('20000000-0000-0000-0000-000000000002', 'B projekt');

set local role authenticated;
set local request.jwt.claims = '{"sub":"aaaaaaaa-0000-0000-0000-000000000001","role":"authenticated"}';

select is((select count(*) from public.projects)::int, 1, 'A csak a saját projektjét látja');
select is((select name from public.projects limit 1), 'A projekt', 'A a saját projektjét látja');
select throws_ok(
  $$ insert into public.projects (tenant_id, name) values ('20000000-0000-0000-0000-000000000002', 'betörés') $$,
  '42501', null, 'A nem írhat B bérlőjébe'
);

set local role anon;
set local request.jwt.claims = '{"role":"anon"}';
select is((select count(*) from public.projects)::int, 0, 'anon semmit sem lát');

select * from finish();
rollback;

Integrációs teszt a kliens felől (Vitest). Két valódi felhasználó a helyi Supabase példányon, a publishable kulccsal. Ez a teszt a teljes láncot ellenőrzi (Auth, PostgREST, RLS, Storage).

import { createClient } from "@supabase/supabase-js";
import { expect, test } from "vitest";

const url = process.env.SUPABASE_URL!;
const key = process.env.SUPABASE_PUBLISHABLE_KEY!;

async function signedIn(email: string, password: string) {
  const c = createClient(url, key, { auth: { persistSession: false } });
  const { error } = await c.auth.signInWithPassword({ email, password });
  if (error) throw error;
  return c;
}

test("B nem olvashatja A számláját id alapján (BOLA)", async () => {
  const a = await signedIn("[email protected]", process.env.TEST_PW_A!);
  const b = await signedIn("[email protected]", process.env.TEST_PW_B!);
  const { data: inv } = await a.from("invoices").insert({ amount: 100 }).select("id").single();
  const { data } = await b.from("invoices").select("*").eq("id", inv!.id);
  expect(data).toEqual([]);
  const { data: upd } = await b.from("invoices").update({ amount: 0 }).eq("id", inv!.id).select();
  expect(upd).toEqual([]);
});

Az RLS által blokkolt update nem dob hibát, hanem nulla sort érint. Ezért a teszt a visszaadott sorokat vizsgálja, nem a hibát.

Szabályozás fejlesztői szemmel

Az alábbiak technikai kontrollokra fordítják a szabályozást. A hatály és a kötelezettségek megállapítása jogi kérdés, arról szakértő dönt. Az irányítási rendszerek és a megfelelés szervezeti oldalát az Irányítási rendszerek és governance és az ISO megfelelés és audit kézikönyv tárgyalja.

NIS2 (EU 2022/2555) és a magyar átültetés

Magyarországon a 2024. évi LXIX. törvény (kiberbiztonsági törvény) ülteti át. A felügyeletet a Szabályozott Tevékenységek Felügyeleti Hatósága (SZTFH) látja el, az incidensbejelentés a Nemzeti Kibervédelmi Intézet felé történik. Az érintett szervezeteknek regisztrálniuk kell, kiberbiztonsági auditon kell átesniük, és a vezetés személyesen felel.

NIS2 21. cikk követelményFejlesztői kontroll
Kockázatelemzés, szabályzatokFenyegetésmodell, access matrix, adatosztályozás
IncidenskezelésRiasztás, runbook, naplók megőrzése, a 24 órás, 72 órás és egy hónapos bejelentési ütem támogatása
Üzletmenet folytonosság, mentésPITR, tesztelt visszaállítás, RTO és RPO
Ellátási lánc biztonságaSBOM, függőség scan, beszállítói értékelés (Supabase, Vercel, LLM szolgáltató)
Biztonság a beszerzésben és a fejlesztésbenSecure SDLC, code review, SAST, secret scan
Hatékonyság méréseAdvisor riport, sérülékenység lezárási idő
Kiberhigiénia, képzésFejlesztői biztonsági ellenőrzőlista
KriptográfiaKriptográfiai szabályzat (a fenti javasolt és kerülendő algoritmusok táblázata alapján), kulcsrotáció
HR biztonság, hozzáférés szabályozás, eszközkezelésJogosultság kiosztási és visszavonási folyamat belépéskor és kilépéskor
MFA, biztonságos kommunikációKötelező MFA admin és fejlesztői fiókokra

Ezeket akkor is érdemes alkalmazni, ha az ügyfél maga nem tartozik a hatály alá, mert a hatály alá tartozó megrendelők a beszállítóiktól is elvárják (ellátási lánc).

EU AI Act (EU 2024/1689)

Ütemezés. A rendelet 2024. augusztus 1-jén lépett hatályba. A tiltott gyakorlatok és az AI jártassági kötelezettség (4. cikk) 2025. február 2-től, az általános célú modellekre vonatkozó szabályok 2025. augusztus 2-től alkalmazandók. A magas kockázatú rendszerek szabályai eredetileg 2026. augusztus 2-től lennének alkalmazandók. A Bizottság 2025 novemberi digitális omnibusz javaslata ezt a szabványok és útmutatók elkészültéhez kötné, legkésőbb 2027 decemberéig, a termékekbe épített rendszereknél 2028 augusztusáig. A 2026 októberi állapot szerint ez ellenőrizendő, az aktuális helyzetet projektkezdéskor meg kell nézni.

Fejlesztői kontrollok szerepkör szerint.

A GDPR az AI rendszerre is teljes egészében vonatkozik. A promptban, a RAG indexben, a finomhangoló adatban és a naplóban lévő személyes adat is személyes adat. Az AI irányítási szabványokat (ISO/IEC 42001 és társai) az AI ISO szabványok kézikönyv ismerteti.

DORA (EU 2022/2554), pénzügyi szektor

  1. január 17-től alkalmazandó bankokra, biztosítókra, befektetési vállalkozásokra, kriptoszolgáltatókra és a kritikus IKT szolgáltatóikra.
DORA pillérFejlesztői következmény
IKT kockázatkezelésEszközleltár, adatosztályozás, hozzáférés szabályozás, titkosítás, változáskezelés
IncidensjelentésAz osztályozási szempontok szerinti naplózás, a gyors (órás) első bejelentés támogatása
Digitális működési ellenálló képesség teszteléseSérülékenység vizsgálat, nagyobb intézménynél fenyegetésalapú penetrációs teszt (TLPT)
Harmadik fél kockázatSzerződéses jogok (audit, kilépési stratégia), beszállítói nyilvántartás. Ha a megoldás Supabase-re vagy LLM szolgáltatóra épül, ezeket fel kell tüntetni
InformációmegosztásFenyegetési információk megosztása

Pénzügyi szolgáltató ügyfélnél a projekt kezdetén tisztázni kell, alkalmazza-e a DORA-t, mert ez a beszállítóválasztást (EU régió, kilépési terv, auditjog) is befolyásolja.

Magyar vonatkozás, NAIH

A gyakorlatban

Állandó szabályok

  1. RLS az első migrációban. Új táblánál a create table, az enable row level security, a négy külön policy és az index egy migrációban születik. Policy nélküli tábla nem kerül élesbe.
  2. Előbb access matrix, utána policy. Ha a projektben még nincs verziózott hozzáférési mátrix, előbb az készül el, a policyk abból következnek.
  3. Kliens a felhasználó nevében. Edge functionben és szerveroldali kódban a hívó JWT-jével jön létre a kliens. A secret vagy service_role kulcs használatát a kódban komment indokolja, és a lekérdezés szűkített.
  4. Titok soha nincs a kliensben, a repóban, a logban vagy a promptban. Ha titok kerül elő a kódban, egy jegyben vagy egy beszélgetésben, a csapat jelzi és rotálja, nem másolja tovább. AI asszisztens promptjába sem kerül titok.
  5. Security definer csak search_path = '' beállítással, revoke execute from public, anon sorral és auth.uid() alapú belső szűréssel. View-nál security_invoker = true.
  6. Jogosultság nem a user_metadata-ból. Szerep és bérlő a memberships táblából vagy az app_metadata-ból jön.
  7. Bevált kriptográfia. libsodium, Web Crypto vagy Node crypto, AEAD, XChaCha20-nál véletlen nonce, kulcsverzió mező. Saját algoritmus, ECB mód és Math.random alapú token nem kerül a kódba, akkor sem, ha egy kódgeneráló modell ilyet javasol.
  8. A jelszót és a hitelesítést a platformra bízzuk. Supabase Auth, PKCE, leaked password protection, MFA admin szerepre. Ha saját hitelesítés kell, argon2id az OWASP paraméterekkel és a NIST 800-63B-4 szabályai szerint (15 karakter MFA nélkül, nincs összetételi szabály, nincs kötelező csere, van tiltólista).
  9. Személyes adat előtt adatosztály. Új személyes adat mezőnél a fejlesztő eldönti vagy egyezteti az osztályt, a megőrzési időt és a nyilvántartás jogalap sorát, és mindezt oszlopkommentbe írja.
  10. LLM hívás előtt maszkolás. Bizalmas adat csak maszkolva, korlátozott adat egyáltalán nem megy külső modellhez. A modell kimenete írás előtt sémavalidáción megy át.
  11. Kétfelhasználós teszt nélkül nincs kész. Minden RLS változáshoz pgTAP vagy integrációs teszt tartozik, amely A, B és anon szereppel ellenőriz.
  12. Advisor minden migráció után. A Supabase security és performance advisor minden migráció után lefut, a figyelmeztetések a pull request leírásába kerülnek.
  13. Szabályozásról tényt, nem jogi véleményt. A fejlesztőcsapat a kontrollokat nevezi meg, és jelzi, ha egy döntés jogászt, DPO-t vagy a felelős vezetőt igényli.
  14. Éles adatbázison óvatosan. Éles adaton DDL vagy tömeges módosítás csak kifejezett, dokumentált jóváhagyással fut, előtte branchen vagy helyi példányon kipróbálva. AI asszisztált munkánál ez a szabály az asszisztens eszközjogosultságaira is vonatkozik.

Ellenőrzőlista, új tábla vagy funkció

Ellenőrzőlista, hitelesítés és session

Ellenőrzőlista, kriptográfia és titkok

Ellenőrzőlista, adatvédelem és szabályozás

Kódáttekintési kérdések AI által generált kódhoz

KérdésMit keresünk
Hol dől el, hogy ez az objektum a hívóé?Ha a válasz a kliens, az BOLA
Van-e út, amely megkerüli az RLS-t?service_role, security definer view, public számára engedélyezett RPC
Mi történik, ha a mező hiányzik vagy null?Fail safe default, null értékű auth.uid()
Mit lát a logban egy támadó vagy egy support munkatárs?Token, email, teljes body
Mi kerül a promptba?Teljes rekord, korlátozott adat, a maszkolás hiánya
Mit tesz a rendszer megbízhatatlan inputtal?Webhook aláírás, LLM kimenet validálás, Biba sértés
Hogyan vonható vissza?Token, kulcs, megosztás, szerep visszavonása és annak azonnali hatása
Hol a teszt, amely bizonyítja?Kétfelhasználós teszt, assurance

Az AI asszisztált fejlesztés munkarendjét, az ügynökök jogosultságait és a generált kód átvételét az AI ügynökök és vibecoding kézikönyv részletezi, a módszerek történeti hátterét a Papírkorszak módszerei és a Szoftverarchitektúra kézikönyv adja.

Források

Állapot 2026 októberében. A szöveg szakmai összefoglaló, nem jogi tanácsadás, a szabványok és jogszabályok aktuális szövegét a kiadótól érdemes ellenőrizni.