\section{HTTP Cookies} \begin{frame}{Mi az a Cookie?} \begin{block}{HTTP Cookie definíció} Kis méretű adat, amit a szerver küld a böngészőnek, és automatikusan vissza küldődik. \end{block} \begin{itemize} \item \textbf{Méret:} Max 4KB \item \textbf{Tárolás:} Böngésző \item \textbf{Élettartam:} Beállítható \item \textbf{Használat:} \begin{itemize} \item Session kezelés \item Felhasználói preferenciák \item Tracking \end{itemize} \end{itemize} \begin{exampleblock}{Authentikációban} Cookie-k ideálisak session ID és token tárolására. \end{exampleblock} \end{frame} \begin{frame}[fragile]{Cookie beállítása} \begin{block}{Set-Cookie HTTP header} A szerver a \texttt{Set-Cookie} header segítségével küldi a cookie-t a kliensnek. \end{block} \vspace{0.3cm} \begin{columns} \begin{column}{0.48\textwidth} \textbf{HTTP válasz:} \small \begin{verbatim} HTTP/1.1 200 OK Set-Cookie: sessionId=abc123 Set-Cookie: theme=dark Content-Type: application/json {"success": true} \end{verbatim} \end{column} \begin{column}{0.48\textwidth} \textbf{Node.js/Express:} \small \begin{verbatim} res.cookie('sessionId', 'abc123'); res.cookie('theme', 'dark', { maxAge: 900000, httpOnly: true }); \end{verbatim} \end{column} \end{columns} \vspace{0.5cm} \begin{itemize} \item A böngésző automatikusan eltárolja a cookie-t \item Minden későbbi kérésben a böngésző automatikusan visszaküldi \end{itemize} \end{frame} \begin{frame}[fragile]{Cookie olvasása} \begin{block}{Cookie HTTP header} A böngésző minden kérésben visszaküldi a releváns cookie-kat. \end{block} \vspace{0.3cm} \begin{columns} \begin{column}{0.48\textwidth} \textbf{HTTP kérés:} \small \begin{verbatim} GET /api/user HTTP/1.1 Host: example.com Cookie: sessionId=abc123; theme=dark \end{verbatim} \end{column} \begin{column}{0.48\textwidth} \textbf{Node.js/Express:} \small \begin{verbatim} // cookie-parser middleware const cookieParser = require('cookie-parser'); app.use(cookieParser()); // Cookie olvasás app.get('/api/user', (req, res) => { const sessionId = req.cookies.sessionId; const theme = req.cookies.theme; // ... }); \end{verbatim} \end{column} \end{columns} \end{frame} \begin{frame}{Cookie típusok} \begin{columns} \begin{column}{0.48\textwidth} \begin{block}{Session Cookie} \begin{itemize} \item Nincs Expires/Max-Age \item Böngésző bezárásakor törlődik \end{itemize} \textbf{Használat:} \begin{itemize} \item Ideiglenes session \end{itemize} \end{block} \end{column} \begin{column}{0.48\textwidth} \begin{block}{Persistent Cookie} \begin{itemize} \item Van Expires/Max-Age \item Megmarad újraindítás után \end{itemize} \textbf{Használat:} \begin{itemize} \item "Remember me" \item Preferenciák \end{itemize} \end{block} \end{column} \end{columns} \begin{exampleblock}{Példa} \texttt{Set-Cookie: sessionId=abc123; Max-Age=3600} \end{exampleblock} \end{frame} \begin{frame}[shrink=5]{Cookie attribútumok} \begin{enumerate} \item \textbf{Expires / Max-Age} \begin{itemize} \item Expires: Dátum, Max-Age: mper \end{itemize} \item \textbf{Domain} \begin{itemize} \item Mely domain-ekhez küldje \end{itemize} \item \textbf{Path} \begin{itemize} \item Mely URL path-okhoz \end{itemize} \item \textbf{Secure} \begin{itemize} \item Csak HTTPS-en \end{itemize} \end{enumerate} \end{frame} \begin{frame}{Biztonsági attribútumok} \begin{alertblock}{HttpOnly} \begin{itemize} \item JavaScript NEM férhet hozzá \item \textbf{XSS védelem!} \end{itemize} \end{alertblock} \begin{alertblock}{Secure} \begin{itemize} \item Csak HTTPS-en \item \textbf{MITM védelem!} \end{itemize} \end{alertblock} \begin{alertblock}{SameSite} \begin{itemize} \item Strict - Csak same-site \item Lax - GET same-site \item None - Mindenhol (Secure kötelező!) \item \textbf{CSRF védelem!} \end{itemize} \end{alertblock} \end{frame} \begin{frame}[fragile]{Biztonságos cookie beállítás} \begin{block}{Production környezetben ajánlott beállítások} \small \begin{verbatim} res.cookie('token', jwtToken, { httpOnly: true, // XSS védelem - JS nem férhet hozzá secure: true, // Csak HTTPS - production-ben! sameSite: 'strict', // CSRF védelem maxAge: 3600000, // 1 óra (milliszekundumban) path: '/', // Teljes alkalmazásban elérhető domain: '.example.com' // Domain és aldomain-ek }); \end{verbatim} \end{block} \vspace{0.5cm} \begin{alertblock}{Kritikus!} Authentikációs token-ek mindig legyenek \texttt{httpOnly}, \texttt{secure} és \texttt{sameSite} attribútumokkal ellátva! \end{alertblock} \end{frame} \begin{frame}[shrink=10]{SameSite részletesen} \begin{block}{CSRF védelem cookie szinten} SameSite határozza meg, cookie mikor küldhető cross-site kérésekben. \end{block} \begin{enumerate} \item \textbf{Strict} \begin{itemize} \item Csak same-site \item Legjobb védelem \end{itemize} \item \textbf{Lax} \begin{itemize} \item Top-level GET küldődik \item POST/PUT/DELETE nem \end{itemize} \item \textbf{None} \begin{itemize} \item Mindenhol \item Securekel kötelező! \end{itemize} \end{enumerate} \end{frame} \begin{frame}[fragile]{SameSite példák} \begin{exampleblock}{Példa forgatókönyv} \texttt{bank.com} oldalon be vagy jelentkezve cookie-val. Megnyitsz egy \texttt{evil.com} oldalt, ami próbál kérést küldeni \texttt{bank.com}-ra. \end{exampleblock} \vspace{0.3cm} \begin{columns} \begin{column}{0.48\textwidth} \textbf{SameSite=Strict:} \small \begin{verbatim} Set-Cookie: session=xyz; SameSite=Strict \end{verbatim} \begin{itemize} \item \textcolor{red}{$\times$} evil.com $\to$ bank.com POST \item \textcolor{red}{$\times$} evil.com link $\to$ bank.com \item \textcolor{green}{$\checkmark$} bank.com $\to$ bank.com \end{itemize} \end{column} \begin{column}{0.48\textwidth} \textbf{SameSite=Lax:} \small \begin{verbatim} Set-Cookie: session=xyz; SameSite=Lax \end{verbatim} \begin{itemize} \item \textcolor{red}{$\times$} evil.com $\to$ bank.com POST \item \textcolor{green}{$\checkmark$} evil.com link $\to$ bank.com GET \item \textcolor{green}{$\checkmark$} bank.com $\to$ bank.com \end{itemize} \end{column} \end{columns} \end{frame} \begin{frame}[fragile,shrink=10]{Cookie törlése} \begin{block}{Logout - cookie invalidálás} \small \begin{verbatim} app.post('/api/logout', (req, res) => { res.clearCookie('token', { httpOnly: true, secure: true, sameSite: 'strict' }); res.json({ message: 'Logged out' }); }); \end{verbatim} \end{block} \end{frame} \begin{frame}[shrink=10]{Cookie biztonság - XSS vs CSRF} \begin{columns} \begin{column}{0.48\textwidth} \begin{alertblock}{XSS} \textbf{Támadás:} \begin{itemize} \item JS injektálás \item Cookie ellopás \end{itemize} \textbf{Védelem:} \begin{itemize} \item HttpOnly \item Input validáció \item CSP \end{itemize} \end{alertblock} \end{column} \begin{column}{0.48\textwidth} \begin{alertblock}{CSRF} \textbf{Támadás:} \begin{itemize} \item Jogosulatlan kérés \item Automatikus cookie \end{itemize} \textbf{Védelem:} \begin{itemize} \item SameSite \item CSRF token \item Origin check \end{itemize} \end{alertblock} \end{column} \end{columns} \begin{block}{Kombinált} HttpOnly + Secure + SameSite + CSRF token \end{block} \end{frame} \begin{frame}[fragile,shrink=10]{CSRF token} \begin{block}{Double submit pattern} \small \begin{verbatim} const csrf = require('csurf'); const csrfProtection = csrf({ cookie: { httpOnly: true } }); app.use(csrfProtection); app.get('/api/csrf-token', (req, res) => { res.json({ csrfToken: req.csrfToken() }); }); app.post('/api/transfer', (req, res) => { // CSRF valid automatikus }); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile,shrink=10]{Cookie és CORS} \begin{block}{Cross-Origin} Cookie-k alapból NEM küldődnek cross-origin-ben. \end{block} \begin{columns} \begin{column}{0.48\textwidth} \textbf{Szerver:} \small \begin{verbatim} app.use(cors({ origin: 'https://frontend.com', credentials: true })); \end{verbatim} \end{column} \begin{column}{0.48\textwidth} \textbf{Kliens:} \small \begin{verbatim} fetch('/api/user', { credentials: 'include' }) \end{verbatim} \end{column} \end{columns} \begin{alertblock}{Fontos!} credentials: true + 'include' + SameSite=None; Secure \end{alertblock} \end{frame} \begin{frame}[shrink=10]{Cookie vs localStorage vs sessionStorage} \begin{small} \begin{tabular}{|l|c|c|c|} \hline & \textbf{Cookie} & \textbf{localStorage} & \textbf{sessionStorage} \\ \hline Kapacitás & 4KB & 5-10MB & 5-10MB \\ \hline Lejárat & Beállítható & Nincs & Tab bezárás \\ \hline HTTP-ben & \textcolor{green}{Igen} & \textcolor{red}{Nem} & \textcolor{red}{Nem} \\ \hline XSS védelem & HttpOnly & \textcolor{red}{Nem} & \textcolor{red}{Nem} \\ \hline \end{tabular} \end{small} \begin{exampleblock}{Ajánlás} \textbf{Érzékeny:} HttpOnly Cookie, \textbf{Publikus:} localStorage \end{exampleblock} \end{frame} \begin{frame}[fragile,shrink=10]{Signed Cookies} \begin{block}{Integritás védelem} HMAC aláírással elliatott cookie, megakadályozza manipulációt. \end{block} \begin{verbatim} const cookieParser = require('cookie-parser'); app.use(cookieParser('secret-key')); app.get('/set', (req, res) => { res.cookie('userId', '12345', { signed: true }); res.send('Set'); }); app.get('/get', (req, res) => { const userId = req.signedCookies.userId; res.json({ userId }); }); \end{verbatim} \end{frame} \begin{frame}[shrink=5]{Cookie best practices} \begin{block}{Authentikációs cookie-k} \begin{enumerate} \item \textbf{HttpOnly:} JS nem fér hozzá \item \textbf{Secure:} Csak HTTPS \item \textbf{SameSite:} Strict/Lax \item \textbf{Rövid Max-Age:} 15-60 perc \item \textbf{Path:} Csak szükséges \item \textbf{Signed cookie:} Manipuláció ellen \end{enumerate} \end{block} \begin{alertblock}{Production template} \small \texttt{res.cookie('token', value, \{httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600000\})} \end{alertblock} \end{frame} \begin{frame}[shrink=5]{\u00d6sszefoglalás} \begin{itemize} \item \textbf{Cookie} = Kis adat szerver és kliens között \item \textbf{Típusok:} Session (ideiglenes) vs Persistent \item \textbf{Attribútumok:} Expires, Domain, Path, Secure, HttpOnly, SameSite \item textbf{HttpOnly:} XSS védelem \item \textbf{Secure:} HTTPS - MITM védelem \item \textbf{SameSite:} CSRF védelem \item \textbf{CORS:} credentials: true + 'include' \item \textbf{Biztonság:} HttpOnly + Secure + SameSite \end{itemize} \begin{exampleblock}{Authentikációban} Cookie a \textbf{legbiztonságosabb} módszer token tárolására. \end{exampleblock} \end{frame}