Mérnöki kézikönyv · 05 / 09 · Biztonságos fejlesztés

Kiberbiztonság és alkalmazásbiztonság mérnököknek

Fejlesztőknek, CTO-knak és IT vezetőknek szóló kézikönyv a biztonságos szoftverfejlesztés módszertanáról, a hatvanas évek időosztásos rendszereitől a 2026-os szabványokig. Minden módszernél megmutatja, mikor éri meg és hol szokott elcsúszni, TypeScript, Next.js, Postgres és Supabase kódpéldákon a sebezhető és a javított változattal együtt.

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

Tartalom

Minden módszernél leírjuk, hogyan működik a gyakorlatban, mikor éri meg, hol szokott elcsúszni, és hogyan épül be egy fejlesztőcsapat napi munkájába, akár AI asszisztált fejlesztésnél is. Kódpéldáink TypeScript, Node, Next.js, Postgres és Supabase környezetre készültek. A sebezhető változat mindig csak annyit mutat, amennyi a hiba megértéséhez kell, támadási recept nincs benne.

Az LLM és ágens specifikus fenyegetésekkel (prompt injekció, eszközvisszaélés, túlzott önállóság, memóriamérgezés, OWASP LLM és Agentic Top 10) az AI ügynökök és vibecoding kézikönyv foglalkozik részletesen. Ebben a kézikönyvben csak akkor kerülnek elő, amikor egy klasszikus védelem (bizalmi határ, legkisebb jogosultság, kimenet kódolás) rájuk is érvényes.

Gyorstérkép

HelyzetKiindulópont
Új funkció tervezéseFenyegetésmodellezés (négy kérdés, STRIDE, DFD)
Kód írása vagy reviewOWASP Top 10 2025, webes alapvédelmek, ASVS L1 vagy L2
Új függőség, CI módosításEllátási lánc szakasz
Éles üzem, naplókNaplózás és detekció
Valami baj történtIncidenskezelés és a kis csapatos runbookok
Szervezeti kérdés, ügyfél auditNIST CSF 2.0, CIS Controls IG1
AI asszisztens által generált kódA gyakorlati szabályok a kézikönyv végén

1. Klasszikus gyökerek és ami belőlük ma is érvényes

A biztonsági mérnökség legtöbb mai elve egy-egy konkrét kudarcból született. A történeti áttekintés minden tételénél ott áll, mit visz tovább belőle a mai fejlesztő.

CTSS és Multics (1961 és 1969 között)

A CTSS (Compatible Time-Sharing System, MIT, Fernando Corbató csapata) az egyik első időosztásos rendszer volt, ahol több felhasználó osztozott ugyanazon a gépen. Ezzel megjelent a felhasználók egymástól való elválasztásának igénye és a jelszó. Egy 1966 körüli esetben a szövegszerkesztő ideiglenes fájljai összekeveredtek, és a rendszer a napi üdvözlő üzenet helyett a teljes jelszófájlt írta ki minden bejelentkezőnek. A hiba oka egy ártalmatlannak tűnő közös munkakönyvtár volt.

A Multics (MIT, Bell Labs, General Electric) már tervezési célként kezelte a biztonságot. Jerome Saltzer és társai itt dolgozták ki a hozzáférés vezérlési listákat, Robert Graham pedig a gyűrű (ring) architektúrát, ahol a kód jogosultsági szintekbe rendeződik, és a belső gyűrűt csak meghatározott kapukon keresztül lehet hívni. A mai CPU privilégiumszintek (kernel és user mód) és a hipervizorok elválasztása ennek a leszármazottja.

Tanulság ma.

Ware jelentés (1970) és Anderson jelentés (1972)

Willis Ware a RAND-nál, a Defense Science Board megbízásából írta meg a Security Controls for Computer Systems jelentést. Ez mondta ki először rendszerszinten, hogy egy többfelhasználós gép biztonsága hardver, szoftver, kommunikáció, fizikai és szervezeti kérdés egyszerre, és hogy a biztonságot a tervezés elején kell beépíteni. James Anderson 1972 jelentése bevezette a referencia monitor fogalmát, egy mindig meghívott, megkerülhetetlen, kicsi és ezért ellenőrizhető komponenst, amely minden hozzáférési döntést meghoz.

Tanulság ma. A jogosultságellenőrzést egyetlen, jól tesztelt helyre tesszük, és minden út azon megy át. Postgresben ez a Row Level Security (RLS) policy, alkalmazásszinten egy authorize() függvény, amit minden adatelérés hív. Ha a jogosultsági logika húsz route handlerben szétszórva él, a huszonegyedikből kimarad.

Tiger team tesztek és a Multics Security Evaluation (1974)

A hetvenes években az amerikai légierő úgynevezett tiger teameket állított fel, amelyek engedéllyel próbálták feltörni a rendszereket. Paul Karger és Roger Schell 1974 jelentése a Multicsról azt mutatta ki, hogy még a kor legbiztonságosabbnak tervezett rendszere is több ponton áttörhető, és felvetette, hogy egy fordítóprogramba rejtett csapóajtó a forráskódban láthatatlan maradhat. A jelentés tanulsága szerint a hibakereső tesztelés soha nem bizonyítja a hiba hiányát, csak a jelenlétét.

Tanulság ma. A pentest mintavétel, nem bizonyíték. Pentest mellé kell a tervezési szintű védelem (fenyegetésmodell, mélységi védelem), különben a jelentés lezárása után a következő commit újra kinyitja a rést.

Saltzer és Schroeder nyolc elve (1975)

A The Protection of Information in Computer Systems cikk nyolc tervezési elve ma is a biztonsági review alapja.

ElvJelentésMai megfelelő
Economy of mechanismKicsi, egyszerű védelmi mechanizmusEgy auth könyvtár, nem saját kripto
Fail-safe defaultsAlapból tilt, kifejezett engedély kellRLS bekapcsolva, policy nélkül semmi sem látszik
Complete mediationMinden hozzáférés ellenőrzöttServer action elején auth, nem csak middleware-ben
Open designA biztonság ne a titkos tervtől függjönKerckhoffs elv, nyilvános algoritmus, titkos kulcs
Separation of privilegeTöbb feltétel együttMFA, kétszemélyes jóváhagyás élesítéshez
Least privilegeCsak a szükséges jogSzűk scope-ú tokenek, read only DB user
Least common mechanismKevés közös komponens a felhasználók közöttBérlőnkénti izoláció
Psychological acceptabilityKényelmes legyen, különben megkerülikPasskey, SSO, értelmes hibaüzenet

Thompson, Reflections on Trusting Trust (1984)

Ken Thompson Turing díjas előadása megmutatta, hogy egy fordítóprogramba olyan logika rejthető, amely a bejelentkező programba csapóajtót fordít, és saját magát is újratermeli a fordító újrafordításakor, így a forráskódból eltűnik. A következtetés az, hogy csak annak a kódnak bízhatunk meg, amit teljes egészében magunk állítottunk elő, ami gyakorlatilag lehetetlen.

Tanulság ma. Ez az ellátási lánc biztonság alapproblémája. A válaszok a reprodukálható build, a több független fél általi építés (diverse double compiling, David A. Wheeler), a build eredetigazolás (SLSA provenance) és az aláírt artefaktumok. Az xz utils eset 2024-ben ugyanezt a mintát mutatta élesben.

Morris worm (1988) és a CERT születése

Robert Tappan Morris 1988 novemberében egy önterjesztő programot indított, amely néhány óra alatt az akkori internetre kötött gépek jelentős részét megbénította. A féreg négy gyengeséget használt ki. A fingerd démonban egy határellenőrzés nélküli gets() hívást, a sendmail bekapcsolva hagyott DEBUG módját, a rsh és rexec gépek közötti bizalmi kapcsolatait és a gyenge jelszavakat. Egy hibás újrafertőzési logika miatt ugyanazt a gépet sokszor megfertőzte, ez okozta a túlterhelést.

A DARPA ezután hozta létre a Carnegie Mellon Software Engineering Institute keretében a CERT Coordination Centert, az első szervezett incidenskezelő és sérülékenység koordináló csapatot.

Tanulság ma.

Cliff Stoll, The Cuckoo's Egg (1989)

Clifford Stoll csillagász a Lawrence Berkeley Laborban egy 75 centes könyvelési eltérést vizsgált, és ebből egy hónapokig tartó nyomozás lett, amely egy katonai rendszerekbe behatoló, a KGB-nek dolgozó nyugatnémet csoporthoz vezetett. Ez az első részletesen dokumentált incidenskezelési esettanulmány.

Módszerek, amelyek innen származnak.

  1. Apró anomália komolyan vétele. A könyvelési eltérés egy integritási jelzés volt. Ma ez a nem egyező számla, a váratlan admin bejelentkezés, a hirtelen megugró kimenő forgalom.
  2. Független, manipulálhatatlan napló. Stoll nyomtatókat kötött a bejövő vonalakra, így a betolakodó nem tudta törölni a nyomait. Ma ez a különálló, írás után nem módosítható (WORM) naplótár.
  3. Megfigyelés a lekapcsolás előtt. A támadót hagyta dolgozni, hogy megértse a célját. Ma ez a containment döntés, amit tudatosan, a kockázatot mérlegelve hozunk meg.
  4. Csali. Hamis, vonzó dokumentumokat helyezett el, hogy a támadót bent tartsa. Ma ez a honeytoken és a canary token.
  5. Napló és jegyzet minden lépésről. Stoll naplója tette lehetővé a bírósági eljárást. Ma ez az incidens idővonal.

Aleph One, Smashing the Stack for Fun and Profit (1996)

A Phrack magazinban megjelent cikk közérthetően leírta a veremalapú puffer túlcsordulást, ahol egy határellenőrzés nélkül másolt bemenet felülírja a függvény visszatérési címét. A cikk után a memóriabiztonsági hibák évtizedekig a legsúlyosabb sérülékenység osztály maradtak.

Tanulság ma. TypeScript és Node alatt ritkán írunk ilyen kódot, de a natív függőségek (képfeldolgozás, tömörítés, parserek) igen. A válaszok a memóriabiztos nyelvek (Rust, Go), a fuzzing a parsereken, és az, hogy a bizonytalan bemenetet feldolgozó natív könyvtárat sandboxban vagy külön folyamatban futtatjuk. A CISA és a Five Eyes partnerek 2023 óta kifejezetten ajánlják a memóriabiztos nyelvekre átállás ütemtervét.

Schneier, Attack Trees (1999)

Bruce Schneier a Dr. Dobb's Journalban írta le a támadási fát. A gyökér a támadó célja, a gyerekcsomópontok a hozzá vezető alternatív (VAGY) vagy együttesen szükséges (ÉS) lépések. A levelekhez költséget, szükséges tudást, valószínűséget rendelünk, és így látszik, melyik út a legolcsóbb a támadónak. A fa a fenyegetésmodellezésben lépésenként kidolgozva tér vissza.

Howard és LeBlanc, Writing Secure Code, és a Microsoft SDL (2002 és 2004 között)

2002 januárjában Bill Gates Trustworthy Computing memója leállíttatta a Windows fejlesztését, hogy a mérnökök biztonsági képzést és kódátvizsgálást végezzenek. Michael Howard és David LeBlanc Writing Secure Code könyve (2002, második kiadás 2003) lett a kötelező olvasmány. Ebből nőtt ki 2004-re a Security Development Lifecycle (SDL), amely minden termékre kötelezővé tette a képzést, a fenyegetésmodellezést, a tiltott függvények listáját, a statikus elemzést, a fuzzingot és a kiadás előtti végső biztonsági review-t.

Az SDL lépései mai formában.

SDL fázisKis csapatban ma
KépzésEz a kézikönyv, plusz az OWASP Cheat Sheet lapok
KövetelményekASVS L1 vagy L2 kiválasztott tételei a ticketben
Tervezés5 perces fenyegetésmodell minden funkcióra
ImplementációTiltott minták listája (a kézikönyv végén), linter, semgrep
VerifikációSAST, SCA, secret scan CI-ban, DAST stagingen
KiadásDeploy előtti kapu, incidens runbook kész
VálaszSérülékenység bejelentési csatorna, patch folyamat

A STRIDE születése (1999)

Loren Kohnfelder és Praerit Garg a Microsoftnál 1999-ben írta le a STRIDE betűszót egy belső dokumentumban, hogy a fejlesztők egy emlékezhető ellenőrzőlistával gondolhassák végig, mi romolhat el. A betűk a fenyegetéstípusok kezdőbetűi (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege). A STRIDE azóta a leggyakrabban használt fenyegetésosztályozás, mert minden betűhöz egy jól definiált védelmi tulajdonság tartozik.


2. Fenyegetésmodellezés lépésről lépésre

A fenyegetésmodellezés a tervezés közbeni strukturált gondolkodás arról, mi romolhat el. Adam Shostack Threat Modeling: Designing for Security (2014) könyve tette mérnöki gyakorlattá, a Threats: What Every Engineer Should Learn from Star Wars (2023) pedig fenyegetésosztályonként tanítja, hogyan néz ki egy-egy hiba és mi a válasz rá. A 2020-ban közösen írt Threat Modeling Manifesto összefoglalja az elveket.

Shostack négy kérdése

  1. Min dolgozunk? Lerajzoljuk a rendszert (DFD), a szereplőket, az adatokat és a bizalmi határokat.
  2. Mi romolhat el? STRIDE elemenként vagy támadási fával végigmegyünk a diagramon.
  3. Mit teszünk ellene? Minden fenyegetésre egy döntés születik. Mérsékeljük, elfogadjuk, áthárítjuk (például Stripe kezeli a kártyaadatot), vagy kiküszöböljük a funkció megváltoztatásával.
  4. Jól csináltuk? A modell és a kód egyezik-e, minden fenyegetéshez van-e ticket, teszt, döntés.

A négy kérdés azért működik, mert nem követel meg eszközt vagy szakértőt. Egy whiteboard és harminc perc elég egy közepes funkcióhoz.

A Threat Modeling Manifesto elvei röviden

A manifesztum értékeket és elveket fogalmaz meg. Az emberek és a közös munka többet ér a folyamatnál és az eszköznél, a folyamatos finomítás pedig az egyszer leadott dokumentumnál. Fontosabb megtalálni a megoldandó problémákat, mint szépen formázni a jelentést. Akkor működik a legjobban a modellezés, ha korán és gyakran csináljuk, ha kockázatkezelési döntésekhez kötődik, és ha a csapat saját nyelvén szól.

Tipikus hibák, amelyeket a manifesztum is megnevez.

Adatfolyam diagram (DFD) bizalmi határokkal

A DFD négy elemből áll.

ElemJelPélda
Külső entitásTéglalapFelhasználó, Stripe, LLM szolgáltató
FolyamatKör vagy lekerekített téglalapNext.js route handler, edge function
AdattárKét párhuzamos vonalPostgres, Supabase Storage, Redis
AdatfolyamNyílHTTPS kérés, webhook, SQL lekérdezés

A bizalmi határ szaggatott vonal ott, ahol a jogosultsági szint vagy a tulajdonos megváltozik. Minden nyíl, amely bizalmi határt keresztez, fenyegetésjelölt. A legtöbb valós hiba ezeken a nyilakon keletkezik, mert az egyik oldal feltételez valamit, amit a másik nem garantál.

A DFD rajzolás lépései.

  1. Felrajzoljuk a külső entitásokat (böngésző, harmadik felek).
  2. Felvesszük a folyamatokat abban a granularitásban, ahol eltérő jogosultsággal futnak.
  3. Hozzáadjuk az adattárakat, és rájuk írjuk, milyen érzékenységű adat van bennük.
  4. Nyilakkal összekötjük az elemeket, a nyilakon jelöljük a protokollt és a hitelesítés módját.
  5. Behúzzuk a bizalmi határokat.
  6. Megszámozzuk a határt keresztező nyilakat. Ezek mennek a STRIDE táblába.

STRIDE elemenként

FenyegetésSérülő tulajdonságKérdés a diagramhozTipikus védelem
Spoofing (megszemélyesítés)Hitelesség (authenticity)Ki lehet valaki más nevében?Erős auth, MFA, passkey, webhook aláírás, mTLS
Tampering (manipuláció)IntegritásMit lehet útközben vagy tárolva módosítani?TLS, aláírás, HMAC, RLS, szerveroldali validáció
Repudiation (letagadás)LetagadhatatlanságKi tagadhatja le, hogy megtette?Audit napló, aláírt események, időbélyeg
Information disclosureBizalmasságMi szivároghat ki és kinek?Titkosítás, hozzáférés szabályozás, hibaüzenet szűkítés
Denial of serviceRendelkezésre állásMi teríthető le olcsón?Rate limit, kvóta, timeout, költségkorlát
Elevation of privilegeJogosultsági izolációKi szerezhet több jogot?Legkisebb jogosultság, szerveroldali authz, sandbox

A Star Wars könyv a hat klasszikus kategória mellé hármat tesz, amelyeket érdemes a listán tartani.

Támadási fa (attack tree) készítése

  1. A gyökérbe a támadó konkrét célja kerül, például más bérlő számláinak letöltése.
  2. Ezt alternatív utakra bontjuk (VAGY). Közvetlen DB hozzáférés, API authz hiba, ellopott session, belső munkatárs.
  3. Ahol egy út több feltételt igényel, azokat ÉS kapcsolattal kötjük. Például ellopott session ÉS hiányzó újrahitelesítés az exportnál.
  4. A levelekhez költséget és valószínűséget írunk (alacsony, közepes, magas).
  5. Megkeressük a legolcsóbb utat, és ott kezdjük a védekezést.
  6. A fát verziókezelt szövegként tároljuk (Mermaid vagy egyszerű behúzott lista), hogy a PR-ban együtt változzon a kóddal.
CÉL: Más bérlő számláinak letöltése
├── VAGY: API objektumszintű authz hiba (BOLA)        [olcsó, valószínű]
│   └── /api/invoices/:id nem ellenőrzi a tenant_id-t
├── VAGY: RLS kikapcsolva egy új táblán               [olcsó, közepes]
├── VAGY: service_role kulcs a kliens bundle-ben       [olcsó, ha megtörténik végzetes]
└── VAGY: Session lopás
    ├── ÉS: XSS a jegyzet mezőben
    └── ÉS: export újrahitelesítés nélkül

PASTA röviden

A PASTA (Process for Attack Simulation and Threat Analysis, Tony UcedaVélez és Marco Morana, 2015) hétlépéses, kockázat központú módszer. Az üzleti célokból indul, a technikai hatókörön, az alkalmazás felbontásán, a fenyegetéselemzésen, a sérülékenységelemzésen és a támadásszimuláción keresztül jut el a kockázat és hatás elemzéséig. Nagyobb szervezetben és szabályozott környezetben (pénzügy, egészségügy) hasznos, ahol a biztonsági döntést üzleti kockázati nyelvre kell fordítani. Kis csapatnak a négy kérdés és a STRIDE általában elég, a PASTA első lépését (mi az üzleti kár, ha ez elromlik) viszont érdemes átvenni.

Mikor csináljunk fenyegetésmodellt

EseményMélység
Új termék vagy nagy architektúra döntésTeljes DFD, STRIDE tábla, támadási fa a fő célokra, 1 és 3 óra között
Új funkció, ami bizalmi határt érint (auth, fizetés, fájl, külső API, LLM)15 és 30 perc közötti négy kérdéses kör
Bármilyen kisebb változtatás5 perces modell (a kézikönyv végén)
Incidens utánA modell frissítése azzal, ami kimaradt

Kidolgozott példa, egy SaaS app (Next.js, Supabase, Stripe, LLM)

Rendszer. Egy B2B jegyzetelő és összefoglaló SaaS. A felhasználó bejelentkezik (Supabase Auth), szervezetekbe (tenant) tartozik, dokumentumokat tölt fel, amelyeket egy LLM összefoglal. Előfizetést Stripe Checkouton vásárol, a Stripe webhook frissíti az előfizetési állapotot.

1. kérdés, min dolgozunk. A DFD szövegesen.

[Böngésző] --(1) HTTPS, session cookie--> (Next.js app, Vercel)
   ||======================= BIZALMI HATÁR: internet / szerver ==============||
(Next.js) --(2) anon kulcs + user JWT--> [Supabase Postgres, RLS]
(Next.js szerver) --(3) service_role kulcs--> [Supabase Postgres]   (csak admin műveletek)
[Böngésző] --(4) signed upload URL--> [Supabase Storage]
(Next.js) --(5) dokumentum szöveg--> [LLM szolgáltató API]
[Stripe] --(6) webhook, aláírt--> (Supabase edge function: stripe-webhook)
(edge function) --(7) service_role--> [Postgres: subscriptions]
(Next.js) --(8) Checkout session létrehozás, secret key--> [Stripe API]
   ||======================= BIZALMI HATÁR: tenant A / tenant B ============||

2. kérdés, mi romolhat el. STRIDE a határt keresztező nyilakon.

#NyílSTRIDEFenyegetésDöntés és védelem
T1(1)SSession lopás XSS-selHttpOnly, Secure, SameSite=Lax cookie, szigorú CSP, React autoescape, dangerouslySetInnerHTML tiltva
T2(1)T, EServer action hívás jogosultság nélkülMinden server action elején getUser() és tenant ellenőrzés, middleware nem elég
T3(2)I, EMás tenant adatainak olvasásaRLS minden táblán, policy tenant_id és tagság alapján, teszt, ami két tenanttal ellenőriz
T4(3)Eservice_role kulcs kiszivárog a kliensbeKulcs csak server-only modulban, nincs NEXT_PUBLIC_ előtag, gitleaks a CI-ban
T5(4)T, DRosszindulatú vagy óriási fájl feltöltéseMéret és MIME korlát a signed URL-en, tartalom alapú típusellenőrzés, külön bucket, nem futtatható domain
T6(5)IÉrzékeny adat megy az LLM szolgáltatóhozAdatfeldolgozói szerződés, zero retention beállítás, PII szűrés, tenant opt-in
T7(5)EPrompt injekció a feltöltött dokumentumbanRészletesen az AI ügynökök és vibecoding kézikönyvben. Alapszabály, hogy az LLM kimenete nem kap eszközt és nem kerül escape nélkül HTML-be
T8(6)S, THamis webhook, ingyenes előfizetésStripe aláírás ellenőrzés a nyers törzsön, esemény ID idempotencia, előfizetés állapot lekérése a Stripe API-ból
T9(8)TA kliens küldi az áratÁr csak szerveroldali price_id alapján, a kliens csak csomagnevet választ
T10(1), (5)DKöltségrobbanás LLM hívásokkalFelhasználónkénti és tenantonkénti kvóta, token bucket rate limit, napi költségplafon riasztással
T11(1)RFelhasználó letagadja, hogy törölt egy dokumentumotAudit napló a törlésről (ki, mikor, mit, honnan), append only tábla
T12(2)EFelhasználó saját magát adminná teszi profilfrissítésselMass assignment védelem, a role mező nem írható RLS-ből, külön admin útvonal

3. kérdés, mit teszünk ellene. Minden sorból ticket lesz, a ticket hivatkozik a T számra, és a hozzá tartozó teszt neve is tartalmazza (például rls_tenant_isolation_T3.test.ts). Az elfogadott kockázatok (például T6 maradék kockázata) dátummal és felelőssel az elfogadott kockázatok nyilvántartásába kerülnek.

4. kérdés, jól csináltuk. A PR template egy jelölőnégyzetet kap. Érint-e a változtatás bizalmi határt, és ha igen, frissült-e a modell. Negyedévente egyszer a csapat végigmegy a táblán, és megnézi, minden védelemhez van-e automatikus teszt.

A T2 és T3 fenyegetés kódszinten, sebezhető és javított változatban így néz ki.

// SEBEZHETŐ: T2 és T3. A server action csak azt nézi, be van-e lépve valaki.
'use server';
export async function deleteDocument(id: string) {
  const supabase = createAdminClient();            // service_role, megkerüli az RLS-t
  await supabase.from('documents').delete().eq('id', id);
}
// JAVÍTOTT: felhasználói kliens (RLS érvényes), explicit tagságellenőrzés, audit.
'use server';
import 'server-only';
import { z } from 'zod';

const Input = z.object({ id: z.string().uuid() });

export async function deleteDocument(raw: unknown) {
  const { id } = Input.parse(raw);
  const supabase = await createServerClient();     // a user JWT-jével fut
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) throw new AuthError();

  const { data: doc } = await supabase
    .from('documents').select('id, tenant_id').eq('id', id).single();
  if (!doc) throw new NotFoundError();             // RLS miatt idegen doksi nem is látszik

  const { error } = await supabase.from('documents').delete().eq('id', id);
  if (error) throw error;
  await audit('document.delete', { actor: user.id, tenant: doc.tenant_id, target: id });
}
-- RLS a documents táblán. Alapból tilt, a policy engedélyez.
alter table documents enable row level security;

create policy "tag olvashatja a sajat tenant dokumentumait"
on documents for select
using (
  exists (
    select 1 from memberships m
    where m.tenant_id = documents.tenant_id
      and m.user_id = (select auth.uid())
  )
);

create policy "tag torolhet a sajat tenantjaban"
on documents for delete
using (
  exists (
    select 1 from memberships m
    where m.tenant_id = documents.tenant_id
      and m.user_id = (select auth.uid())
      and m.role in ('owner', 'editor')
  )
);

3. OWASP Top 10 2025

A 2025-ös kiadás (véglegesítve 2025 végén) két új kategóriát hozott, és a nevekben a tünet helyett a gyökérokra helyezte a hangsúlyt. Az SSRF önálló kategóriaként megszűnt, az A01 része lett. A lista a 2026 októberi állapotot tükrözi. Ha egy projekt auditba vagy ügyfél elé kerül, a hivatkozás előtt az owasp.org/Top10 oldalon ellenőrizzük az aktuális szöveget.

#KategóriaVáltozás 2021-hez képest
A01Broken Access ControlVáltozatlanul első, magában foglalja az SSRF-et
A02Security MisconfigurationAz 5. helyről feljött
A03Software Supply Chain FailuresÚj név és bővült tartalom (korábban Vulnerable and Outdated Components)
A04Cryptographic FailuresA 2. helyről lejjebb
A05InjectionA 3. helyről lejjebb, az XSS is ide tartozik
A06Insecure DesignA 4. helyről lejjebb
A07Authentication FailuresRövidebb név, azonos terület
A08Software or Data Integrity FailuresHasonló, a build ellátási lánc része az A03-ba került
A09Security Logging and Alerting FailuresA Monitoring helyett Alerting, a riasztás a hangsúly
A10Mishandling of Exceptional ConditionsÚj, hibakezelés, fail open, kivételes állapotok

A01 Broken Access Control

Mi ez. A felhasználó olyan adathoz vagy funkcióhoz fér hozzá, amihez nincs joga. Ide tartozik az objektumszintű (IDOR, BOLA), a funkciószintű (admin végpont jogosultság nélkül), a CORS hiba, a JWT manipuláció és az SSRF.

// SEBEZHETŐ: az URL-ben jövő azonosítóra bízza magát.
export async function GET(_: Request, { params }: { params: { id: string } }) {
  const invoice = await db.invoice.findUnique({ where: { id: params.id } });
  return Response.json(invoice);
}
// JAVÍTOTT: az objektumot a felhasználó jogosultsági körén belül keressük.
export async function GET(_: Request, { params }: { params: { id: string } }) {
  const user = await requireUser();
  const invoice = await db.invoice.findFirst({
    where: { id: params.id, tenant: { members: { some: { userId: user.id } } } },
  });
  if (!invoice) return new Response('Not found', { status: 404 });
  return Response.json(invoice);
}

Védelem.

Tesztelés. Két felhasználó, két tenant fixture. Minden végpontra automatikus teszt, ami A felhasználóval B erőforrását kéri, és 404-et vagy 403-at vár. Supabase alatt a pgTAP vagy egy egyszerű integrációs teszt két JWT-vel.

A02 Security Misconfiguration

Mi ez. Rossz alapértelmezés, bekapcsolva hagyott debug, nyilvános bucket, kikapcsolt RLS, hiányzó security header, túl bőbeszédű hibaüzenet, alapértelmezett jelszó.

// SEBEZHETŐ: a teljes hibát és stack trace-t visszaküldi.
catch (e) { return Response.json({ error: String(e), stack: (e as Error).stack }, { status: 500 }); }
// JAVÍTOTT: belül részletes napló korrelációs azonosítóval, kifelé általános üzenet.
catch (e) {
  const ref = crypto.randomUUID();
  logger.error({ ref, err: e }, 'invoice export failed');
  return Response.json({ error: 'Belső hiba', ref }, { status: 500 });
}

Védelem. Infrastruktúra kódként (Terraform, Supabase migrációk), környezetenként azonos konfiguráció, a Supabase Security Advisor rendszeres futtatása (jelzi az RLS nélküli táblákat, a security definer nézeteket, a mutable search_path függvényeket), security headerek middleware-ben, X-Powered-By kikapcsolva (poweredByHeader: false a next.config-ban).

Tesztelés. Konfiguráció szkenner (Supabase advisor, checkov vagy trivy config az IaC-ra), ZAP baseline scan a headerekre, egy CI teszt, ami ellenőrzi, hogy minden public sémabeli táblán be van-e kapcsolva az RLS.

-- CI ellenőrzés: RLS nélküli táblák listája. Üres eredmény a cél.
select schemaname, tablename
from pg_tables
where schemaname = 'public' and rowsecurity = false;

A03 Software Supply Chain Failures

Mi ez. Sérülékeny, elavult, kompromittált vagy hamis függőség, megbízhatatlan build folyamat, ellopott publikálási token. A részletekre az ellátási lánc biztonságánál visszatérünk.

Védelem röviden. Lockfile és npm ci, SCA a CI-ban, Dependabot vagy Renovate, GitHub Actions SHA pinning, trusted publishing, SBOM.

Tesztelés. npm audit --omit=dev, osv-scanner, a lockfile változásainak review-ja minden PR-ban.

A04 Cryptographic Failures

Mi ez. Titkosítás hiánya érzékeny adatnál, gyenge algoritmus (MD5, SHA1 jelszóhoz), saját kripto, rossz véletlenszám, hardkódolt kulcs, TLS ellenőrzés kikapcsolva.

// SEBEZHETŐ: gyors hash jelszóra, kiszámítható token.
const hash = createHash('sha256').update(password).digest('hex');
const resetToken = Math.random().toString(36).slice(2);
// JAVÍTOTT: lassú, sózott jelszó hash és kriptográfiailag erős token.
import { hash, verify } from '@node-rs/argon2';
const pwHash = await hash(password);                       // Argon2id, alapértelmezett paraméterek
const resetToken = randomBytes(32).toString('base64url');
await db.reset.create({ data: { tokenHash: sha256(resetToken), expiresAt: in15min() } });

Védelem. Jelszóhoz Argon2id, scrypt vagy bcrypt (vagy hagyjuk a Supabase Auth-ra). Szimmetrikus titkosításhoz AES-256-GCM vagy a libsodium. A tokeneket hash-elve tároljuk. TLS 1.2 vagy 1.3 mindenhol, HSTS. A kulcsok KMS-ben vagy secret managerben, rotációs tervvel. A poszt kvantum átállásra (NIST FIPS 203, 204, 205, 2024) a nagy szolgáltatók TLS szinten már felkészültek, saját kódban a kripto agilitás (cserélhető algoritmus) a feladat.

Tesztelés. Semgrep szabályok a Math.random, createHash('md5'), rejectUnauthorized: false mintákra, testssl.sh vagy SSL Labs a TLS-re.

A05 Injection

Mi ez. Megbízhatatlan adat értelmezőhöz jut (SQL, NoSQL, OS shell, LDAP, template, HTML és JavaScript a böngészőben), és ott kódként fut. Az XSS 2021 óta ebbe a kategóriába tartozik.

// SEBEZHETŐ: string összefűzés SQL-ben.
const rows = await sql.unsafe(`select * from users where email = '${email}'`);
// JAVÍTOTT: paraméterezett lekérdezés (postgres.js tagged template).
const rows = await sql`select * from users where email = ${email}`;
// SEBEZHETŐ: shell parancs felhasználói bemenettel.
exec(`convert ${file} out.png`);
// JAVÍTOTT: argumentumtömb, shell nélkül, előtte allowlist validáció.
execFile('convert', [safePath(file), 'out.png']);

Supabase alatt a supabase.rpc() paraméterei biztonságosak, de egy plpgsql függvényen belüli execute format('... %s', input) újra nyitja a rést. Ott %L (literál) vagy %I (azonosító) kell, vagy execute ... using.

Védelem. Paraméterezés, ORM vagy query builder, kimenet kódolás a célkontextus szerint, allowlist validáció (zod séma), a dinamikus azonosítók (oszlopnév, rendezés) allowlistből.

Tesztelés. Semgrep (javascript.lang.security, typescript.react.security szabálycsomagok), DAST stagingen, unit teszt rosszindulatú bemenetekkel a validátorra.

A06 Insecure Design

Mi ez. A hiba a tervben van, a kód hibátlanul valósítja meg. Például a jelszó visszaállítás biztonsági kérdéssel, a korlátlan kupon beváltás, az ár a kliensről, a többlépcsős folyamat, ahol a lépések kihagyhatók.

// SEBEZHETŐ TERV: a kliens mondja meg az árat.
const session = await stripe.checkout.sessions.create({
  line_items: [{ price_data: { currency: 'huf', unit_amount: body.amount, product_data: { name: body.plan } }, quantity: 1 }],
  mode: 'payment', success_url, cancel_url,
});
// JAVÍTOTT TERV: a szerver allowlistből választ árat, a kliens csak csomagot jelöl.
const PRICES = { pro: process.env.STRIPE_PRICE_PRO!, team: process.env.STRIPE_PRICE_TEAM! } as const;
const plan = z.enum(['pro', 'team']).parse(body.plan);
const session = await stripe.checkout.sessions.create({
  line_items: [{ price: PRICES[plan], quantity: 1 }],
  mode: 'subscription', customer: await customerIdFor(user), success_url, cancel_url,
}, { idempotencyKey: `checkout:${user.id}:${plan}:${today()}` });

Védelem. Fenyegetésmodell, visszaélési esetek (abuse case) a user story mellé, állapotgépek a többlépcsős folyamatokra, üzleti limitek (kvóta, sebesség, összeghatár).

Tesztelés. Abuse case tesztek (mi van, ha kétszer küldi, ha kihagy egy lépést, ha párhuzamosan küldi), versenyhelyzet teszt párhuzamos kérésekkel.

A07 Authentication Failures

Mi ez. Credential stuffing elleni védelem hiánya, gyenge jelszó politika, session fixáció, hosszú életű token visszavonás nélkül, MFA hiánya, felhasználó felsorolás a hibaüzenetből.

Védelem.

Tesztelés. Automatikus teszt a felsorolásra (két eltérő hibaüzenet tilos), rate limit teszt, ZAP authenticated scan.

A08 Software or Data Integrity Failures

Mi ez. Integritásellenőrzés nélkül elfogadott kód vagy adat. Aláíratlan webhook, CDN-ről betöltött script SRI nélkül, nem biztonságos deszerializáció, automatikus frissítés aláírás nélkül, cache mérgezés.

// SEBEZHETŐ: Stripe webhook aláírás nélkül, JSON-ba parse-olt törzzsel.
export async function POST(req: Request) {
  const event = await req.json();
  if (event.type === 'checkout.session.completed') await activate(event.data.object.customer);
  return new Response('ok');
}
// JAVÍTOTT: Supabase edge function, nyers törzs, aláírás és idempotencia.
import Stripe from 'npm:stripe@17';
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!);
const whsec = Deno.env.get('STRIPE_WEBHOOK_SECRET')!;

Deno.serve(async (req) => {
  const sig = req.headers.get('stripe-signature');
  const body = await req.text();                                   // nyers törzs kell az aláíráshoz
  let event: Stripe.Event;
  try {
    event = await stripe.webhooks.constructEventAsync(body, sig!, whsec);
  } catch {
    return new Response('invalid signature', { status: 400 });
  }
  const { error } = await admin.from('stripe_events').insert({ id: event.id });  // unique PK
  if (error?.code === '23505') return new Response('duplicate', { status: 200 });
  await handle(event);                                              // az állapotot a Stripe API-ból kérjük le újra
  return new Response('ok');
});

Ennek az edge functionnek a verify_jwt = false beállítás kell (a Stripe nem küld Supabase JWT-t), ezért a hitelesítést itt az aláírás adja. Ez egy tudatos kivétel, amit a konfigurációban kommenttel jelölünk.

Védelem. Aláírás ellenőrzés minden bejövő integráción, SRI (integrity attribútum) külső scripteken, deszerializáláskor séma validáció, aláírt artefaktumok.

Tesztelés. Teszt, ami aláíratlan és lejárt időbélyegű webhookot küld és 400-at vár.

A09 Security Logging and Alerting Failures

Mi ez. A biztonsági esemény nem kerül naplóba, a napló nem jut el senkihez, vagy a riasztás annyira zajos, hogy senki nem nézi. Az átnevezés jelzi, hogy a napló riasztás nélkül keveset ér. A részletekre a naplózásnál visszatérünk.

Tesztelés. Évente legalább egy gyakorlat, ahol szándékosan kiváltunk egy eseményt (sikertelen admin login sorozat) és mérjük, mennyi idő alatt jut el emberhez.

A10 Mishandling of Exceptional Conditions

Mi ez. A rendszer váratlan állapotban (hiba, timeout, null, erőforrás kimerülés, részleges siker) bizonytalan vagy nyitott állapotba kerül. Tipikus a fail open, ahol egy hibát elnyelő catch engedélyez. Ide tartozik a félbeszakadt tranzakció, ami fél állapotot hagy, és a hibaüzenetből szivárgó belső információ is.

// SEBEZHETŐ: fail open. Ha a jogosultsági szolgáltatás elérhetetlen, mindent enged.
async function canExport(userId: string) {
  try { return await authz.check(userId, 'export'); }
  catch { return true; }
}
// JAVÍTOTT: fail closed, naplózva, időkorláttal.
async function canExport(userId: string): Promise<boolean> {
  try {
    return await withTimeout(authz.check(userId, 'export'), 2000);
  } catch (e) {
    logger.warn({ userId, err: e }, 'authz check failed, denying');
    return false;
  }
}
// SEBEZHETŐ: két külön írás, a második hibája fél állapotot hagy (kredit levonva, munka nem indult).
await db.credits.decrement(user.id, cost);
await queue.enqueue(job);

// JAVÍTOTT: egy tranzakció és outbox minta. A sor felvétele ugyanabban a tranzakcióban történik.
await db.$transaction(async (tx) => {
  await tx.credits.decrement(user.id, cost);
  await tx.outbox.create({ data: { type: 'job.start', payload: job } });
});

Védelem. Fail closed alapértelmezés, explicit timeout minden külső hívásra, tranzakciók és idempotencia, globális hibakezelő, ami nem szivárogtat, erőforráskorlát (méret, idő, memória, rekurziós mélység).

Tesztelés. Hibainjektálás (a függőség timeoutol, 500-at ad, üres választ ad), chaos teszt stagingen, a catch blokkok review-ja.


4. OWASP ASVS 5.0, API Security Top 10 és a Cheat Sheet Series

ASVS 5.0 (2025 május)

Az Application Security Verification Standard a Top 10-zel szemben követelménylista. A Top 10 tudatosító dokumentum, az ASVS ellenőrizhető tételekből áll, ezért szerződésbe, ticketbe, pentest hatókörbe írható. Az 5.0.0 verzió 2025 májusában jelent meg, nagyjából 350 követelményt tartalmaz 17 fejezetben, és teljesen átszámozta a korábbi 4.0.3 tételeket.

FejezetTerület
V1Encoding and Sanitization
V2Validation and Business Logic
V3Web Frontend Security
V4API and Web Service
V5File Handling
V6Authentication
V7Session Management
V8Authorization
V9Self-contained Tokens (JWT és társai)
V10OAuth and OIDC
V11Cryptography
V12Secure Communication
V13Configuration
V14Data Protection
V15Secure Coding and Architecture
V16Security Logging and Error Handling
V17WebRTC

Szintek.

SzintKinekGyakorlatban
L1Minden alkalmazás, alacsony kockázatAz első védelmi vonal, a legolcsóbban megvalósítható és legtöbbet érő tételek
L2Érzékeny adatot kezelő üzleti app (a legtöbb B2B SaaS)Az ajánlott alapszint, ide tartozik a tételek nagyobb része
L3Kritikus rendszer (egészségügy, pénzügy, infrastruktúra)Mélyebb tervezési és verifikációs követelmények

A szintek kumulatívak, az L2 tartalmazza az L1-et.

Használat követelménylistaként lépésről lépésre.

  1. Célszintet választunk a fenyegetésmodell és az adatérzékenység alapján. Egy Supabase alapú B2B SaaS-nél ez L2.
  2. Kihúzzuk a releváns fejezeteket. WebRTC nélkül a V17 kimarad, saját OAuth szerver nélkül a V10 jó része is.
  3. A tételeket verziózott azonosítóval hivatkozzuk (például v5.0.0-8.2.1), mert a régi és új számozás nem keverhető.
  4. Minden tételhez rögzítjük, hol és hogyan teljesül (kód hely, konfiguráció, teszt), vagy miért nem releváns.
  5. Az új funkció ticketjébe a releváns ASVS tételek kerülnek elfogadási kritériumként.
  6. A pentest hatókörét az ASVS szint és fejezetek szerint adjuk meg, így a jelentés összevethető.

Tipikus hiba. Az egész ASVS-t egyszerre próbáljuk teljesíteni, és a táblázat soha nem készül el. Jobb kezdés az L1 a V6, V7, V8 fejezetekből, mert ott keletkezik a legtöbb valós kár.

OWASP API Security Top 10 (2023)

A REST és GraphQL API-k sajátos hibáit gyűjti. Egy Next.js plus Supabase app minden route handlere és a PostgREST felület is API.

#KategóriaMit jelent egy SaaS-benVédelem
API1Broken Object Level AuthorizationMás tenant objektuma ID alapján elérhetőRLS, tenant szűrés minden lekérdezésben
API2Broken AuthenticationGyenge token kezelés, hiányzó rate limitBevált auth, rövid életű token, MFA
API3Broken Object Property Level AuthorizationTúl sok mező a válaszban, vagy írható role mezőExplicit select lista, DTO, írási allowlist
API4Unrestricted Resource ConsumptionLapozás nélküli lista, korlátlan fájl, LLM költségLimit, lapozás, kvóta, timeout
API5Broken Function Level AuthorizationAdmin végpont sima felhasználónakSzerepkör ellenőrzés a funkció szintjén
API6Unrestricted Access to Sensitive Business FlowsKupon, regisztráció, foglalás tömeges automatizálásaÜzleti rate limit, bot észlelés, kvóta
API7Server Side Request ForgeryURL előnézet belső címreAllowlist, DNS ellenőrzés, egress szűrés
API8Security MisconfigurationNyitott CORS, debug végpontSzigorú CORS, header, konfiguráció review
API9Improper Inventory ManagementElfelejtett v1 vagy staging API élesenAPI leltár, régi verziók kikapcsolása
API10Unsafe Consumption of APIsHarmadik fél válaszát vakon elfogadjukValidáció és timeout a bejövő harmadik fél adatra is
// SEBEZHETŐ: API3. A teljes kérés törzset beírja a profilba, így a role is írható.
await supabase.from('profiles').update(await req.json()).eq('id', user.id);
// JAVÍTOTT: írási allowlist séma, és az RLS sem engedi a role oszlopot.
const ProfileUpdate = z.object({ display_name: z.string().max(80), avatar_url: z.string().url().optional() }).strict();
await supabase.from('profiles').update(ProfileUpdate.parse(await req.json())).eq('id', user.id);
-- Oszlopszintű védelem: a hitelesített szerepkör csak a megengedett oszlopokat frissítheti.
revoke update on profiles from authenticated;
grant update (display_name, avatar_url) on profiles to authenticated;

Cheat Sheet Series, a legfontosabb lapok

Az OWASP Cheat Sheet Series (cheatsheetseries.owasp.org) témánként tömör, gyakorlati útmutató. Egy webes SaaS-hez ezeket érdemes ismerni.

LapMikor hasznos
Authentication, Password Storage, Multifactor AuthenticationAuth folyamat, jelszó tárolás
Session ManagementCookie beállítás, session élettartam
Authorization, Insecure Direct Object Reference PreventionJogosultsági modell, BOLA
Cross Site Scripting Prevention, DOM based XSS PreventionBármi, ami HTML-t renderel
Content Security PolicyCSP bevezetés
Cross-Site Request Forgery PreventionCookie alapú auth mellett
Server Side Request Forgery PreventionURL-t fogadó funkció
SQL Injection Prevention, Query ParameterizationNyers SQL írásakor
File UploadFeltöltés
Input Validation, Mass AssignmentBejövő adat kezelése
Logging, Logging VocabularyNaplózás tervezése
Secrets ManagementKulcskezelés
REST Security, GraphQLAPI tervezés
DeserializationNem JSON vagy objektum visszaállítás
HTTP Headers, HTTP Strict Transport SecurityHeader konfiguráció
NPM Security, Software Supply Chain SecurityFüggőségkezelés
Threat ModelingModellezés kezdése

5. Webes alapvédelmek részletesen

XSS típusai és a CSP

TípusHol keletkezikPélda SaaS-ben
ReflectedA szerver a kérésből visszatükrözKeresési kifejezés a találati oldalon kódolás nélkül
StoredTárolt adat renderelődik másnakJegyzet, kommentár, profil név
DOM basedKliens JS ír a DOM-ba nem biztonságos forrásbóllocation.hash értéke innerHTML-be
Mutation XSSA sanitizer és a böngésző parsere eltérően értelmezSaját regex alapú HTML tisztító

A React és a Next.js alapból kódolja a JSX-be írt szöveget. A rés ott nyílik, ahol ezt megkerüljük.

// SEBEZHETŐ: felhasználói vagy LLM által generált HTML közvetlenül.
<div dangerouslySetInnerHTML={{ __html: note.body }} />
<a href={user.website}>Weboldal</a>      {/* javascript: séma is lehet */}
// JAVÍTOTT: sanitizálás egy bevált könyvtárral, URL séma allowlist.
import DOMPurify from 'isomorphic-dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(note.body, { USE_PROFILES: { html: true } }) }} />

const safeHref = (u: string) => { try { const x = new URL(u); return ['https:', 'http:'].includes(x.protocol) ? x.href : '#'; } catch { return '#'; } };
<a href={safeHref(user.website)} rel="noopener noreferrer">Weboldal</a>

Markdown renderelésnél (például LLM kimenet) a react-markdown alapból nem renderel nyers HTML-t. Ezt nem kapcsoljuk be rehype-raw-val sanitizálás nélkül.

Content Security Policy. A CSP a második védelmi vonal. Ha mégis bejut egy script, a böngésző nem futtatja. A modern, nonce alapú szigorú CSP mintája Next.js middleware-ben.

// middleware.ts: nonce alapú szigorú CSP.
export function middleware(req: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  const csp = [
    `default-src 'self'`,
    `script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'self' 'nonce-${nonce}'`,
    `img-src 'self' data: https://*.supabase.co`,
    `connect-src 'self' https://*.supabase.co https://api.stripe.com`,
    `frame-src https://js.stripe.com https://checkout.stripe.com`,
    `object-src 'none'`, `base-uri 'none'`, `frame-ancestors 'none'`, `form-action 'self'`,
    `upgrade-insecure-requests`,
  ].join('; ');
  const headers = new Headers(req.headers);
  headers.set('x-nonce', nonce);
  const res = NextResponse.next({ request: { headers } });
  res.headers.set('Content-Security-Policy', csp);
  return res;
}

CSP bevezetés lépései.

  1. A szabály Content-Security-Policy-Report-Only módban indul egy riport végponttal (report-to).
  2. Egy-két hétig gyűjtjük a sértéseket, és az inline scripteket nonce-ra javítjuk.
  3. Élesre kapcsoljuk, a riportolás bekapcsolva marad.
  4. A script-src nem kap 'unsafe-inline' és 'unsafe-eval' értéket. Ha egy könyvtár igényli, mást keresünk helyette.
  5. Opcionálisan a Trusted Types (require-trusted-types-for 'script') a DOM XSS ellen.

CSRF és SameSite

A CSRF akkor él, ha a böngésző automatikusan csatolja a hitelesítő cookie-t egy idegen oldalról indított kéréshez.

VédelemMit adMegjegyzés
SameSite=Lax cookieIdegen oldalról jövő POST nem viszi a cookie-tModern böngésző alapértelmezés, de explicit beállítjuk
SameSite=StrictSemmilyen cross site navigáció nem visziKényelmetlen, külső linkről kijelentkezettnek látszik
Anti CSRF token (synchronizer vagy double submit)Független a cookie viselkedéstőlKlasszikus űrlapoknál
Origin és Sec-Fetch-Site ellenőrzésOlcsó, megbízható modern böngészőbenNext.js server actions beépítetten ellenőrzi az Origin és Host egyezést
Bearer token a header-ben cookie helyettNincs automatikus csatolásViszont a token tárolása XSS kockázat

A GET kérés soha nem változtat állapotot. A Next.js server actions POST-ot használ és összeveti az Origin fejlécet a hosttal. Reverse proxy mögött a serverActions.allowedOrigins beállítást szűkre vesszük.

CORS helyes beállítása

A CORS nem védelem, hanem a same origin policy kontrollált lazítása. A hiba mindig a túl tág engedély.

// SEBEZHETŐ: visszatükrözi a kérő origint és engedi a cookie-t.
res.headers.set('Access-Control-Allow-Origin', req.headers.get('origin') ?? '*');
res.headers.set('Access-Control-Allow-Credentials', 'true');
// JAVÍTOTT: explicit allowlist, Vary fejléc, credentials csak ahol kell.
const ALLOWED = new Set(['https://app.pelda.hu', 'https://admin.pelda.hu']);
const origin = req.headers.get('origin');
if (origin && ALLOWED.has(origin)) {
  res.headers.set('Access-Control-Allow-Origin', origin);
  res.headers.set('Access-Control-Allow-Credentials', 'true');
  res.headers.set('Vary', 'Origin');
}

Szabályok. A * credentials mellett nem működik és nem is kell. Regexszel vagy végződés alapján nem illesztünk (endsWith('pelda.hu') átengedi a gonoszpelda.hu címet). A null origint nem engedjük. Supabase edge functionnél a CORS fejlécet mi állítjuk be, a sablonban szereplő * csak publikus, cookie nélküli végpontra jó.

SSRF védekezés

Az SSRF akkor keletkezik, ha a szerver a felhasználó által megadott URL-t kéri le (link előnézet, webhook cél, kép importálás, PDF generálás URL-ből). A támadó belső címet ad meg, és a szerver a saját hálózati pozíciójából éri el, például a felhő metaadat szolgáltatást.

// SEBEZHETŐ: bármilyen URL-t lekér.
const html = await fetch(body.url).then((r) => r.text());
// JAVÍTOTT: séma és port allowlist, DNS feloldás után IP ellenőrzés, átirányítás kézzel, timeout, méretkorlát.
import { lookup } from 'node:dns/promises';
import ipaddr from 'ipaddr.js';

async function safeFetch(raw: string) {
  const url = new URL(raw);
  if (url.protocol !== 'https:' || (url.port && url.port !== '443')) throw new Error('blocked');
  const addrs = await lookup(url.hostname, { all: true });
  for (const { address } of addrs) {
    if (ipaddr.parse(address).range() !== 'unicast') throw new Error('blocked');   // privát, loopback, link local tiltva
  }
  const res = await fetch(url, { redirect: 'manual', signal: AbortSignal.timeout(5000) });
  if (res.status >= 300 && res.status < 400) throw new Error('redirect not followed');
  return readLimited(res, 2_000_000);
}

A fenti kód csökkenti, de nem zárja ki a DNS rebinding kockázatot, mert az ellenőrzés és a kapcsolódás két külön feloldás. Erősebb megoldás, ha a kapcsolat a már ellenőrzött IP-re épül (egyedi lookup az HTTP agentben), vagy ha a kimenő kéréseket egy egress proxy (például Smokescreen) végzi, ami hálózati szinten tiltja a belső tartományokat. Felhőben az IMDSv2 kötelezővé tétele (AWS) is csökkenti a kárt.

Nyílt átirányítás

// SEBEZHETŐ: login után bárhová visszairányít.
redirect(searchParams.get('next') ?? '/');
// JAVÍTOTT: csak saját, relatív útvonal.
function safeNext(n: string | null) {
  if (!n || !n.startsWith('/') || n.startsWith('//') || n.startsWith('/\\')) return '/';
  return n;
}
redirect(safeNext(searchParams.get('next')));

A nyílt átirányítás phishinghez és OAuth token lopáshoz vezet. Az OAuth redirect URI-t mindig pontos egyezéssel regisztráljuk a szolgáltatónál (Supabase Auth URL allowlist), wildcard nélkül.

Fájlfeltöltés biztonsága

KockázatVédelem
Futtatható tartalom (HTML, SVG scripttel) a saját domainenKülön, cookie nélküli domainről szolgáljuk ki, Content-Disposition: attachment, X-Content-Type-Options: nosniff
Hamis kiterjesztésTartalom alapú típusfelismerés (magic bytes, file-type), allowlist
Óriási fájl, tömörítési bombaMéretkorlát a signed URL-en és a feldolgozásnál, kibontási korlát
Útvonal manipuláció a fájlnévbenSaját generált név (UUID), az eredeti név csak metaadat
KártevőVíruskeresés aszinkron (ClamAV vagy szolgáltatói scan), karantén bucket
Más tenant fájljaStorage RLS policy a tenant prefixre
Képfeldolgozó könyvtár sérülékenységeSandbox vagy külön worker, frissített sharp és libvips
-- Supabase Storage: csak a saját tenant mappájába írhat a tag.
create policy "tenant upload"
on storage.objects for insert to authenticated
with check (
  bucket_id = 'uploads'
  and (storage.foldername(name))[1] in (
    select tenant_id::text from memberships where user_id = (select auth.uid())
  )
);

Deszerializáció

A JSON.parse önmagában adatot ad vissza, de ha az eredményt vakon objektumokba olvasztjuk, prototype pollution keletkezhet (__proto__ kulcs egy mély merge-ben). Más formátumoknál (YAML teljes sémával, node-serialize, Java vagy PHP natív szerializáció, pickle) a deszerializáció kódfuttatást is jelenthet. 2025 decemberében a React Server Components Flight protokolljában talált kritikus sérülékenység (CVE-2025-55182) azt mutatta, hogy egy keretrendszer saját szerializációs formátuma is ilyen bizalmi határ, és a javítást gyorsan telepíteni kellett minden érintett Next.js verzión.

// SEBEZHETŐ: mély merge ellenőrizetlen bemenettel.
const settings = merge(defaults, JSON.parse(body));

// JAVÍTOTT: séma validáció, ismeretlen kulcsok elutasítva, prototípus nélküli objektum.
const Settings = z.object({ theme: z.enum(['light', 'dark']), locale: z.string().max(10) }).strict();
const settings = { ...defaults, ...Settings.parse(JSON.parse(body)) };

YAML-nál yaml csomag alapértelmezett, biztonságos sémája, és soha nem használunk olyan formátumot, ami osztálypéldányt állít vissza megbízhatatlan forrásból. A keretrendszer biztonsági frissítéseire külön riasztás kell (GitHub security advisory feliratkozás a next és react csomagokra).

Security headerek listája

HeaderAjánlott értékMit véd
Content-Security-PolicyNonce alapú, a fenti minta szerintXSS, adatszivárgás, clickjacking (frame-ancestors)
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preloadSSL stripping
X-Content-Type-OptionsnosniffMIME szaglás
X-Frame-OptionsDENY (régi böngészőknek, a CSP mellé)Clickjacking
Referrer-Policystrict-origin-when-cross-origin vagy no-referrerURL-ben lévő adat szivárgása
Permissions-Policycamera=(), microphone=(), geolocation=()Böngésző API visszaélés
Cross-Origin-Opener-Policysame-originCross window támadások, XS leak
Cross-Origin-Resource-Policysame-origin vagy same-siteErőforrás beágyazás idegen oldalon
Cross-Origin-Embedder-Policyrequire-corp (ha kell izoláció)Spectre jellegű szivárgás
Cache-Controlno-store érzékeny válaszokonMegosztott cache-ben maradó adat
X-Powered-ByEltávolítvaFelderítés

Rate limiting algoritmusai

AlgoritmusMűködésElőnyHátrány
Fixed windowSzámláló időablakonkéntEgyszerű, olcsóAz ablakhatáron dupla burst
Sliding logMinden kérés időbélyegePontosMemóriaigényes
Sliding window counterKét ablak súlyozott átlagaJó közelítés, olcsóKözelítés
Token bucketVödör tokenekkel, fix ütemben töltődikBurst engedhető, átlag korlátosKét paraméter hangolása
Leaky bucketSor fix kifolyási sebességgelEgyenletes kimenetBurst késleltetve
GCRAToken bucket időbélyeggel, egy értékkelHatékony, Redisben atomikusNehezebben érthető

Kulcsválasztás. Bejelentkezésnél felhasználónév és IP külön-külön. API-nál felhasználó vagy API kulcs, nem IP (NAT mögött sokan osztoznak). Drága műveletnél (LLM, export) tenant szintű kvóta is. Serverless környezetben a számláló külső tárban kell legyen (Upstash Redis, Postgres), mert a példányok nem osztoznak memórián.

// Token bucket Upstash Ratelimit könyvtárral, felhasználónként.
import { Ratelimit } from '@upstash/ratelimit';
const limiter = new Ratelimit({ redis, limiter: Ratelimit.tokenBucket(10, '1 m', 20) });
const { success, reset } = await limiter.limit(`summarize:${user.id}`);
if (!success) return new Response('Too Many Requests', { status: 429, headers: { 'Retry-After': String(Math.ceil((reset - Date.now()) / 1000)) } });

6. Ellátási lánc biztonság

A modern webapp kódjának túlnyomó része függőség. A támadók ezért egyre gyakrabban a függőséget, a build folyamatot vagy a publikálási jogot támadják. Thompson 1984-es kérdése ma minden npm install alkalmával felmerül.

SLSA szintek

A SLSA (Supply chain Levels for Software Artifacts, slsa.dev) a build integritásának lépcsőzetes keretrendszere. Az 1.x specifikáció build trackje az alábbi szinteket határozza meg, a frissebb kiadások ezt forrás (source) trackkel egészítik ki, ami a verziókezelő védelmére vonatkozik.

SzintKövetelményMit véd
Build L0Nincs garanciaSemmit
Build L1Létezik provenance (ki, mit, miből épített)Hibák és tévedések felderítése
Build L2Hosztolt build platform aláírja a provenance-tBuild utáni manipuláció
Build L3Megerősített, izolált build, a titkok nem érhetők el a build lépésekbőlBuild közbeni manipuláció

Kis projektben. A GitHub Actions hosztolt runner és a actions/attest-build-provenance vagy az npm --provenance publikálás (trusted publishing OIDC-vel) gyakorlatilag L2-t ad, a reusable workflow alapú izolált build közelít az L3-hoz.

SBOM (CycloneDX, SPDX)

A Software Bill of Materials a szoftver összetevőinek gépi úton olvasható listája verziókkal és licencekkel. Két fő formátum létezik. A CycloneDX (OWASP, biztonsági fókuszú, VEX támogatással) és az SPDX (Linux Foundation, ISO/IEC 5962 szabvány, licenc fókuszú eredettel).

# SBOM generálás és szkennelés kis projektben
npx @cyclonedx/cyclonedx-npm --output-file sbom.cdx.json
osv-scanner scan --sbom=sbom.cdx.json

Az SBOM akkor ér valamit, ha egy új CVE megjelenésekor percek alatt megmondjuk belőle, érintett-e a termék. Ehhez minden kiadáshoz tároljuk, és az EU Cyber Resilience Act (CRA) jelentési kötelezettségei 2026 szeptemberétől erre építenek a digitális elemeket tartalmazó termékeknél.

Sigstore

A Sigstore (cosign, Fulcio, Rekor) kulcs nélküli aláírást ad. A fejlesztő vagy a CI OIDC identitással rövid életű tanúsítványt kap, aláír, és az aláírás egy nyilvános átláthatósági naplóba kerül. Így nem kell hosszú életű aláíró kulcsot őrizni. Az npm provenance, a PyPI attestations és a GitHub artifact attestations mind erre épül.

# Konténer kép aláírása és ellenőrzése CI identitással
cosign sign ghcr.io/ceg/app@sha256:...
cosign verify ghcr.io/ceg/app@sha256:... \
  --certificate-identity-regexp 'https://github.com/ceg/app/.github/workflows/release.yml@.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Lockfile fegyelem

Dependency confusion, typosquatting, slopsquatting

TámadásMechanizmusVédelem
Dependency confusionBelső csomagnév a nyilvános registryben magasabb verzióvalScoped csomagok (@ceg/), registry hozzárendelés scope-onként .npmrc-ben, a scope lefoglalása a nyilvános registryben
TyposquattingElgépelt név (expres, lodahs)Telepítés előtti név ellenőrzés, allowlist, SCA reputáció jelzés (Socket)
SlopsquattingAz LLM által kitalált, nem létező csomagnév, amit a támadó előre regisztrálMinden AI által javasolt csomagot ellenőrzünk a registryben (kor, letöltésszám, karbantartó, repó), mielőtt telepítjük
Account takeoverKarbantartó fiókjának feltörése phishinggelPhishing álló MFA, trusted publishing, granulált és lejáró tokenek
Protestware, sabotageKarbantartó szándékosan ártVerzió rögzítés, frissítés késleltetés, diff review kritikus csomagoknál

A slopsquatting kifejezést Seth Larson (Python Software Foundation) használta először. Kutatások szerint a kódgeneráló modellek javaslatainak jelentős része nem létező csomagra mutat, és ezek a nevek ismétlődnek, tehát előre regisztrálhatók. AI asszisztált fejlesztésben ebből közvetlen csapatszabály következik, a kézikönyv végén részletezve.

2025 szeptemberében egy célzott phishing kampány népszerű npm csomagok (köztük a chalk és a debug) karbantartójának fiókját vette át, és pár órára kártékony verziók jelentek meg. Ugyanebben a hónapban a Shai-Hulud féreg ellopott npm tokenekkel önmagát publikálta újabb csomagokba. Mindkét esetben a gyorsan frissítő, install scripteket futtató projektek sérültek leginkább.

Az xz utils backdoor (2024) tanulsága

2024 márciusában Andres Freund, a Microsoft mérnöke egy szokatlan, fél másodperces SSH bejelentkezési késleltetés miatt kezdett nyomozni, és felfedezte, hogy az xz tömörítő könyvtár két kiadásába hátsó ajtót rejtettek (CVE-2024-3094). Egy Jia Tan nevű közreműködő két éven át építette a bizalmat, a túlterhelt egyedüli karbantartóra kívülről nyomást gyakoroltak, végül társkarbantartói jogot kapott. A kártékony kód a git repóban nem volt olvasható formában. A kiadási tarballban lévő build script és bináris tesztfájlok illesztették be fordításkor, és bizonyos disztribúciókban az sshd-n keresztül hatott.

Tanulságok mérnöki szabályként.

  1. A kiadási artefaktum nem azonos a forrással. Lehetőleg a git tagből építünk, és a tarball és a repó közötti eltérést automatikusan ellenőrizzük.
  2. Reprodukálható build. Ha két független build bitre azonos eredményt ad, a rejtett beavatkozás feltűnik.
  3. A teljesítmény anomália biztonsági jelzés. Freund egy benchmark közben vette észre. Ugyanaz a figyelem, mint Stoll 75 centjénél.
  4. A karbantartói egészség biztonsági tényező. Az egyszemélyes, kiégett projekt a legkönnyebb célpont. A kritikus függőségeinket ezért támogatjuk (OpenSSF, pénz, review munka), és figyeljük a karbantartó változását.
  5. Bináris tesztadat a repóban gyanús terület. Review nélkül nem kerülhet be.
  6. Gyors visszaállíthatóság. A disztribúciók órák alatt visszaléptek egy korábbi verzióra. Ehhez verziórögzítés és ismert jó állapot kell.

GitHub Actions biztonság

# SEBEZHETŐ: tagre hivatkozó action, széles jogosultság, pull_request_target kódkivétellel.
on: pull_request_target
permissions: write-all
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { ref: ${{ github.event.pull_request.head.sha }} }   # idegen kód titkokkal
      - uses: some-org/changed-files@v45
      - run: npm install && npm test
        env: { NPM_TOKEN: ${{ secrets.NPM_TOKEN }} }
# JAVÍTOTT: pull_request trigger, minimális jogosultság, SHA pinning, titkok nélkül.
on: pull_request
permissions:
  contents: read
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
        with: { persist-credentials: false }
      - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
        with: { node-version: 22, cache: npm }
      - run: npm ci --ignore-scripts
      - run: npm test

A fenti SHA értékek mintaként szerepelnek, élesben a Dependabot vagy a pinact eszköz frissíti őket.

Szabályok.


7. Mélységi védelem, zero trust, közös felelősség

Mélységi védelem (defense in depth)

Egyetlen kontroll sem tökéletes, ezért egymástól független rétegeket építünk, hogy egy réteg hibája ne vezessen azonnal kárhoz. A rétegek akkor érnek valamit, ha tényleg függetlenek. Ha mindegyik ugyanarra a feltevésre épül (a JWT érvényes), egyetlen hiba mindet átüti.

RétegPélda a SaaS-ben
PeremVercel vagy Cloudflare WAF, DDoS védelem, bot szűrés
KliensCSP, Trusted Types, SameSite cookie
AlkalmazásValidáció, server action authz, rate limit
AdatRLS, oszlopszintű grant, titkosítás, backup
IdentitásMFA, rövid életű token, legkisebb jogosultság
DetekcióAudit napló, riasztás, anomália figyelés
HelyreállításPoint in time recovery, kulcsrotáció runbook

Zero trust (NIST SP 800-207) és BeyondCorp

A NIST SP 800-207 (2020) szerint a hálózati helyzet önmagában nem ad bizalmat. Minden hozzáférési kérést a kérő identitása, eszközállapota és a kontextus alapján, erőforrásonként és munkamenetenként bírálunk el. Az architektúra központi elemei a policy decision point (PDP), amely dönt, és a policy enforcement point (PEP), amely végrehajt.

A Google BeyondCorp (2014 óta publikált cikksorozat) ennek ipari megvalósítása. A belső alkalmazások nem VPN mögött vannak, hanem egy identity aware proxy mögött, amely a felhasználó és az eszköz állapota alapján enged be.

Kis csapatra fordítva.

Felhő közös felelősségi modell

RétegIaaS (AWS EC2)PaaS (Vercel, Supabase)SaaS (Stripe)
Fizikai, hálózat, hipervizorSzolgáltatóSzolgáltatóSzolgáltató
Operációs rendszer, patchÜgyfélSzolgáltatóSzolgáltató
Runtime, adatbázis motorÜgyfélSzolgáltatóSzolgáltató
Alkalmazás kódÜgyfélÜgyfélSzolgáltató
Konfiguráció (RLS, bucket policy, CORS)ÜgyfélÜgyfélÜgyfél (webhook, kulcsok)
Identitás, kulcsok, hozzáférésÜgyfélÜgyfélÜgyfél
Adat besorolás, backup stratégiaÜgyfélÜgyfél (PITR bekapcsolása)Ügyfél

A leggyakoribb felhős incidens a jól működő szolgáltatás rosszul beállított ügyféloldali része. Kikapcsolt RLS, publikus bucket, kiszivárgott kulcs. A Supabase dokumentációja ezt kifejezetten az ügyfél felelősségeként írja le.


8. Támadói modellek a védekezéshez

Lockheed Martin Cyber Kill Chain (2011)

Hutchins, Cloppert és Amin 2011-es cikke a katonai célzási láncot vitte át a behatolásokra. A hét fázis a felderítés, a fegyverkezés, a kézbesítés, a kihasználás, a telepítés, a vezérlő csatorna (command and control) és a cél végrehajtása. A gondolat lényege, hogy a védőnek elég egy láncszemet eltörni, ezért minden fázisra érdemes legalább egy észlelő vagy blokkoló kontrollt tervezni.

FázisWebes SaaS megfelelőÉszlelés vagy blokkolás
FelderítésVégpont felsorolás, subdomain keresésSzokatlan 404 arány riasztás, API leltár
FegyverkezésKihasználó kérés készítéseNem látjuk, ezért itt a patch a válasz
KézbesítésPhishing a fejlesztőnek, rosszindulatú csomagPhishing álló MFA, SCA
KihasználásInjekció, authz hibaWAF, validáció, RLS
TelepítésWebshell, új admin felhasználó, perzisztens tokenAudit napló új adminra, fájlintegritás
Command and controlKimenő kapcsolat ismeretlen hosztraEgress szűrés, DNS napló
CélAdatexport, titkosításExport mennyiség riasztás, backup

MITRE ATT&CK

Az ATT&CK (attack.mitre.org) valós incidensekből összegyűjtött tudásbázis a támadói taktikákról (miért) és technikákról (hogyan). Az Enterprise mátrix 14 taktikája a felderítés (Reconnaissance), az erőforrás fejlesztés, a kezdeti hozzáférés, a végrehajtás, a perzisztencia, a jogosultság emelés, a védelem kijátszás, a hitelesítő adat szerzés, a környezet feltérképezés (Discovery), a laterális mozgás, a gyűjtés, a vezérlő csatorna, a kiszivárogtatás és a hatás (Impact). Minden technikának azonosítója van (például T1078 Valid Accounts, T1190 Exploit Public-Facing Application, T1552 Unsecured Credentials).

Használat detekciós szabályokhoz lépésről lépésre.

  1. Kiválasztunk 5 és 10 közötti technikát, amelyek a mi rendszerünkre a legvalószínűbbek. Egy SaaS-nél jellemzően T1078 (érvényes fiók visszaélés), T1190, T1552 (kódban vagy CI-ban hagyott titok), T1098 (fiók manipuláció, például új admin), T1530 (adat felhő tárolóból), T1567 (kiszivárogtatás webszolgáltatásra).
  2. Minden technikához leírjuk, milyen naplóforrás látná (auth napló, audit tábla, Storage napló, GitHub audit log).
  3. Ha nincs ilyen forrás, az a következő naplózási feladat.
  4. Detekciós szabályt írunk, lehetőleg Sigma formátumban, ami több SIEM-re fordítható.
  5. A szabályt szándékos, ártalmatlan kiváltással teszteljük (Atomic Red Team jellegű teszt), és mérjük, megjelenik-e a riasztás.
  6. Lefedettségi táblát vezetünk (ATT&CK Navigator), amelyen látszanak a vakfoltok.
# Sigma jellegű szabály: új admin szerepkör kiosztása munkaidőn kívül (T1098).
title: Admin szerepkor kiosztas munkaidon kivul
logsource: { product: app, service: audit }
detection:
  selection: { action: 'membership.role_change', new_role: 'owner' }
  timeframe_filter: { hour|lt: 7 }
  condition: selection and timeframe_filter
level: high
tags: [attack.persistence, attack.t1098]

A MITRE D3FEND a védekező technikák párja, és az ATT&CK technikákhoz rendel ellenintézkedéseket.


9. Naplózás és detekció

Mit naplózzunk (OWASP Logging Cheat Sheet alapján)

EseményMezők
Bejelentkezés siker és hiba, MFA eseményFelhasználó, IP, user agent, eredmény, módszer
Jelszó, email, MFA változás, fiók helyreállításKi, kié, honnan
Jogosultság változás (szerepkör, tagság, API kulcs)Végrehajtó, célpont, régi és új érték
Authz elutasítás (403, RLS által üres eredmény gyanús mintában)Felhasználó, erőforrás, művelet
Validációs hiba a bizalmi határonVégpont, hibatípus, minta nélkül
Érzékeny adat elérés, export, tömeges letöltésKi, mennyit, milyen szűrővel
Admin és támogatói műveletek, impersonationKi, kinek a nevében, indoklás
Konfiguráció és kulcs változásKi, mit, mikor
Fizetési események, webhook ellenőrzés hibájaEsemény ID, eredmény
Rate limit és kvóta túllépésKulcs, végpont

Amit soha nem naplózunk. Jelszó, session token, API kulcs, teljes kártyaszám, egészségügyi vagy egyéb különleges adat, teljes kérés törzs érzékeny végponton. A PII-t (email, IP) minimalizáljuk, és megőrzési időt rendelünk hozzá a GDPR szerint.

Formátum. Strukturált JSON (pino Node alatt), egységes mezőnevek (az OWASP Logging Vocabulary eseménynevei jó kiindulás, például authn_login_fail, authz_fail, user_updated), UTC időbélyeg, korrelációs azonosító, ami a kéréstől a háttér munkáig követhető. A naplóinjekció ellen a felhasználói értékeket mezőként, nem összefűzött szövegként írjuk.

// SEBEZHETŐ: szöveg összefűzés (sortörés injekció) és token a naplóban.
console.log(`login failed for ${email} token=${req.headers.get('authorization')}`);

// JAVÍTOTT: strukturált, minimalizált, redaktált.
const logger = pino({ redact: ['req.headers.authorization', 'req.headers.cookie', '*.password'] });
logger.warn({ event: 'authn_login_fail', user: hashEmail(email), ip, ua, reqId }, 'login failed');

Audit napló

Az audit napló a letagadhatatlanság eszköze (a STRIDE R betűje), ezért más szabályok vonatkoznak rá, mint az alkalmazás naplóra.

create table audit_log (
  id bigint generated always as identity primary key,
  at timestamptz not null default now(),
  actor uuid, tenant_id uuid, action text not null,
  target text, ip inet, meta jsonb not null default '{}'
);
alter table audit_log enable row level security;
-- Az alkalmazás csak beszúrhat, módosítani és törölni senki nem tud.
revoke update, delete, truncate on audit_log from authenticated, anon, service_role;
create policy "tenant admin olvashatja" on audit_log for select
  using (tenant_id in (select tenant_id from memberships where user_id = (select auth.uid()) and role = 'owner'));

Az írást security definer függvényen vagy szerveroldali kódon keresztül végezzük, és a napló másolatát rendszeresen egy külön fiókba vagy WORM tárolóba exportáljuk, hogy egy adatbázis szintű kompromittálás se törölhesse.

Riasztás

A riasztás három szabálya.

  1. Minden riasztás akcióra vezet. Ha egy riasztásra a válasz rendszeresen az, hogy nem kell semmit tenni, a szabályt hangoljuk vagy töröljük.
  2. Tünetre riasztunk, nem minden okra. Sok sikertelen login egy fiókra, új admin, tömeges export, kiugró költség, ismeretlen országból admin login.
  3. Van gazdája. Kis csapatban egy ügyeleti rotáció vagy legalább egy megnevezett felelős, és egy csatorna, ahová a riasztás befut (például dedikált Slack csatorna plus telefonos értesítés a kritikusakra).

Kezdő riasztáskészlet egy SaaS-hez.

RiasztásKüszöb (kiindulás)
Sikertelen login egy fiókra10 darab 10 perc alatt
Új owner vagy admin szerepkörMinden esetben
Service role kulcs használata ismeretlen IP-rőlMinden esetben, ha a szolgáltató naplózza
Export vagy letöltés mennyiség5-szöröse a felhasználó 30 napos átlagának
LLM vagy felhő költségNapi plafon 80 százaléka
Webhook aláírás hiba5 darab 10 perc alatt
Secret scanning találat (GitHub push protection)Minden esetben
RLS nélküli tábla éles sémábanMinden esetben

10. Incidenskezelés

NIST SP 800-61 3. revízió (2025 április)

A 2025 áprilisában véglegesített 3. revízió lecserélte a 2012-es Computer Security Incident Handling Guide-ot, és az incidenskezelést a CSF 2.0 profiljaként írja le. A korábbi négyfázisú életciklus (felkészülés, észlelés és elemzés, elszigetelés, felszámolás és helyreállítás, utólagos tevékenység) helyett a CSF 2.0 hat funkciója szervezi a tevékenységeket. A Govern, Identify és Protect a felkészülést fedi, a Detect, Respond és Recover a kezelést, és az egész egy folyamatos fejlesztési hurok, ahol a tanulságok minden funkcióba visszacsatolódnak. A részletes, gyorsan változó technikai útmutatás kikerült a dokumentumból, és a NIST online forrásaiban él tovább.

CSF 2.0 funkcióIncidenskezelési tartalom kis csapatban
GovernKi dönt, ki kommunikál, ki értesíti a hatóságot, kapcsolattartó lista, jogi és biztosítói kapcsolat
IdentifyEszközleltár, adatleltár (hol van személyes adat), kockázatok, tanulságok beépítése
ProtectMFA, legkisebb jogosultság, backup, a runbookok gyakorlása
DetectNaplók, riasztások, külső bejelentés csatorna (security.txt)
RespondTriázs, elszigetelés, felszámolás, kommunikáció, bizonyíték megőrzés
RecoverVisszaállítás, ellenőrzés, ügyfél tájékoztatás, utólagos elemzés

Általános incidens folyamat kis csapatra

  1. Észlelés és bejelentés. Bárki bejelentheti egy közös csatornán. A bejelentőt soha nem hibáztatjuk.
  2. Incidens vezető kijelölése. Egy ember dönt és koordinál, egy másik dokumentál. A vezető nem feltétlenül a legtapasztaltabb, hanem aki éppen tud fókuszálni.
  3. Idővonal napló nyitása. Minden lépés UTC időbélyeggel egy közös dokumentumba.
  4. Súlyosság besorolás. Érint-e személyes adatot, ügyféladatot, fizetést, rendelkezésre állást.
  5. Elszigetelés. A kár terjedésének megállítása (kulcs visszavonás, fiók zárolás, funkció lekapcsolás feature flaggel).
  6. Bizonyíték megőrzés. Naplók exportja a törlés vagy rotáció előtt.
  7. Felszámolás és helyreállítás. A gyökérok javítása, tiszta állapotból visszaállítás.
  8. Kommunikáció. Belső, ügyfél, hatóság, a szükséges határidőkön belül.
  9. Utólagos elemzés (blameless postmortem). 5 munkanapon belül, akciópontokkal és felelősökkel.

Runbook, kiszivárgott kulcs vagy titok

Kiváltó. GitHub secret scanning riasztás, gitleaks találat, a kulcs megjelent egy naplóban, képernyőképen vagy nyilvános repóban.

  1. Visszavonás azonnal, vizsgálat utána. A kulcsot rotáljuk a szolgáltatónál (Stripe dashboard, Supabase API keys, felhő IAM). Supabase alatt az új típusú secret kulcsok egyenként visszavonhatók. A régi JWT alapú service role kulcsnál a JWT secret rotációja minden munkamenetet érvénytelenít, ezt tudatosan vállaljuk.
  2. Az új kulcs telepítése a secret managerbe és a deploy környezetbe, újratelepítés.
  3. A régi kulcs használatának vizsgálata a szolgáltató naplóiban a kiszivárgás időpontjától (Stripe request log, Supabase API és Postgres napló, CloudTrail).
  4. Eltávolítás a git történetből (git filter-repo) csak a visszavonás után, mert a fork és a cache már őrizheti. A visszavonás a védelem, a törlés csak higiénia.
  5. Kár felmérése. Ha a kulccsal adathoz fértek hozzá, az adatszivárgás runbook indul.
  6. Gyökérok. Miért kerülhetett ki (hiányzó .gitignore, NEXT_PUBLIC_ előtag, debug napló), és mi akadályozza meg legközelebb (push protection, pre-commit gitleaks).

Runbook, feltört felhasználói vagy admin fiók

  1. Munkamenetek visszavonása. Supabase alatt az admin API-val a felhasználó kijelentkeztetése, refresh tokenek érvénytelenítése, szükség esetén a fiók tiltása (ban_duration).
  2. Jelszó és MFA visszaállítás egy ellenőrzött csatornán, a támadó által hozzáadott MFA eszközök és API kulcsok törlése.
  3. Perzisztencia keresése. Új tagok, meghívók, szerepkör változások, webhook célok, OAuth alkalmazások, email továbbítás az audit naplóban a kompromittálás kezdete óta.
  4. Hatókör. Milyen adatot látott vagy exportált a fiók az adott időszakban.
  5. Admin fióknál a teljes kulcsrotáció és a CI titkok ellenőrzése is.
  6. Ügyfél tájékoztatás és szükség esetén az adatszivárgás runbook.

Runbook, adatszivárgás és a 72 órás GDPR bejelentés

Az adatvédelmi oldal részletei az Adatbiztonság és adatvédelem kézikönyvben szerepelnek, itt az incidenskezelési menetrend a fókusz. A GDPR 33. cikke szerint az adatkezelő a személyes adatokat érintő adatvédelmi incidenst indokolatlan késedelem nélkül, lehetőleg a tudomásszerzéstől számított 72 órán belül bejelenti a felügyeleti hatóságnak (Magyarországon a NAIH). Kivétel, ha az incidens valószínűleg nem jár kockázattal az érintettek jogaira. A 34. cikk szerint magas kockázat esetén az érintetteket is értesíteni kell. Adatfeldolgozóként (például B2B SaaS az ügyfél adataival) az adatkezelőt kell késedelem nélkül értesíteni, és a szerződés általában rövidebb határidőt ír elő.

Idő a tudomásszerzéstőlTeendő
0 és 4 óra közöttElszigetelés, incidens vezető, idővonal, bizonyíték megőrzés, jogi kapcsolattartó bevonása
4 és 24 óra közöttHatókör becslés (milyen adat, hány érintett, mely ügyfelek), adatfeldolgozóként az adatkezelő ügyfelek értesítése
24 és 48 óra közöttKockázatértékelés az érintettekre, bejelentés vázlat (az incidens jellege, érintettek és rekordok köre, valószínű következmények, intézkedések, kapcsolattartó)
72 órán belülBejelentés a NAIH felé. Ha nem minden információ ismert, részletekben is beadható, a késés okát indokolni kell
UtánaÉrintettek értesítése magas kockázatnál, belső incidens nyilvántartás vezetése akkor is, ha nem kellett bejelenteni

A 72 óra a tudomásszerzéstől, nem a vizsgálat lezárásától számít. Ezért a runbookban előre kitöltött bejelentés sablon, a hatóság elérhetősége és a döntéshozó neve szerepel. Ha a szervezet a NIS2 hatálya alá tartozik, ott 24 órás korai figyelmeztetés és 72 órás bejelentés az irányadó a nemzeti szabályozás szerint.


11. Google, Building Secure and Reliable Systems (2020)

A könyv fő állítása, hogy a biztonság és a megbízhatóság ugyanannak a rendszertulajdonságnak két arca. Mindkettő a nehezen előre látható hibák és a rossz szándékú vagy hibázó emberek elleni tervezés. Az alábbi táblázat a legfontosabb módszereket kis csapatra fordítja.

MódszerLényegAlkalmazás kis projektben
Least privilegeMinden ember és szolgáltatás csak a feladatához szükséges jogot kapja, időben korlátozvaÉles DB-hez nincs állandó emberi írási jog, helyette migráció és ideiglenes jog
Zero touch prodÉles rendszerhez ember nem nyúl közvetlenül, csak automatizált, review-zott eszközön keresztülMinden éles változás PR és CI útján, a dashboardos kézi átírás kivétel
Multi party authorization (MPA)Érzékeny művelethez második ember jóváhagyása kellBranch protection kötelező review-val, éles adatbázis törléshez két ember
BreakglassVészhelyzeti hozzáférés, ami mindig naplózott és riasztLezárt admin fiók, használata azonnali riasztással és utólagos review-val
Design for understandabilityAz érthető rendszer biztonságosabb, mert a hibát észre lehet venniEgy helyen élő authz, kevés speciális eset, jól elnevezett invariánsok
Design for recoveryFeltételezzük, hogy baj lesz, és a helyreállítás útja előre megvanPITR bekapcsolva, kipróbált visszaállítás, kulcsrotáció runbook, verziózott konfig
Blast radius csökkentésEgy hiba vagy kompromittálás kis területet érintsenTenantonkénti izoláció, környezetenként külön kulcs és projekt, szűk scope-ú tokenek, feature flag lekapcsoláshoz
Graceful degradation, DoS tűrésTúlterheléskor a lényeg működjönRate limit, sorba állítás, nem kritikus funkciók lekapcsolhatósága
Biztonságos alapértelmezésű APIA könnyű út legyen a biztonságos útcreateServerClient a kódbázis alapja, az admin kliens nehezen elérhető, külön modulban

Tipikus hiba. A backup létezik, de soha senki nem próbálta visszaállítani. A design for recovery a visszaállítás gyakorlásával kezdődik, legalább negyedévente egy staging környezetbe.


12. Biztonsági tesztelés

MódszerMit találMikor futEszköz kis projektben
SASTVeszélyes kódminták a forrásbanMinden PRSemgrep (p/typescript, p/nextjs, p/secrets csomagok), CodeQL nyilvános repón
SCAIsmert sérülékenység a függőségekbenMinden PR és napontanpm audit --omit=dev, osv-scanner, Dependabot vagy Renovate, Socket a csomag reputációra
Secret scanningKódba vagy történetbe került titokPre-commit és PRgitleaks, GitHub push protection
IaC és konfigurációRossz felhő és konténer beállításPRtrivy config, checkov, Supabase Security Advisor
CI workflow scanVeszélyes GitHub Actions mintákPRzizmor, actionlint
DASTFutó alkalmazás sérülékenységei kívülrőlStaging deploy utánOWASP ZAP baseline vagy full scan, nuclei sablonok
FuzzingParser és határeset hibákKritikus parsereken folyamatosanfast-check property based teszt, Jazzer.js
Authz tesztBOLA, BFLA, tenant szivárgásMinden PRSaját integrációs teszt két tenanttal
PentestLáncolt, üzleti logikai hibákÉvente és nagy változás utánKülső cég, ASVS alapú hatókörrel
Bug bountyFolyamatos külső szemekÉrett termék, kapacitás a válaszraElőbb VDP (bejelentési csatorna), utána fizetős program

Minimális CI biztonsági pipeline

name: security
on: [pull_request]
permissions: { contents: read }
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<sha> # v4
        with: { fetch-depth: 0, persist-credentials: false }
      - name: Secret scan
        uses: gitleaks/gitleaks-action@<sha> # v2
      - name: SAST
        run: pipx run semgrep scan --config p/typescript --config p/nextjs --config p/secrets --error
      - name: SCA
        run: |
          npm ci --ignore-scripts
          npm audit --omit=dev --audit-level=high
          go run github.com/google/osv-scanner/v2/cmd/osv-scanner@latest scan source -r .
      - name: Workflow scan
        run: pipx run zizmor .github/workflows
# Staging DAST, deploy után
  dast:
    runs-on: ubuntu-latest
    steps:
      - uses: zaproxy/action-baseline@<sha> # v0.14
        with: { target: 'https://staging.pelda.hu', fail_action: true }

Tipikus hibák.


13. NIST CSF 2.0 és CIS Controls v8.1 IG1 kis szervezetre

NIST Cybersecurity Framework 2.0 (2024 február)

A CSF 2.0 hat funkciója kis szervezetre fordítva, a NIST kisvállalati gyorsútmutatóinak szellemében.

FunkcióKérdésKis szervezetben konkrétan
Govern (új 2.0-ban)Ki felel, mi a kockázati étvágy, milyen szabályok kötnek?Egy megnevezett biztonsági felelős, egyoldalas biztonsági politika, beszállítói lista (Supabase, Vercel, Stripe, LLM), GDPR szerepek
IdentifyMink van és mi a fontos?Eszköz, fiók, SaaS és adatleltár, kockázatlista a fenyegetésmodellből
ProtectHogyan védjük?MFA mindenhol, jelszókezelő, frissítések, legkisebb jogosultság, titkosítás, backup, képzés
DetectHogyan vesszük észre?Riasztások, naplók, secret scanning, külső bejelentés csatorna
RespondMit teszünk, ha baj van?Runbookok, kapcsolattartó lista, kommunikációs sablonok
RecoverHogyan állunk talpra?Kipróbált visszaállítás, ügyfél kommunikáció, tanulságok

A CSF 2.0 profilokkal (jelenlegi és cél állapot) és szintekkel (Tier 1 és 4 között) dolgozik. Kis szervezetnek elég egy táblázat a hat funkcióval, a jelenlegi és a cél állapottal, és a következő negyedév három legfontosabb lépésével.

CIS Controls v8.1 (2024) és az IG1

A CIS Controls 18 kontrollcsaládból álló, priorizált védelmi lista. Az implementációs csoportok (IG1, IG2, IG3) a szervezet méretéhez és kockázatához igazodnak. Az IG1 az alapvető kiberhigiénia, a v8.1-ben 56 safeguard, amit minden szervezetnek érdemes teljesítenie. A v8.1 a CSF 2.0-hoz igazodva Govern jellegű safeguardokat is kiemel.

Az IG1 lényege fejlesztő cégre fordítva.

CIS kontrollIG1 lényegGyakorlatban
1 EszközleltárMinden gép ismertLista a céges laptopokról
2 SzoftverleltárEngedélyezett szoftverekSaaS lista, SBOM a termékről
3 AdatvédelemAdatleltár, kezelés, törlésHol van személyes adat, megőrzési idő
4 Biztonságos konfigurációAlapbeállítások, képernyőzár, tűzfalMDM vagy ellenőrzőlista, lemeztitkosítás
5 FiókkezelésFiókleltár, egyedi jelszavak, inaktív fiókok tiltásaKilépéskor azonnali fiókzárás minden SaaS-ben
6 Hozzáférés kezelésMFA, hozzáférés adása és elvétele folyamattalMFA kötelező GitHub, Supabase, Vercel, Stripe, Google fiókon
7 Sérülékenység kezelésPatch folyamatAutomatikus OS frissítés, Dependabot
8 Napló kezelésNaplózás bekapcsolva és megőrizveAudit log a SaaS eszközökben bekapcsolva
9 Email és böngésző védelemTámogatott böngésző, DNS szűrésSPF, DKIM, DMARC a domainre
10 Kártevő védelemVédelem minden gépenBeépített védelem bekapcsolva, frissítve
11 Adat helyreállításAutomatikus, védett backup, tesztPITR, offsite másolat, negyedéves visszaállítás teszt
14 Biztonsági tudatosságKépzésPhishing és titokkezelési képzés évente
15 Szolgáltató kezelésBeszállítói leltárDPA a beszállítókkal, SOC 2 jelentések begyűjtése
17 Incidens válaszFelelős, kapcsolattartók, bejelentési folyamatA fenti runbookok

A kimaradó kontrollok (12 hálózati infrastruktúra, 13 hálózati monitorozás, 16 alkalmazásbiztonság, 18 pentest) az IG2 és IG3 szinten hangsúlyosabbak, de egy szoftvercégnél a 16-os (alkalmazásbiztonság) az IG1 mellett is a fő terep, és ezt a kézikönyv többi része fedi.


A gyakorlatban

Az alábbi szabályok minden fejlesztési munkamenetre érvényesek, külön kérés nélkül, akár ember írja a kódot, akár egy kódgeneráló modell. Céljuk, hogy a kód első változata is a biztonságos alapértelmezést kövesse, és a kockázatos döntés láthatóvá váljon.

5 perces fenyegetésmodell minden új funkciónál

Kódírás előtt a fejlesztő röviden, a ticketben vagy a PR leírásban megválaszol négy kérdést. AI asszisztált fejlesztésnél ugyanezt az asszisztens válaszától is elvárjuk, mielőtt kódot generál.

  1. Min dolgozunk. Egy mondat a funkcióról és a bizalmi határokról, amelyeket keresztez (böngésző és szerver, felhasználó és tenant, saját rendszer és harmadik fél, kód és LLM).
  2. Mi romolhat el. STRIDE gyorskör a határt keresztező adatfolyamokra. Legalább az S, I és E betű sorra kerül, a kiszámíthatóság és a parsing kérdésével együtt.
  3. Mit teszünk ellene. Fenyegetésenként egy konkrét kontroll, ami bekerül a kódba vagy a teendők listájába.
  4. Mit nem csinálunk meg most. Az elfogadott vagy elhalasztott kockázatok kimondva, hogy a felelős (tech lead, termékgazda) tudatosan dönthessen róluk.

Ha a funkció auth, fizetés, fájl, URL lekérés, külső webhook, admin jogosultság vagy LLM eszközhívás területét érinti, a modell mélyebb, 15 perces változata készül el, és erről a csapat vezetője is tud.

Tiltott minták a kódban

MintaHelyette
String összefűzés SQL-ben, sql.unsafe, execute format('%s')Paraméterezés, %L és %I, using
dangerouslySetInnerHTML sanitizálás nélkül, rehype-rawDOMPurify, alapértelmezett markdown renderelés
Math.random() tokenhez, azonosítóhozcrypto.randomUUID(), randomBytes()
Service role vagy secret kulcs kliens oldali kódban, NEXT_PUBLIC_ előtaggalserver-only modul, környezeti változó csak szerveren
Új tábla RLS nélkülenable row level security és policy ugyanabban a migrációban
Authz csak middleware-benEllenőrzés az adatelérés mellett
catch { return true } vagy elnyelt hiba jogosultsági döntésnélFail closed, naplózott hiba
CORS origin visszatükrözés, * credentials mellettExplicit allowlist
fetch(userUrl) ellenőrzés nélkülSSRF védett lekérő
redirect(searchParams.next)Relatív útvonal allowlist
Webhook aláírás ellenőrzés nélkülAláírás, nyers törzs, idempotencia
Kliens által küldött ár, szerepkör, tenant azonosítóSzerveroldali forrás
exec shell stringgelexecFile argumentumtömbbel
Titok, token, jelszó a naplóbanRedakció
GitHub Action taggel, pull_request_target kódkivétellel, write-allSHA pin, pull_request, minimális permissions

A lista akkor működik igazán, ha nem csak review közben él. Semgrep szabályként vagy linter konfigurációként a CI-ban, AI asszisztált fejlesztésnél pedig a projekt szintű utasítások között is szerepel, így a generált kód első változata sem tartalmazza ezeket a mintákat.

Függőség hozzáadás szabálya (slopsquatting ellen)

Új csomag bevezetése előtt a csapat ellenőrzi, hogy a csomag létezik a registryben, és megnézi a korát, a heti letöltésszámát, a karbantartót és a forrásrepót (npm view <csomag> time.created repository maintainers). Fiatal, kevés letöltésű vagy egy ismert csomag elgépelésére emlékeztető nevű csomag nem kerül telepítésre, helyette jelzés és emberi review következik. Egy AI asszisztens által javasolt vagy emlékezetből felidézett csomagnév önmagában nem számít ellenőrzöttnek. Ahol a standard könyvtár vagy egy már meglévő függőség megoldja a feladatot, nem hozunk be újat.

Biztonsági review checklist

Minden bizalmi határt érintő változtatás végén a szerző (ember vagy AI asszisztens) végigmegy ezen a listán, és a PR leírásban röviden jelzi az eredményt.

Deploy előtti kapu

Élesítés előtt az alábbi kapuk futnak, automatikusan a CI-ban vagy kézi ellenőrzéssel. Ha bármelyik bukik, nincs deploy, és a bukás oka dokumentálva visszakerül a csapathoz.

KapuParancs vagy ellenőrzésBukás feltétele
Titkokgitleaks detect --redactBármilyen találat
SASTsemgrep scan --config p/typescript --config p/nextjs --errorMagas súlyosságú találat
Függőségeknpm audit --omit=dev --audit-level=high, osv-scanner scan source -r .Magas vagy kritikus, javítás elérhető
RLSselect tablename from pg_tables where schemaname='public' and not rowsecurity és Supabase advisorNem üres eredmény, hiba szintű advisor jelzés
Authz tesztekA két tenantos integrációs tesztcsomagBármilyen bukás
MigrációA migráció review-zva, visszaállítási út ismertDestruktív lépés terv nélkül
HeaderekZAP baseline vagy curl -I ellenőrzés stagingenHiányzó CSP, HSTS, nosniff
CI workflowzizmor .github/workflowsMagas súlyosságú találat
HelyreállításPITR bekapcsolva, utolsó visszaállítási teszt dátuma ismertNincs backup vagy sosem tesztelték

Csapatszabályok

Á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.

Források