diff --git a/Backend_ppt/authn_z/authn.tex b/Backend_ppt/authn_z/authn.tex new file mode 100644 index 0000000..72b65fe --- /dev/null +++ b/Backend_ppt/authn_z/authn.tex @@ -0,0 +1,205 @@ +\section{Authentikáció} + +\begin{frame}{Mi az authentikáció?} + \begin{block}{Definíció} + Folyamat, amely során a rendszer ellenőrzi egy felhasználó identitását. + \end{block} + + \begin{itemize} + \item \textbf{Kérdés:} Ki vagy? + \item \textbf{Cél:} Felhasználó identitásának megállapítása + \item \textbf{Eredmény:} Sikeres vagy sikertelen azonosítás + \end{itemize} + + \begin{alertblock}{Fontos!} + Authentikáció $\neq$ Autorizáció! + \end{alertblock} +\end{frame} + +\begin{frame}{Authentikáció vs. Autorizáció} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{Authentikáció} + \begin{itemize} + \item Ki vagy? + \item Bejelentkezés + \item Példa: Jelszó ellenőrzés + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{Autorizáció} + \begin{itemize} + \item Mit tehetsz? + \item Jogosultságok + \item Példa: Admin jogosultság + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \begin{center} + \texttt{Authentikáció} $\rightarrow$ \texttt{Autorizáció} + \end{center} +\end{frame} + +\begin{frame}{Authentikációs módszerek} + \begin{enumerate} + \item \textbf{Tudás alapú} + \begin{itemize} + \item Példa: jelszó, PIN kód + \end{itemize} + + \item \textbf{Tulajdon alapú} + \begin{itemize} + \item Példa: okostelefon, hardver token + \end{itemize} + + \item \textbf{Inherencia alapú} + \begin{itemize} + \item Példa: ujjlenyomat, arcfelismerés + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{Többfaktoros authentikáció (MFA)} + \begin{block}{Multi-Factor Authentication} + Két vagy több független faktor kombinációja. + \end{block} + + \begin{itemize} + \item \textbf{2FA példák:} + \begin{itemize} + \item Jelszó + SMS kód + \item Jelszó + Authenticator app + \item Jelszó + ujjlenyomat + \end{itemize} + + \item \textbf{Előnyök:} + \begin{itemize} + \item Magasabb biztonság + \item Védelem jelszó kompromittálódás esetén + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{Jelszó alapú authentikáció} + \begin{block}{Leggyakoribb módszer} + Felhasználónév + jelszó párossal történő azonosítás. + \end{block} + + \begin{itemize} + \item \textbf{Folyamat:} + \begin{enumerate} + \item Felhasználó megadja username + password + \item Rendszer összehasonlítja a hash-sel + \item Sikeres/sikertelen válasz + \end{enumerate} + + \item \textbf{Biztonság:} + \begin{itemize} + \item Soha ne tároljuk plain text-ben! + \item Erős hash (bcrypt, Argon2) + \item Salt minden jelszóhoz + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}{Token alapú authentikáció} + \begin{block}{Modern megközelítés} + Bejelentkezés után szerver tokent generál, kliens minden kéréshez csatolja. + \end{block} + + \begin{enumerate} + \item Bejelentkezés (username + password) + \item Szerver ellenőrzi + \item Token generálás + \item Kliens eltárolja (localStorage, cookie) + \item Token csatolása minden kéréshez + \item Szerver validálja + \end{enumerate} + + \begin{itemize} + \item \textbf{Típusok:} JWT, OAuth, API keys + \item \textbf{Előnyök:} Stateless, skálázható + \end{itemize} +\end{frame} + +\begin{frame}{Session alapú authentikáció} + \begin{block}{Hagyományos megközelítés} + Bejelentkezési információkat a szerver tárolja session-ökben. + \end{block} + + \begin{enumerate} + \item Bejelentkezés + \item Szerver session létrehozás + \item Session ID küldése cookie-ban + \item Kliens csatolja minden kéréshez + \item Szerver azonosítja a felhasználót + \end{enumerate} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Előnyök:} + \begin{itemize} + \item Biztonságosabb + \item Könnyű visszavonás + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Hátrányok:} + \begin{itemize} + \item Stateful + \item Nehezebb skálázás + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{OAuth 2.0 / OpenID Connect} + \begin{block}{Harmadik fél authentikáció} + Bejelentkezés külső szolgáltatókkal (Google, Facebook, GitHub). + \end{block} + + \begin{itemize} + \item \textbf{OAuth 2.0:} Autorizációs framework + \item \textbf{OpenID Connect:} Authentikációs réteg + + \item \textbf{Előnyök:} + \begin{itemize} + \item Nincs jelszó kezelés + \item Jobb UX + \item Biztonságos protokoll + \end{itemize} + + \item \textbf{Használat:} + \begin{itemize} + \item Social login + \item SSO megoldások + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[shrink=5]{Biztonsági megfontolások} + \begin{alertblock}{Gyakori sebezhetőségek} + \begin{itemize} + \item Gyenge jelszavak + \item Nincs rate limiting + \item Token kiszivárogtatás + \item Man-in-the-middle (nincs HTTPS) + \end{itemize} + \end{alertblock} + + \begin{block}{Best Practices} + \begin{itemize} + \item HTTPS mindig + \item Erős jelszó policy + \item Rate limiting + \item MFA + \item Token lejárati idő + \item CSRF védelem + \end{itemize} + \end{block} +\end{frame} diff --git a/Backend_ppt/authn_z/authn_z.pdf b/Backend_ppt/authn_z/authn_z.pdf new file mode 100644 index 0000000..2917393 Binary files /dev/null and b/Backend_ppt/authn_z/authn_z.pdf differ diff --git a/Backend_ppt/authn_z/authn_z.tex b/Backend_ppt/authn_z/authn_z.tex new file mode 100644 index 0000000..5d9af25 --- /dev/null +++ b/Backend_ppt/authn_z/authn_z.tex @@ -0,0 +1,25 @@ +\documentclass[usenames,dvipsnames,aspectratio=169]{beamer} +\usepackage{../common/webfejl} + +\title[Webtechnológia és webalkalmazás-fejlesztés - Auth]{Webtechnológia és webalkalmazás-fejlesztés - Auth} +\subtitle{Authentikáció és autorizáció} + +\begin{document} + +\begin{frame}[plain] + \titlepage + \logoalul +\end{frame} + +\input{authn.tex} +\input{authz.tex} +\input{hash.tex} +\input{cookies.tex} +\input{session.tex} +\input{jwt.tex} +\input{oauth2.tex} +\input{middlewares.tex} +\input{services.tex} + + +\end{document} \ No newline at end of file diff --git a/Backend_ppt/authn_z/authz.tex b/Backend_ppt/authn_z/authz.tex new file mode 100644 index 0000000..11ba815 --- /dev/null +++ b/Backend_ppt/authn_z/authz.tex @@ -0,0 +1,191 @@ +\section{Autorizáció} + +\begin{frame}{Mi az autorizáció?} + \begin{block}{Definíció} + Meghatározza, hogy egy azonosított felhasználó milyen erőforrásokhoz férhet hozzá és milyen műveleteket végezhet. + \end{block} + + \begin{itemize} + \item \textbf{Kérdés:} Mit tehetsz? + \item \textbf{Cél:} Jogosultságok szabályozása + \item \textbf{Eredmény:} Engedélyezett vagy tiltott művelet + \end{itemize} + + \begin{alertblock}{Fontos!} + Autorizáció az authentikáció után történik. + \end{alertblock} +\end{frame} + +\begin{frame}{Autorizációs modelljei} + \begin{enumerate} + \item \textbf{RBAC (Role-Based Access Control)} + \begin{itemize} + \item Szerepkörök alapján + \item Példa: Admin, Editor, User + \end{itemize} + + \item \textbf{ABAC (Attribute-Based)} + \begin{itemize} + \item Attribútum alapján (user, resource, context) + \item Példa: "Department = IT és 9-17 között" + \end{itemize} + + \item \textbf{ACL (Access Control List)} + \begin{itemize} + \item Erőforrás-szintű lista + \item Példa: fájlrendszer jogosultságok + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{RBAC - Szerepkör alapú} + \begin{block}{Lényege} + Felhasználók szerepköröket kapnak, ez határozza meg a jogosultságokat. + \end{block} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Előnyök:} + \begin{itemize} + \item Könnyű adminisztráció + \item Átlátható + \item Gyors ellenőrzés + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Hátrányok:} + \begin{itemize} + \item Skálázhatóság + \item Kevesebb rugalmasság + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{ABAC - Attribútum alapú} + \begin{block}{Lényege} + Hozzáférési szabályok attribútumok alapján. + \end{block} + + \begin{itemize} + \item \textbf{User:} szerepkör, osztály, beosztás + \item \textbf{Resource:} tulajdonos, típus + \item \textbf{Context:} idő, hely, eszköz + \end{itemize} + + \begin{exampleblock}{Példa szabály} + \texttt{(role=Manager) AND (resource.owner = user) AND (time < 18:00)} + \end{exampleblock} +\end{frame} + +\begin{frame}{ACL - Access Control List} + \begin{block}{Lényege} + Erőforrásonként tárolt lista a hozzáférési jogokról. + \end{block} + + \begin{itemize} + \item \textbf{Példa:} fájlrendszer (read, write, execute) + \item \textbf{Előny:} finém hozzáférés + \item \textbf{Hátrány:} nagy rendszereknelnehéz kezelni + \end{itemize} +\end{frame} + +\begin{frame}{Autorizáció webalkalm azásban} + \begin{block}{Tipikus folyamat} + Szerver minden kérésnél ellenőrzi a jogosultságot. + \end{block} + + \begin{enumerate} + \item Authnentikáció után szerepkör kiosztás + \item Tokenben vagy session-ben tárolás + \item Middleware ellenőrzi a hozzáférést + \item Nincs jog: \texttt{403 Forbidden} + \end{enumerate} + + \begin{alertblock}{HTTP státuszkód} + \texttt{401} = nincs auth, \texttt{403} = nincs jogosultság + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Autorizációs middleware (RBAC)} + \begin{exampleblock}{Egyszerű szerepkör ellenőrzés} + \tiny + \begin{verbatim} +const authorize = (...roles) => { + return (req, res, next) => { + const { role } = req.user; // pl. JWT-ből + if (!roles.includes(role)) { + return res.status(403).json({ error: 'Forbidden' }); + } + next(); + }; +}; + +app.get('/admin', auth, authorize('Admin'), (req, res) => { + res.json({ data: 'Admin content' }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Policy alapú autorizáció (ABAC)} + \begin{exampleblock}{Szabály központú hozzáférés} + \tiny + \begin{verbatim} +const canAccess = (user, resource) => { + return user.department === resource.department && + user.level >= resource.requiredLevel && + new Date().getHours() < 18; +}; + +app.get('/reports/:id', auth, async (req, res) => { + const report = await getReport(req.params.id); + if (!canAccess(req.user, report)) { + return res.status(403).json({ error: 'Forbidden' }); + } + res.json(report); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{Legkisebb jogosultság elve} + \begin{block}{Principle of Least Privilege} + Mindenki csak a feladatohoz szükséges minimális jogokat kapja. + \end{block} + + \begin{itemize} + \item Csökkenti a kockázatot + \item Korlátozza a hibák hatását + \item Egyszerűbb auditálás + \end{itemize} + + \begin{alertblock}{Gyakori hiba} + \textbf{Mindenki admin} = gyors fejlesztés, de veszélyes élesben! + \end{alertblock} +\end{frame} + +\begin{frame}{Autorizáció Best Practices} + \begin{itemize} + \item Minden érzékeny végpontot ellenőrizz + \item Middleware vagy policy réteg + \item Role/policy központi kezelés + \item Logolás + \item Admin funkciók külön kezelése + \item RBAC + ABAC kombináció + \end{itemize} +\end{frame} + +\begin{frame}{Összefoglalás - Autorizáció} + \begin{itemize} + \item Autorizáció = hozzáférések és jogosultságok kezelése + \item 401 vs 403: authentikáció hiánya vs jogosultság hiánya + \item Fő modellek: RBAC, ABAC, ACL + \item Middleware-ekkel megvalósítható a gyakorlatban + \item Least Privilege elv alkalmazása + \end{itemize} + + \vspace{0.5cm} + +\end{frame} \ No newline at end of file diff --git a/Backend_ppt/authn_z/cookies.tex b/Backend_ppt/authn_z/cookies.tex new file mode 100644 index 0000000..2a9ae6b --- /dev/null +++ b/Backend_ppt/authn_z/cookies.tex @@ -0,0 +1,166 @@ +\section{HTTP Cookies} + +\begin{frame}[shrink=15]{Mi az a Cookie?} + \begin{block}{HTTP Cookie definíció} + Kis méretű adat (max 4KB), amit szerver küld böngészőnek, automatikusan visszaküldődik. + \end{block} + \begin{itemize} + \item Session kezelés, preferenciák, tracking + \item Beállítható élettartam + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Cookie beállítása Express-ben} + \begin{verbatim} +// Egyszerű cookie +res.cookie('session', 'abc123'); + +// Opciókkal +res.cookie('user', 'john', { + maxAge: 900000, // 15 perc ms-ban + httpOnly: true, // JS nem fér hozzá + secure: true, // csak HTTPS + sameSite: 'strict' // CSRF védelem +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Cookie attribútumok} + \begin{small} + \begin{itemize} + \item \textbf{Domain}: \texttt{.example.com} - subdomain-ek + \item \textbf{Path}: \texttt{/api} - URL path scope + \item \textbf{Expires/Max-Age}: Lejárat ideje + \item \textbf{HttpOnly}: JS hozzáférés blokkolása (XSS védelem) + \item \textbf{Secure}: Csak HTTPS-en küldve + \item \textbf{SameSite}: \texttt{Strict|Lax|None} - CSRF védelem + \end{itemize} + \end{small} +\end{frame} + +\begin{frame}[shrink=15]{Cookie típusok} + \begin{enumerate} + \item \textbf{Session Cookie}: Nincs Expires/Max-Age, böngésző bezáráskor törlődik + \item \textbf{Persistent Cookie}: Van lejárati idő, túléli böngésző bezárást + \item \textbf{Secure Cookie}: Csak HTTPS-en + \item \textbf{HttpOnly Cookie}: JS nem fér hozzá + \item \textbf{SameSite Cookie}: CSRF támadás ellen + \item \textbf{Third-party Cookie}: Más domain-ről + \end{enumerate} +\end{frame} + +\begin{frame}[shrink=15]{SameSite Cookie attribútum} + \begin{description} + \item[Strict] Cookie csak saját site kérésekkel küldve. Legjobb CSRF védelem. + \item[Lax] GET kéréseknél küldi cross-site. Default modern böngészőkben. + \item[None] Mindig küldi (Secure kötelező). Third-party használathoz. + \end{description} + \begin{alertblock}{CSRF véd} + SameSite=Strict/Lax megakadályozza cross-site támadásokat. + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=15]{Cookie vs localStorage vs sessionStorage} + \begin{tiny} + \begin{tabular}{|l|c|c|c|} + \hline + & \textbf{Cookie} & \textbf{localStorage} & \textbf{sessionStorage} \\ + \hline + Kapacitás & 4KB & 5-10MB & 5-10MB \\ + Lejárat & Beállítható & Nincs & Tab bezárás \\ + HTTP-ben & Igen & Nem & Nem \\ + XSS véd & HttpOnly & Nem & Nem \\ + \hline + \end{tabular} + \end{tiny} + \begin{exampleblock}{Ajánlás} + \textbf{Érzékeny:} HttpOnly Cookie, \textbf{Publikus:} localStorage + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Cookie olvasása Express-ben} + \begin{verbatim} +const cookieParser = require('cookie-parser'); +app.use(cookieParser()); + +app.get('/profile', (req, res) => { + const sessionId = req.cookies.session; + res.json({ sessionId }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Cookie törlése} + \begin{verbatim} +app.post('/logout', (req, res) => { + res.clearCookie('session'); + res.json({ message: 'Logged out' }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Signed Cookies} + \begin{block}{Integritás védelem} + HMAC aláírással ellátott cookie, megakadályozza manipulációt. + \end{block} + \begin{verbatim} +app.use(cookieParser('secret-key')); + +app.get('/set', (req, res) => { + res.cookie('userId', '123', { signed: true }); + res.send('OK'); +}); + +app.get('/get', (req, res) => { + const id = req.signedCookies.userId; + res.json({ id }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{XSS vs CSRF} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{XSS (Cross-Site Scripting)} + \begin{itemize} + \item JS injection támadás + \item Cookie lopás \texttt{document.cookie} + \item \textbf{Védelem:} HttpOnly cookie + \end{itemize} + \end{column} + \begin{column}{0.48\textwidth} + \textbf{CSRF (Cross-Site Request Forgery)} + \begin{itemize} + \item Hamis kérés küldés + \item Cookie auto-send kihasználása + \item \textbf{Védelem:} SameSite, CSRF token + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile,shrink=20]{CSRF Token védelem} + \begin{verbatim} +const csrf = require('csurf'); +app.use(csrf({ cookie: true })); + +app.get('/form', (req, res) => { + res.render('form', { csrfToken: req.csrfToken() }); +}); + +app.post('/submit', (req, res) => { + res.send('Valid!'); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Cookie Security Best Practices} + \begin{enumerate} + \item \textbf{HttpOnly}: Érzékeny cookie-khoz mindig + \item \textbf{Secure}: Éles környezetben HTTPS-el + \item \textbf{SameSite}: Strict vagy Lax CSRF ellen + \item \textbf{Short expiration}: Session token-ekhez rövid lejárat + \item \textbf{Domain scope}: Csak szükséges domain-hez + \item \textbf{Signed cookies}: Integritás ellenőrzéshez + \end{enumerate} +\end{frame} diff --git a/Backend_ppt/authn_z/cookies_old.tex b/Backend_ppt/authn_z/cookies_old.tex new file mode 100644 index 0000000..af400dd --- /dev/null +++ b/Backend_ppt/authn_z/cookies_old.tex @@ -0,0 +1,470 @@ +\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} + diff --git a/Backend_ppt/authn_z/hash.tex b/Backend_ppt/authn_z/hash.tex new file mode 100644 index 0000000..ca083ef --- /dev/null +++ b/Backend_ppt/authn_z/hash.tex @@ -0,0 +1,153 @@ +\section{Hash} + +\begin{frame}{Mi az a hash?} + \begin{block}{Definíció} + Tetszőleges hosszúságú bemenetet rögzített méretű lenyomattá alakít. + \end{block} + + \begin{itemize} + \item Egyirányű függvény (nem visszafejthető) + \item Gyorsan számolható + \item Kis változás teljesen más hasht eredményez + \end{itemize} + + \begin{alertblock}{Fontos} + Hash $\neq$ titkosítás: nem visszafejthető! + \end{alertblock} +\end{frame} + +\begin{frame}{Hash tulajdonságok} + \begin{itemize} + \item \textbf{Deterministic}: azonos bemenet = azonos kimenet + \item \textbf{Preimage resistance}: nehéz visszafejteni + \item \textbf{Second preimage}: nehéz másik bemenetet találni + \item \textbf{Collision resistance}: nehéz két különböző bemenetet találni azonos hash-sel + \end{itemize} + + \begin{exampleblock}{Példa} + \texttt{hash("jelszo") = 5f4dcc3b5aa765d61d8327deb882cf99} + \end{exampleblock} +\end{frame} + +\begin{frame}{Gyenge vs. erős hash} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{Gyenge hash} + \begin{itemize} + \item MD5, SHA1 (elavult) + \item Gyors → brute force könnyebb + \item Ütközések ismertek + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{Erős hash} + \begin{itemize} + \item bcrypt, Argon2, scrypt + \item Lassú, konfigurálható + \item Salt + work factor támogatás + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \vspace{0.4cm} + + \begin{alertblock}{Jelszavakhoz} + Soha ne használj általános hash-t (MD5, SHA1) jelszavak tárolásához! + \end{alertblock} +\end{frame} + +\begin{frame}{Salt és Pepper} + \begin{block}{Miért fontos?} + \textbf{Salt:} véletlen érték, amit a jelszóhoz adunk hash előtt. + \end{block} + + \begin{itemize} + \item Védelem rainbow table ellen + \item Minden jelszónál egyedi salt + \item Salt-et tárolhatjuk adatbázisban + \end{itemize} + + \begin{exampleblock}{Pepper} + Közös titkos kulcs, szerver konfigban tárolva. + \end{exampleblock} +\end{frame} + +\begin{frame}{Password hashing algoritmusok} + \begin{itemize} + \item \textbf{bcrypt} + \begin{itemize} + \item Beépített salt és work factor + + \end{itemize} + \item \textbf{scrypt} + \begin{itemize} + \item Memória-igényes → GPU támadások ellen jobb + \end{itemize} + \item \textbf{Argon2} + \begin{itemize} + \item Modern szabvány (PHC winner) + \item Paraméterezhető (time, memory, parallelism) + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=10]{bcrypt használat (Node.js)} + \begin{exampleblock}{Hash készítés} + \tiny + \begin{verbatim} +const bcrypt = require('bcrypt'); +const saltRounds = 12; + +const hashPassword = async (password) => { + const salt = await bcrypt.genSalt(saltRounds); + return bcrypt.hash(password, salt); +}; + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Ellenőrzés} + \tiny + \begin{verbatim} +const isValid = await bcrypt.compare(password, storedHash); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{Hash-elés felhasználása} + \begin{itemize} + \item Jelszó tárolás + \item Fájl integritás ellenőrzés + \item Digitális aláírások + \item Cache kulcsok + \end{itemize} + + \begin{alertblock}{Megjegyzés} + Authentikációban: jelszó soha ne legyen visszaolvasható. + \end{alertblock} +\end{frame} + +\begin{frame}{Hash Best Practices} + \begin{itemize} + \item Jelszhoz \textbf{bcrypt/Argon2/scrypt} + \item Egyedi salt minden jelszóhoz + \item Work factor beállítása (pl. bcrypt 10-12) + \item Soha ne plain text + \item Rate limiting + lockout + \end{itemize} +\end{frame} + +\begin{frame}{Összefoglalás - Hash} + \begin{itemize} + \item Hash = egyirányú lenyomat + \item Jelszavakhoz speciális algoritmus kell + \item Salt és pepper növeli a biztonságot + \item Argon2 a legajánlottabb modern választás + \item Hash nem titkosítás! + \end{itemize} + + \vspace{0.5cm} + +\end{frame} \ No newline at end of file diff --git a/Backend_ppt/authn_z/jwt.tex b/Backend_ppt/authn_z/jwt.tex new file mode 100644 index 0000000..c7fe545 --- /dev/null +++ b/Backend_ppt/authn_z/jwt.tex @@ -0,0 +1,194 @@ +\section{JWT} + +\begin{frame}[shrink=15]{Mi az a JWT?} + \begin{block}{JSON Web Token (RFC 7519)} + Kompakt, önálló token információ biztonságos továbbításához. + \end{block} + \begin{itemize} + \item Digitálisan aláírt, Base64URL kódolt + \item Használat: authentikáció, stateless session + \item \textbf{NEM} titkosított - payload olvasható! + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=15]{JWT formátum} + \texttt{\textcolor{red}{HEADER}.\textcolor{purple}{PAYLOAD}.\textcolor{blue}{SIGNATURE}} + \begin{tiny} + \begin{verbatim} +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZ +SI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36P + \end{verbatim} + \end{tiny} + \begin{itemize} + \item \textcolor{red}{Header}: Algoritmus + token típus + \item \textcolor{purple}{Payload}: Claims (user adatok) + \item \textcolor{blue}{Signature}: Integritás védelem + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=15]{JWT Header \& Payload} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Header:} + \begin{verbatim} +{ + "alg": "HS256", + "typ": "JWT" +} + \end{verbatim} + \end{column} + \begin{column}{0.48\textwidth} + \textbf{Payload:} + \begin{verbatim} +{ + "sub": "1234", + "name": "John", + "iat": 1516239022, + "exp": 1516242622 +} + \end{verbatim} + \end{column} + \end{columns} + \begin{alertblock}{Fontos} + Ne tároljunk jelszót payload-ban! + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{JWT Signature} + \begin{verbatim} +HMACSHA256( + base64(header) + "." + base64(payload), + secret +) + \end{verbatim} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{HMAC:} Egy kulcs, gyors (HS256) + \end{column} + \begin{column}{0.48\textwidth} + \textbf{RSA:} Privát+publikus, biztonságosabb (RS256) + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile,shrink=20]{JWT generálás Node.js} + \begin{verbatim} +const jwt = require('jsonwebtoken'); + +const payload = { userId: user.id, role: user.role }; + +const token = jwt.sign( + payload, + process.env.JWT_SECRET, + { expiresIn: '1h' } +); + +res.json({ token }); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{JWT validálás Node.js} + \begin{verbatim} +const token = req.headers.authorization?.split(' ')[1]; + +if (!token) return res.status(401).json({ error: 'No token' }); + +try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); +} catch (err) { + res.status(403).json({ error: 'Invalid' }); +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{JWT middleware} + \begin{verbatim} +const authJWT = (req, res, next) => { + const token = req.headers.authorization?.split(' ')[1]; + if (!token) return res.status(401).send('Token required'); + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) return res.status(403).send('Invalid'); + req.user = user; + next(); + }); +}; + +app.get('/protected', authJWT, (req, res) => { + res.json({ data: 'Secret', user: req.user }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{JWT vs Session} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{JWT:} + \begin{itemize} + \item Stateless + \item Skálázható + \item Cross-domain + \item Nehéz visszavonni + \item Token méret + \end{itemize} + \end{column} + \begin{column}{0.48\textwidth} + \textbf{Session:} + \begin{itemize} + \item Stateful + \item Szerver tárolja + \item Könnyű visszavonás + \item Nehezebb skálázás + \item Kis cookie + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[shrink=15]{Access \& Refresh Token} + \begin{block}{Két token stratégia} + \textbf{Access token}: Rövid élet (15 perc), API hozzáférés\\ + \textbf{Refresh token}: Hosszú élet (7 nap), új access token generálás + \end{block} + \begin{enumerate} + \item Login $\to$ Access + Refresh token + \item API kérés Access token-nel + \item Access lejár $\to$ Refresh-el új Access-t kér + \item Refresh lejár $\to$ Újra login + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Refresh Token példa} + \begin{verbatim} +app.post('/refresh', (req, res) => { + const { refreshToken } = req.body; + if (!refreshToken) return res.sendStatus(401); + + jwt.verify(refreshToken, REFRESH_SECRET, (err, user) => { + if (err) return res.sendStatus(403); + + const accessToken = jwt.sign( + { userId: user.userId }, + ACCESS_SECRET, + { expiresIn: '15m' } + ); + res.json({ accessToken }); + }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{JWT Security Best Practices} + \begin{enumerate} + \item Rövid lejárat (15-60 perc) + \item Erős secret kulcs (min 256 bit) + \item HTTPS kötelező + \item Ne tároljunk érzékeny adatot payload-ban + \item Validáld mindig az \texttt{exp} és \texttt{iat} claim-eket + \item Használj RS256-ot HS256 helyett production-ben + \item Refresh token rotation + \item Token blacklist megfontolása + \end{enumerate} +\end{frame} diff --git a/Backend_ppt/authn_z/jwt_old.tex b/Backend_ppt/authn_z/jwt_old.tex new file mode 100644 index 0000000..e515ef0 --- /dev/null +++ b/Backend_ppt/authn_z/jwt_old.tex @@ -0,0 +1,549 @@ +\section{JWT} + +\begin{frame}{Mi az a JWT?} + \begin{block}{JSON Web Token (RFC 7519)} + Nyílt szabványú, kompakt és önálló módszer információ biztonságos továbbítására. + \end{block} + + \begin{itemize} + \item \textbf{Kompakt:} Kis méret, URL/header-ben is + \item \textbf{Önálló:} Minden információt tartalmaz + \item \textbf{Biztonságos:} Digitálisan aláírt + + \item \textbf{Használat:} + \begin{itemize} + \item Authentikáció + \item Információ csere + \item Stateless session + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=10]{JWT formátum} + \begin{block}{Három részből áll, ponttal elválasztva} + \texttt{\textcolor{red}{HEADER}.\textcolor{purple}{PAYLOAD}.\textcolor{blue}{SIGNATURE}} + \end{block} + + \begin{exampleblock}{Példa JWT} + \tiny + \begin{verbatim} +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZ +SI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36P +OkmLXOUqhfKJ4 + \end{verbatim} + \end{exampleblock} + + \begin{itemize} + \item \textcolor{red}{\textbf{Header:}} Token típusa és algoritmus + \item \textcolor{purple}{\textbf{Payload:}} Claims (állítások) - felhasználói adatok + \item \textcolor{blue}{\textbf{Signature:}} Aláírás - integritás ellenőrzés + \end{itemize} + + \begin{alertblock}{Fontos!} + A JWT Base64URL kódolású, \textbf{NEM} titkosított! A payload tartalma olvasható! + \end{alertblock} +\end{frame} + +\begin{frame}[fragile]{JWT Header} + \begin{block}{Token metaadatok} + Token típusa és aláírási algoritmus. + \end{block} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{JSON:} + \begin{verbatim} +{ + "alg": "HS256", + "typ": "JWT" +} + \end{verbatim} + + \begin{itemize} + \item \texttt{alg}: Algoritmus + \item \texttt{typ}: Token típus + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Kódolt:} + \begin{verbatim} +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 + \end{verbatim} + + \textbf{Algoritmusok:} + \begin{itemize} + \item HS256 - HMAC + \item RS256 - RSA + \item ES256 - ECDSA + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile,shrink=5]{JWT Payload (Claims)} + \begin{block}{Felhasználói adatok és meta} + Claims (allítoltak) a felhasználóról és tokenről. + \end{block} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Registered:} + \begin{itemize} + \item \texttt{iss} - Kibocsátó + \item \texttt{sub} - Tárgy/user ID + \item \texttt{aud} - Címzett + \item \texttt{exp} - Lejárat + \item \texttt{iat} - Kiállítás + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Példa:} + \small + \begin{verbatim} +{ + "sub": "1234567890", + "name": "John Doe", + "role": "admin", + "iat": 1516239022, + "exp": 1516242622 +} + \end{verbatim} + \end{column} + \end{columns} + + \begin{alertblock}{Figyelem!} + Ne tároljunk érzékeny adatokat (jelszó) payload-ban! + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=5]{JWT Signature} + \begin{block}{Digitális aláírás} + Biztosítja a token integritását és hitelességét. + \end{block} + + \textbf{Signature (HMAC SHA256):} + \begin{verbatim} +HMACSHA256( + base64UrlEncode(header) + "." + base64UrlEncode(payload), + secret +) + \end{verbatim} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{HMAC (Symmetric):} + \begin{itemize} + \item Egy kulcs + \item Gyorsabb + \item HS256 + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{RSA (Asymmetric):} + \begin{itemize} + \item Privát + publikus + \item Biztonságosabb + \item RS256 + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{JWT működése} + \begin enumerate} + \item \textbf{Bejelentkezés:} + \begin{itemize} + \item Username + password + \item Szerver validál + \end{itemize} + + \item \textbf{Token generálás:} + \begin{itemize} + \item JWT létrehozás user adatokkal + \item Aláírás titkos kulccsal + \end{itemize} + + \item \textbf{Tárolás:} + \begin{itemize} + \item localStorage / cookie + \end{itemize} + + \item \textbf{API kérések:} + \begin{itemize} + \item \texttt{Authorization: Bearer } + \item Szerver validálja + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=10]{JWT generálás Node.js-ben} + \begin{block}{jsonwebtoken könyvtár használata} + \small + \begin{verbatim} +const jwt = require('jsonwebtoken'); + +const payload = { + userId: user.id, + email: user.email, + role: user.role +}; + +const token = jwt.sign( + payload, + process.env.JWT_SECRET, + { expiresIn: '1h', issuer: 'myapp' } +); + +res.json({ token, expiresIn: 3600 }); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{JWT validálás Node.js-ben} + \begin{block}{Token ellenőrzés} + \small + \begin{verbatim} +const authHeader = req.headers.authorization; +const token = authHeader?.split(' ')[1]; + +if (!token) { + return res.status(401).json({ error: 'No token' }); +} + +try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); +} catch (error) { + return res.status(403).json({ error: 'Invalid' }); +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{JWT middleware Express-ben} + \begin{block}{Védett route-ok} + \small + \begin{verbatim} +const authenticateJWT = (req, res, next) => { + const token = req.headers.authorization?.split(' ')[1]; + + if (!token) return res.status(401).json({ error: 'Token required' }); + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) return res.status(403).json({ error: 'Invalid token' }); + req.user = user; + next(); + }); +}; + +app.get('/api/protected', authenticateJWT, (req, res) => { + res.json({ message: 'Protected', user: req.user }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}{JWT vs Session} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{JWT (Stateless)} + \textbf{Előnyök:} + \begin{itemize} + \item Skálázható + \item Nincs szerver-oldali tárolás + \item Cross-domain működés + \item Mobile-friendly + \end{itemize} + + \vspace{0.3cm} + + \textbf{Hátrányok:} + \begin{itemize} + \item Nehéz visszavonni + \item Token méret + \item XSS sebezhetőség (localStorage) + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{Session (Stateful)} + \textbf{Előnyök:} + \begin{itemize} + \item Azonnali visszavonás + \item Kis cookie méret + \item Biztonságosabb token tárolás + \end{itemize} + + \vspace{0.3cm} + + \textbf{Hátrányok:} + \begin{itemize} + \item Szerver-oldali memória + \item Nehezebb skálázás + \item CORS problémák + \item Session store szükséges + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \vspace{0.5cm} + + \begin{alertblock}{Melyiket válasszuk?} + Függ az alkalmazás követelményeitől: méret, skálázhatóság, biztonság, komplexitás. + \end{alertblock} +\end{frame} + +\begin{frame}{Token tárolás a kliens oldalon} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{localStorage} + \begin{itemize} + \item Egyszerű + \item JS hozzáférés + \item Megosztott + \end{itemize} + + \textcolor{red}{\textbf{XSS!}} + \begin{itemize} + \item \textcolor{red}{JS hozzáférhet} + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{HttpOnly Cookie} + \begin{itemize} + \item JS NEM fér hozzá + \item Automatikus + \item XSS védett + \end{itemize} + + \textcolor{red}{\textbf{CSRF}} + \begin{itemize} + \item \textcolor{red}{Védelem kell} + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \begin{exampleblock}{Ajánlás} + HttpOnly, Secure, SameSite cookie a legbiztonságosabb. + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile]{JWT tárolás HttpOnly Cookie-ban} + \begin{block}{Token beállítás cookie-ban} + \small + \begin{verbatim} +// Login endpoint +app.post('/api/login', (req, res) => { + // ... authentikáció logika ... + + const token = jwt.sign(payload, secret, { expiresIn: '1h' }); + + res.cookie('token', token, { + httpOnly: true, // JavaScript nem férhet hozzá + secure: true, // Csak HTTPS-en keresztül + sameSite: 'strict',// CSRF védelem + maxAge: 3600000 // 1 óra milliszekundumban + }); + + res.json({ success: true, message: 'Logged in' }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{JWT olvasás Cookie-ból} + \begin{block}{Cookie-parser middleware} + \small + \begin{verbatim} +const cookieParser = require('cookie-parser'); +app.use(cookieParser()); + +const authenticateJWT = (req, res, next) => { + // Token a cookie-ból + const token = req.cookies.token; + + if (!token) { + return res.status(401).json({ error: 'Not authenticated' }); + } + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); + } catch (error) { + return res.status(403).json({ error: 'Invalid token' }); + } +}; + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[shrink=5]{Refresh Token stratégia} + \begin{block}{Miért szükséges?} + Access tokenek rövidek (15 perc). Refresh tokennel új access token kérhető. + \end{block} + + \begin{itemize} + \item \textbf{Access Token:} + \begin{itemize} + \item Rövid (15-60 perc) + \item API kérésekhez + \end{itemize} + + \item \textbf{Refresh Token:} + \begin{itemize} + \item Hosszú (7-30 nap) + \item Új access tokenhez + \item HttpOnly cookie-ban + \item DB-ben tárolva + \end{itemize} + \end{itemize} + + \begin{alertblock}{Best Practice} + Refresh token rotation: minden refresh-nél új refresh token is. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile]{Refresh Token implementáció} + \begin{block}{Token refresh endpoint} + \small + \begin{verbatim} +app.post('/api/refresh', (req, res) => { + const refreshToken = req.cookies.refreshToken; + + if (!refreshToken) { + return res.status(401).json({ error: 'No refresh token' }); + } + + try { + // Refresh token validálás + const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET); + + // Ellenőrizzük az adatbázisban + const storedToken = await RefreshToken.findOne({ + token: refreshToken, + userId: decoded.userId + }); + + if (!storedToken) { + return res.status(403).json({ error: 'Invalid refresh token' }); + } + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Refresh Token implementáció (folyt.)} + \begin{block}{Új tokenek generálása} + \small + \begin{verbatim} + // Új access token + const accessToken = jwt.sign( + { userId: decoded.userId, role: decoded.role }, + process.env.JWT_SECRET, + { expiresIn: '15m' } + ); + + // Új refresh token (rotation) + const newRefreshToken = jwt.sign( + { userId: decoded.userId }, + process.env.REFRESH_SECRET, + { expiresIn: '7d' } + ); + + // Régi token törlése, új mentése az adatbázisba + await RefreshToken.deleteOne({ token: refreshToken }); + await RefreshToken.create({ token: newRefreshToken, userId: decoded.userId }); + + res.cookie('refreshToken', newRefreshToken, { httpOnly: true, ... }); + res.json({ accessToken }); + } catch (error) { + res.status(403).json({ error: 'Invalid refresh token' }); + } +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[shrink=5]{JWT biztonság best practices} + \begin{alertblock}{Kritikus pontok} + \begin{itemize} + \item \textbf{Titkos kulcs:} Erős, random, env-ben + \item \textbf{HTTPS:} Mindig! + \item \textbf{Lejárat:} Max 1 óra + \item \textbf{Érzeky adatok:} Ne JWT-ben + \item \textbf{Algoritmus:} Ellenőrizd (ne 'none') + \end{itemize} + \end{alertblock} + + \begin{block}{További ajánlások} + \begin{itemize} + \item HttpOnly cookie + \item Refresh token rotation + \item Rate limiting + \item Aud és Iss validálás + \end{itemize} + \end{block} +\end{frame} + +\begin{frame}{Gyakori JWT hibák és támadások} + \begin{alertblock}{Gyakori sebezhetőségek} + \begin{enumerate} + \item \textbf{None algoritmus támadás} + \begin{itemize} + \item Header: \texttt{"alg": "none"} - aláírás nélküli token + \item Védelem: Mindig ellenőrizzük az algoritmust + \end{itemize} + + \item \textbf{Gyenge titkos kulcs} + \begin{itemize} + \item Rövid vagy közismert kulcs + \item Védelem: Min. 256-bit random kulcs + \end{itemize} + + \item \textbf{Token kiszivárogtatás} + \begin{itemize} + \item XSS támadás localStorage-ból + \item Védelem: HttpOnly cookie használata + \end{itemize} + + \item \textbf{Algoritmus konfúzió} + \begin{itemize} + \item RS256 publikus kulcs HMAC secret-ként használva + \item Védelem: Explicit algoritmus megadása validáláskor + \end{itemize} + \end{enumerate} + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás} + \begin{itemize} + \item \textbf{JWT} = Kompakt, önálló, biztonságos token formátum + \item \textbf{Struktúra:} Header.Payload.Signature + \item \textbf{Előnyök:} Stateless, skálázható, cross-domain + \item \textbf{Claims:} Registered (exp, iat, sub) és Custom (role, email) + \item \textbf{Tárolás:} HttpOnly cookie a legbiztonságosabb + \item \textbf{Refresh token:} Új access token kéréshez hosszú élettartammal + \item \textbf{Biztonság:} HTTPS, erős secret, rövid lejárat, validálás + \end{itemize} + + \vspace{0.5cm} + + \begin{exampleblock}{Mikor használjuk a JWT-t?} + \begin{itemize} + \item RESTful API authentikáció + \item Microservice architektúra + \item Mobile alkalmazások + \item Single Page Applications (SPA) + \item Stateless session kezelés + \end{itemize} + \end{exampleblock} +\end{frame} diff --git a/Backend_ppt/authn_z/middlewares.tex b/Backend_ppt/authn_z/middlewares.tex new file mode 100644 index 0000000..a72dd85 --- /dev/null +++ b/Backend_ppt/authn_z/middlewares.tex @@ -0,0 +1,205 @@ +\section{Middleware} + +\begin{frame}[shrink=15]{Mi az a middleware?} + \begin{block}{Definíció} + Függvény HTTP kérés-válasz között, hozzáfér \texttt{req}, \texttt{res}, \texttt{next}-hez. + \end{block} + \begin{itemize} + \item Láncolható + \item Kérés előfeldolgozás, válasz utófeldolgozás + \item Kérés megszakítható + \end{itemize} +\end{frame} + +\begin{frame}[shrink=15]{Middleware működése} + \begin{center} + \begin{tikzpicture}[node distance=1.4cm, auto] + \node (browser) [rectangle, draw, text width=1.5cm, text centered] {Browser}; + \node (mw1) [rectangle, draw, right of=browser, xshift=1cm, text width=2cm, text centered] {MW 1\\Logger}; + \node (mw2) [rectangle, draw, right of=mw1, xshift=1.5cm, text width=2cm, text centered] {MW 2\\Auth}; + \node (route) [rectangle, draw, right of=mw2, xshift=1.5cm, text width=1.5cm, text centered] {Route}; + + \draw[->, thick] (browser) -- node[above] {Req} (mw1); + \draw[->, thick] (mw1) -- node[above] {\tiny next()} (mw2); + \draw[->, thick] (mw2) -- node[above] {\tiny next()} (route); + \draw[->, thick, dashed] (route) -- node[below] {Res} (browser); + \end{tikzpicture} + \end{center} + \begin{alertblock}{Fontos} + \texttt{next()} nélkül kérés megáll! + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=15]{Middleware típusok} + \begin{enumerate} + \item \textbf{Application-level}: \texttt{app.use()} + \item \textbf{Router-level}: \texttt{router.use()} + \item \textbf{Error-handling}: \texttt{(err, req, res, next)} + \item \textbf{Built-in}: \texttt{express.json()}, \texttt{express.static()} + \item \textbf{Third-party}: \texttt{cors}, \texttt{helmet}, \texttt{morgan} + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Logger middleware} + \begin{verbatim} +const logger = (req, res, next) => { + console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); + next(); +}; + +app.use(logger); + +app.get('/', (req, res) => { + res.json({ msg: 'Hello' }); +}); + +// Kimenet: +// [2026-02-24T10:30:15.000Z] GET / + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Request timing middleware} + \begin{verbatim} +const timer = (req, res, next) => { + req.startTime = Date.now(); + res.on('finish', () => { + const duration = Date.now() - req.startTime; + console.log(`${req.method} ${req.url} - ${duration}ms`); + }); + next(); +}; + +app.use(timer); + +// GET /api/users - 145ms + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Validation middleware} + \begin{verbatim} +const validateEmail = (req, res, next) => { + const { email } = req.body; + const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + if (!email || !re.test(email)) { + return res.status(400).json({ error: 'Invalid email' }); + } + next(); +}; + +app.post('/register', validateEmail, (req, res) => { + res.json({ success: true }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Auth middleware} + \begin{verbatim} +const auth = (req, res, next) => { + const token = req.headers.authorization?.split(' ')[1]; + + if (!token) { + return res.status(401).json({ error: 'Token required' }); + } + + try { + const decoded = jwt.verify(token, SECRET); + req.user = decoded; + next(); + } catch (err) { + res.status(403).json({ error: 'Invalid token' }); + } +}; + +app.get('/protected', auth, (req, res) => { + res.json({ user: req.user }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Role-based middleware} + \begin{verbatim} +const requireRole = (role) => { + return (req, res, next) => { + if (req.user.role !== role) { + return res.status(403).json({ error: 'Forbidden' }); + } + next(); + }; +}; + +app.delete('/user/:id', auth, requireRole('admin'), (req, res) => { + res.json({ deleted: true }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Error handling middleware} + \begin{verbatim} +// Mindig utolsóként! +app.use((err, req, res, next) => { + console.error(err.stack); + + res.status(err.status || 500).json({ + error: err.message || 'Internal Server Error' + }); +}); + +// Használat +app.get('/error', (req, res, next) => { + const err = new Error('Something broke!'); + err.status = 500; + next(err); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{CORS middleware} + \begin{verbatim} +const cors = require('cors'); + +// Minden origin engedélyezése +app.use(cors()); + +// Specifikus beállítás +app.use(cors({ + origin: 'https://frontend.com', + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE'] +})); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Built-in middleware} + \begin{itemize} + \item \texttt{express.json()}: JSON body parsing + \item \texttt{express.urlencoded()}: URL-encoded body parsing + \item \texttt{express.static()}: Static fájlok kiszolgálása + \end{itemize} + \begin{exampleblock}{Használat} + \texttt{app.use(express.json());}\\ + \texttt{app.use(express.static('public'));} + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=15]{Third-party middleware} + \begin{description} + \item[helmet] HTTP security headers + \item[morgan] HTTP request logger + \item[compression] Response compression (gzip) + \item[cookie-parser] Cookie parsing + \item[express-session] Session kezelés + \item[passport] Authentication + \end{description} +\end{frame} + +\begin{frame}[shrink=15]{Middleware best practices} + \begin{enumerate} + \item Mindig hívd meg \texttt{next()}-et (vagy küldjél választ) + \item Error middleware utolsóként + \item Sorrendiség számít! + \item Global middleware-ek előre (\texttt{cors}, \texttt{helmet}) + \item Route-specifikus middleware-ek közvetlenül route-hoz + \item Async middleware-eknél használj try-catch-et + \end{enumerate} +\end{frame} diff --git a/Backend_ppt/authn_z/middlewares_old.tex b/Backend_ppt/authn_z/middlewares_old.tex new file mode 100644 index 0000000..563759e --- /dev/null +++ b/Backend_ppt/authn_z/middlewares_old.tex @@ -0,0 +1,555 @@ +\section{Middleware} + +\begin{frame}{Mi az a middleware?} + \begin{block}{Definíció} + Függvény, amely HTTP kérés és válasz között fut, hozzáfér \texttt{req}, \texttt{res}, \texttt{next}-hez. + \end{block} + + \begin{itemize} + \item Láncolható függvények + \item Kérés előfeldolgozás + \item Válasz utófeldolgozás + \item Kérés megszakítás + \end{itemize} + + \begin{exampleblock}{Express middleware} + \texttt{function middleware(req, res, next) \{ ... \}} + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=5]{Middleware működése} + \begin{center} + \begin{tikzpicture}[node distance=1.5cm, auto] + \node (browser) [rectangle, draw, text width=2cm, text centered] {Browser}; + \node (mw1) [rectangle, draw, right of=browser, xshift=1.5cm, text width=2.5cm, text centered] {Middleware 1\\(Logger)}; + \node (mw2) [rectangle, draw, right of=mw1, xshift=2cm, text width=2.5cm, text centered] {Middleware 2\\(Auth)}; + \node (route) [rectangle, draw, right of=mw2, xshift=2cm, text width=2cm, text centered] {Route Handler}; + + \draw[->, thick] (browser) -- node[above] {Request} (mw1); + \draw[->, thick] (mw1) -- node[above] {\texttt{next()}} (mw2); + \draw[->, thick] (mw2) -- node[above] {\texttt{next()}} (route); + \draw[->, thick, dashed] (route) -- node[below] {Response} (browser); + \end{tikzpicture} + \end{center} + + \begin{alertblock}{Fontos!} + Ha nem hívod meg a \texttt{next()}-et, a kérés megáll és nem jut el a következő middleware-hez vagy route handler-hez! + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=5]{Middleware típusok} + \begin{enumerate} + \item \textbf{Application-level} + \begin{itemize} + \item Egész app-ra + \end{itemize} + + \item \textbf{Router-level} + \begin{itemize} + \item Adott routerre + \end{itemize} + + \item \textbf{Error-handling} + \begin{itemize} + \item 4 param: \texttt{(err, req, res, next)} + \end{itemize} + + \item \textbf{Built-in} + \begin{itemize} + \item \texttt{express.json()} + \end{itemize} + + \item \textbf{Third-party} + \begin{itemize} + \item \texttt{cors}, \texttt{helmet} + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Egyszerű logger middleware} + \begin{block}{Request logging - 1. rész} + \small + \begin{verbatim} +const express = require('express'); +const app = express(); + +const logger = (req, res, next) => { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] ${req.method} ${req.url}`); + next(); +}; + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Egyszerű logger middleware (folyt.)} + \begin{block}{Request logging - 2. rész (használat)} + \small + \begin{verbatim} +app.use(logger); + +app.get('/', (req, res) => { + res.json({ message: 'Hello World' }); +}); + +app.get('/users', (req, res) => { + res.json({ users: ['Alice', 'Bob'] }); +}); + +app.listen(3000); + +// Kimenet: +// [2026-02-23T10:30:15.000Z] GET / +// [2026-02-23T10:30:20.000Z] GET /users + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Request timing middleware} + \begin{block}{Válaszidő mérése} + \small + \begin{verbatim} +const requestTimer = (req, res, next) => { + req.startTime = Date.now(); + + res.on('finish', () => { + const duration = Date.now() - req.startTime; + console.log(`${req.method} ${req.url} - ${duration}ms`); + }); + + next(); +}; + +app.use(requestTimer); + +// Kimenet: +// GET /api/users - 145ms +// POST /api/login - 523ms + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Validation middleware} + \begin{block}{Input validáció - 1. rész} + \small + \begin{verbatim} +// email validáló middleware +const validateEmail = (req, res, next) => { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ + error: 'Email is required' + }); + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + return res.status(400).json({ + error: 'Invalid email format' + }); + } + + next(); // Validáció sikeres, tovább +}; + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Validation middleware (folyt.)} + \begin{block}{Input validáció - 2. rész (használat)} + \small + \begin{verbatim} +app.use(express.json()); // Body parser middleware + +// Route-specific middleware +app.post('/register', validateEmail, (req, res) => { + const { email, password } = req.body; + + // Email már validálva van a middleware által + // Regisztráció logika... + + res.json({ message: 'User registered', email }); +}); + +// Több middleware egyszerre +app.post('/login', + validateEmail, + validatePassword, // Másik validator + (req, res) => { + // Login logika... + } +); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Authentikációs middleware - JWT} + \begin{block}{Token validáció - 1. rész} + \small + \begin{verbatim} +const jwt = require('jsonwebtoken'); + +const authenticateJWT = (req, res, next) => { + // Token a Authorization header-ből + const authHeader = req.headers.authorization; + + if (!authHeader) { + return res.status(401).json({ + error: 'Access token required' + }); + } + + // Bearer TOKEN formátum + const token = authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ + error: 'Token not found' + }); + } + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Authentikációs middleware - JWT (folyt.)} + \begin{block}{Token validáció - 2. rész} + \small + \begin{verbatim} + // Token verify + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + + // Felhasználói adat hozzáadása a request objektumhoz + req.user = decoded; // { userId, email, role, ... } + + next(); // Sikeres authentikáció + + } catch (error) { + return res.status(403).json({ + error: 'Invalid or expired token' + }); + } +}; + +// Használat +app.get('/api/profile', authenticateJWT, (req, res) => { + // req.user elérhető itt + res.json({ user: req.user }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Autorizációs middleware - Role-based} + \begin{block}{Szerepkör ellenőrzés} + \small + \begin{verbatim} +// Role-based access control middleware factory +const requireRole = (role) => { + return (req, res, next) => { + // Feltételezzük, hogy az authenticateJWT már futott + if (!req.user) { + return res.status(401).json({ + error: 'Not authenticated' + }); + } + + if (req.user.role !== role) { + return res.status(403).json({ + error: `Forbidden: ${role} role required` + }); + } + + next(); // Jogosultság OK + }; +}; + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Autorizációs middleware (folyt.)} + \begin{block}{Használat szerepkör ellenőrzésre} + \small + \begin{verbatim} +// Admin-only endpoint +app.delete('/api/users/:id', + authenticateJWT, // 1. Authentikáció + requireRole('admin'), // 2. Autorizáció + (req, res) => { + // Csak admin férhet ide + const userId = req.params.id; + // Delete user logika... + res.json({ message: 'User deleted' }); + } +); + +// Több szerepkör támogatása +const requireAnyRole = (...roles) => { + return (req, res, next) => { + if (!req.user || !roles.includes(req.user.role)) { + return res.status(403).json({ error: 'Forbidden' }); + } + next(); + }; +}; + +app.get('/api/reports', authenticateJWT, requireAnyRole('admin', 'manager'), ...); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Rate limiting middleware} + \begin{block}{API hívások korlátozása} + \small + \begin{verbatim} +const rateLimit = require('express-rate-limit'); + +// Rate limiter konfiguráció +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 perc + max: 100, // Max 100 kérés 15 percenként + message: 'Too many requests, please try again later', + standardHeaders: true, // RateLimit-* headers + legacyHeaders: false +}); + +// Application-level +app.use('/api/', limiter); + +// Strict limiter login-hoz +const loginLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 5, // Max 5 login kísérlet 15 percenként + skipSuccessfulRequests: true // Sikeres login nem számít bele +}); + +app.post('/api/login', loginLimiter, (req, res) => { ... }); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Error handling middleware} + \begin{block}{Központi hibakezelés} + \small + \begin{verbatim} +// Error handling middleware (4 paraméter!) +const errorHandler = (err, req, res, next) => { + // Logolás + console.error('Error:', err); + + // Custom error osztályok kezelése + if (err.statusCode) { + return res.status(err.statusCode).json({ + error: err.name, + message: err.message + }); + } + + // Váratlan hibák + res.status(500).json({ + error: 'InternalServerError', + message: 'Something went wrong' + }); +}; + +// Error handler middleware UTOLJÁRA kell regisztrálni! +app.use(errorHandler); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Error handling használata} + \begin{block}{Hibák továbbítása a next()-el} + \small + \begin{verbatim} +// Async route handler +app.get('/api/users/:id', async (req, res, next) => { + try { + const user = await User.findById(req.params.id); + + if (!user) { + const error = new Error('User not found'); + error.statusCode = 404; + throw error; // vagy: return next(error); + } + + res.json({ user }); + + } catch (error) { + next(error); // Error handler middleware-nek továbbítja + } +}); + +// A errorHandler middleware automatikusan feldolgozza + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{CORS middleware} + \begin{block}{Cross-Origin Resource Sharing} + \small + \begin{verbatim} +const cors = require('cors'); + +// Egyszerű CORS - minden origin engedélyezve +app.use(cors()); + +// Konfigurált CORS +const corsOptions = { + origin: 'https://frontend.example.com', // Engedélyezett origin + methods: ['GET', 'POST', 'PUT', 'DELETE'], + allowedHeaders: ['Content-Type', 'Authorization'], + credentials: true, // Cookie-k engedélyezése + maxAge: 86400 // Preflight cache 24 óra +}; + +app.use(cors(corsOptions)); + +// Route-specific CORS +app.get('/api/public', cors(), (req, res) => { + res.json({ message: 'Public data' }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Security middleware - Helmet} + \begin{block}{HTTP header biztonság} + \small + \begin{verbatim} +const helmet = require('helmet'); + +// Helmet middleware - biztonságos HTTP headerek +app.use(helmet()); + +// Helmet beállítja: +// - Content-Security-Policy +// - X-DNS-Prefetch-Control +// - X-Frame-Options (SAMEORIGIN) +// - X-Content-Type-Options (nosniff) +// - X-XSS-Protection +// stb. + +// Egyedi konfiguráció +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'"] + } + } +})); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Middleware láncolás} + \begin{block}{Több middleware egyidejű használata} + \small + \begin{verbatim} +// Middleware láncolás egy route-on +app.post('/api/posts', + authenticateJWT, // 1. Authentikáció + requireRole('user'), // 2. Autorizáció + validatePost, // 3. Validáció + uploadImages, // 4. Képfeltöltés + (req, res) => { // 5. Route handler + // Post létrehozás logika + res.json({ message: 'Post created' }); + } +); + +// Middleware tömb +const postMiddlewares = [ + authenticateJWT, + requireRole('user'), + validatePost +]; + +app.post('/api/posts', postMiddlewares, (req, res) => { + // ... +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}{Middleware Best Practices} + \begin{enumerate} + \item \textbf{Sorrend számít:} + \begin{itemize} + \item CORS és security middleware-ek ELŐRE + \item Error handler middleware HÁTRA + \end{itemize} + + \vspace{0.2cm} + + \item \textbf{Mindig hívd meg a next()-et:} + \begin{itemize} + \item Kivéve ha response-t küldesz vagy hibát dobsz + \end{itemize} + + \vspace{0.2cm} + + \item \textbf{Error handling:} + \begin{itemize} + \item Async függvényekben try-catch + next(error) + \item 4 paraméteres error handler middleware + \end{itemize} + + \vspace{0.2cm} + + \item \textbf{Teljesítmény:} + \begin{itemize} + \item Ne futtass middleware-t szükségtelenül (route-specific) + \item Async műveletek csak ha szükséges + \end{itemize} + + \vspace{0.2cm} + + \item \textbf{Újrafelhasználhatóság:} + \begin{itemize} + \item Middleware factory pattern (pl. requireRole) + \item Konfigurálható middleware-ek + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{Middleware execution order} + \begin{block}{Tipikus middleware sorrend Express alkalmazásban} + \small + \begin{enumerate} + \item \textbf{helmet()} - Security headers + \item \textbf{cors()} - CORS beállítások + \item \textbf{morgan/logger} - Request logging + \item \textbf{express.json()} - Body parser + \item \textbf{express.urlencoded()} - URL-encoded body + \item \textbf{cookie-parser()} - Cookie parsing + \item \textbf{Rate limiter} - API rate limiting + \item \textbf{Custom middlewares} - Saját middleware-ek + \item \textbf{Routes} - Route handler-ek + \item \textbf{404 handler} - Not found middleware + \item \textbf{Error handler} - Központi hibakezelés (4 param) + \end{enumerate} + \end{block} +\end{frame} + +\begin{frame}{Összefoglalás - Middleware} + \begin{itemize} + \item \textbf{Middleware} = Függvény a kérés-válasz között + \item \textbf{Paraméterek:} (req, res, next) vagy (err, req, res, next) + \item \textbf{Típusok:} Application, Router, Error-handling, Built-in, Third-party + \item \textbf{next():} Következő middleware-re adja a vezérlést + \item \textbf{Használat:} + \begin{itemize} + \item Logging (morgan, winston) + \item Authentication (JWT validáció) + \item Authorization (szerepkör ellenőrzés) + \item Validation (input ellenőrzés) + \item Rate limiting (express-rate-limit) + \item Security (helmet, cors) + \item Error handling (központi hibakezelés) + \end{itemize} + \item \textbf{Sorrend fontos:} Security → Parsing → Auth → Routes → Error + \end{itemize} +\end{frame} diff --git a/Backend_ppt/authn_z/oauth2.tex b/Backend_ppt/authn_z/oauth2.tex new file mode 100644 index 0000000..9803f96 --- /dev/null +++ b/Backend_ppt/authn_z/oauth2.tex @@ -0,0 +1,154 @@ +\section{OAuth 2.0} + +\begin{frame}[shrink=15]{Mi az OAuth 2.0?} + \begin{block}{Definíció} + Autorizációs protokoll - korlátozott hozzáférés jelszó megosztása nélkül. + \end{block} + \begin{itemize} + \item RFC 6749 (2012) + \item Google, Facebook, GitHub használja + \item Példa: App hozzáfér Google Drive-hoz jelszó nélkül + \end{itemize} +\end{frame} + +\begin{frame}[shrink=15]{OAuth 2.0 szerepkörök} + \begin{enumerate} + \item \textbf{Resource Owner}: Felhasználó (birtokolja erőforrást) + \item \textbf{Client}: Alkalmazás (hozzáférést kér) + \item \textbf{Authorization Server}: Token kiállító + \item \textbf{Resource Server}: Védett erőforrás tároló + \end{enumerate} +\end{frame} + +\begin{frame}[shrink=15]{OAuth 2.0 Flow} + \begin{center} + \begin{tikzpicture}[node distance=1.8cm, auto] + \node (ro) [rectangle, draw, text width=1.8cm, text centered] {Resource Owner}; + \node (client) [rectangle, draw, below of=ro, text width=1.8cm, text centered] {Client}; + \node (auth) [rectangle, draw, right of=client, xshift=2.5cm, text width=2cm, text centered] {Auth Server}; + \node (res) [rectangle, draw, below of=auth, text width=2cm, text centered] {Resource Server}; + + \draw[->, thick] (client) -- node[left] {1. Kérés} (ro); + \draw[->, thick] (ro) -- node[above] {2. OK} (auth); + \draw[->, thick] (auth) -- node[right] {3. Token} (client); + \draw[->, thick] (client) -- node[above] {4. API} (res); + \draw[->, thick] (res) -- node[below] {5. Data} (client); + \end{tikzpicture} + \end{center} +\end{frame} + +\begin{frame}[shrink=15]{Grant Types} + \begin{enumerate} + \item \textbf{Authorization Code}: Szerver app, legbiztonságosabb + \item \textbf{Implicit}: \textcolor{red}{Elavult!} + \item \textbf{Resource Owner Password}: Közvetlen jelszó, megbízható app + \item \textbf{Client Credentials}: Machine-to-machine + \item \textbf{PKCE}: Modern mobil/SPA + \end{enumerate} +\end{frame} + +\begin{frame}[shrink=20]{Authorization Code Flow lépések} + \begin{enumerate} + \item Kliens átirányít: \texttt{/authorize?client\_id=\&redirect\_uri=\&scope=} + \item Felhasználó bejelentkezik és hozzájárul + \item Auth Server visszairányít: \texttt{redirect\_uri?code=AUTH\_CODE} + \item Kliens kicseréli code-ot: POST \texttt{/token} + \item Auth Server ad access token-t (+ refresh) + \item Kliens használja tokent: \texttt{Authorization: Bearer } + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Authorization Code - Auth kérés} + \begin{verbatim} +GET /oauth/authorize? + response_type=code& + client_id=YOUR_CLIENT_ID& + redirect_uri=https://app.com/callback& + scope=read write& + state=xyz123 + \end{verbatim} + \begin{verbatim} +// Callback +GET https://app.com/callback? + code=AUTH_CODE& + state=xyz123 + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Authorization Code - Token csere} + \begin{verbatim} +POST /oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=AUTH_CODE& +redirect_uri=https://app.com/callback& +client_id=YOUR_ID& +client_secret=YOUR_SECRET + \end{verbatim} + \begin{verbatim} +// Válasz +{ + "access_token": "eyJ...", + "expires_in": 3600, + "refresh_token": "tGzv..." +} + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{PKCE (Proof Key for Code Exchange)} + \begin{block}{Miért?} + Authorization Code Flow biztonságosabbá tétele mobil/SPA app-okhoz. + \end{block} + \begin{enumerate} + \item Kliens generál \texttt{code\_verifier} (random string) + \item Hash: \texttt{code\_challenge = SHA256(code\_verifier)} + \item Auth kéréshez csatol: \texttt{code\_challenge} + \item Token kérésnél küldi: \texttt{code\_verifier} + \item Server validálja: \texttt{SHA256(verifier) == challenge} + \end{enumerate} + \begin{alertblock}{Védelem} + Megakadályozza authorization code ellopását. + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=15]{Scope és Permission} + \begin{block}{Scope} + Jogosultság amit app kér. Példa: \texttt{read:user write:repo} + \end{block} + \begin{itemize} + \item Felhasználó látja mit kér az app + \item Csak kért scope-okat kapja meg + \item Token tartalmazza scope-okat + \end{itemize} + \begin{exampleblock}{GitHub példa} + \texttt{repo, user, gist, notifications} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Access Token használata} + \begin{verbatim} +// API kérés Bearer token-nel +GET /api/user/profile +Authorization: Bearer eyJhbGciOiJIUzI1NiIs... + \end{verbatim} + \begin{verbatim} +// Express validálás +app.get('/api/user', (req, res) => { + const token = req.headers.authorization?.split(' ')[1]; + // Token validálás... + res.json({ user: userData }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{OAuth 2.0 Security} + \begin{enumerate} + \item \textbf{HTTPS kötelező}: Minden kommunikáció + \item \textbf{State parameter}: CSRF védelem + \item \textbf{Redirect URI validation}: Előre regisztrált URI + \item \textbf{Short token lifetime}: Access token max 1 óra + \item \textbf{PKCE használata}: Mobil/SPA app-okhoz + \item \textbf{Scope limitation}: Csak szükséges jogok + \end{enumerate} +\end{frame} diff --git a/Backend_ppt/authn_z/oauth2_old.tex b/Backend_ppt/authn_z/oauth2_old.tex new file mode 100644 index 0000000..ddc9af2 --- /dev/null +++ b/Backend_ppt/authn_z/oauth2_old.tex @@ -0,0 +1,443 @@ +\section{OAuth 2.0} + +\begin{frame}{Mi az OAuth 2.0?} + \begin{block}{Definíció} + OAuth 2.0 egy nyílt szabványú \textbf{autorizációs protokoll}, amely korlátozott hozzáférést ad jelszó megosztása nélkül. + \end{block} + + \begin{itemize} + \item RFC 6749 (2012) + \item Autorizációra fókuszál + \item Biztonságos delegált hozzáférés + \item Google, Facebook, GitHub + \end{itemize} + + \begin{exampleblock}{Használat} + Alkalmazás hozzáfér Google Drive-hoz anélkül, hogy ismebné a jelszót. + \end{exampleblock} +\end{frame} + +\begin{frame}{OAuth 2.0 szerepkörök} + \begin{enumerate} + \item \textbf{Resource Owner (Erőforrás tulajdonos)} + \begin{itemize} + \item A felhasználó, aki birtokolja az erőforrást + \item Engedélyezheti a hozzáférést + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Client (Kliens)} + \begin{itemize} + \item Az alkalmazás, amely hozzáférést kér az erőforráshoz + \item Például: mobil app, webalkalmazás + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Authorization Server (Autorizációs szerver)} + \begin{itemize} + \item Kiállítja az access token-t sikeres authentikáció után + \item Kezeli a felhasználói hozzájárulást + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Resource Server (Erőforrás szerver)} + \begin{itemize} + \item Tárolja a védett erőforrásokat + \item Elfogadja és validálja az access token-eket + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{OAuth 2.0 Flow - Áttekintés} + \begin{center} + \begin{tikzpicture}[node distance=2cm, auto] + \node (ro) [rectangle, draw, text width=2cm, text centered] {Resource Owner}; + \node (client) [rectangle, draw, below of=ro, text width=2cm, text centered] {Client}; + \node (authserver) [rectangle, draw, right of=client, xshift=3cm, text width=2.5cm, text centered] {Authorization Server}; + \node (resserver) [rectangle, draw, below of=authserver, text width=2.5cm, text centered] {Resource Server}; + + \draw[->, thick] (client) -- node[left] {1. Kérés} (ro); + \draw[->, thick] (ro) -- node[above] {2. Hozzájárulás} (authserver); + \draw[->, thick] (authserver) -- node[right] {3. Token} (client); + \draw[->, thick] (client) -- node[above] {4. API kérés} (resserver); + \draw[->, thick] (resserver) -- node[below] {5. Védett erőforrás} (client); + \end{tikzpicture} + \end{center} + + \begin{itemize} + \item Az Authorization Server és Resource Server lehet ugyanaz a rendszer + \item A kommunikáció HTTPS-en keresztül történik + \end{itemize} +\end{frame} + +\begin{frame}[shrink=10]{OAuth 2.0 Grant Types} + \begin{block}{Grant Type} + Módszer, ahogyan kliens access token-t szerez. + \end{block} + + \begin{enumerate} + \item \textbf{Authorization Code} + \begin{itemize} + \item Legbiztonságosabb, szerveroldali app-hoz + \end{itemize} + + \item \textbf{Implicit} + \begin{itemize} + \item \textcolor{red}{Elavult!} + \end{itemize} + + \item \textbf{Resource Owner Password} + \begin{itemize} + \item Közvetlen jelszó, megbízható app-okhoz + \end{itemize} + + \item \textbf{Client Credentials} + \begin{itemize} + \item Machine-to-machine + \end{itemize} + + \item \textbf{PKCE} + \begin{itemize} + \item Modern mobil és SPA-hoz + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}[shrink=10]{Authorization Code Flow} + \begin{block}{Leggyakrabban használt flow} + Biztonságos szerveroldali alkalmazásokhoz, ahol a client secret biztonságosan tárolható. + \end{block} + + \begin{enumerate} + \small + \item \textbf{Kliens átirányítja a felhasználót} az Authorization Server-re + \begin{itemize} + \item URL: \texttt{/authorize?client\_id=\&redirect\_uri=\&scope=\&state=} + \end{itemize} + + \item \textbf{Felhasználó bejelentkezik és hozzájárul} + + \item \textbf{Authorization Server visszairányít} authorization code-dal + \begin{itemize} + \item URL: \texttt{redirect\_uri?code=AUTH\_CODE\&state=} + \end{itemize} + + \item \textbf{Kliens kicseréli a code-ot} access token-re + \begin{itemize} + \item POST: \texttt{/token} (client\_id, client\_secret, code, redirect\_uri) + \end{itemize} + + \item \textbf{Authorization Server visszaad} access token-t (és refresh token-t) + + \item \textbf{Kliens használja az access token-t} API kérésekhez + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Authorization Code Flow - Példa} + \begin{block}{1. Felhasználó átirányítása az authorization endpoint-ra} + \small + \begin{verbatim} +GET /oauth/authorize? + response_type=code& + client_id=YOUR_CLIENT_ID& + redirect_uri=https://yourapp.com/callback& + scope=read write& + state=xyz123 + \end{verbatim} + \end{block} + + \begin{block}{2. Callback authorization code-dal} + \small + \begin{verbatim} +GET https://yourapp.com/callback? + code=AUTH_CODE_HERE& + state=xyz123 + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Authorization Code Flow - Példa (folyt.)} + \begin{block}{3. Token kérés} + \small + \begin{verbatim} +POST /oauth/token +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=AUTH_CODE_HERE& +redirect_uri=https://yourapp.com/callback& +client_id=YOUR_CLIENT_ID& +client_secret=YOUR_CLIENT_SECRET + \end{verbatim} + \end{block} + + \begin{block}{4. Token válasz} + \small + \begin{verbatim} +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "scope": "read write" +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}{PKCE (Proof Key for Code Exchange)} + \begin{block}{Miért szükséges?} + A natív és SPA (Single Page Application) alkalmazások nem tudják biztonságosan tárolni a client secret-et. A PKCE authorization code interception elleni védelmet nyújt. + \end{block} + + \vspace{0.5cm} + + \begin{enumerate} + \item Kliens generál egy random \textbf{code\_verifier} értéket + \item Létrehoz egy \textbf{code\_challenge}-t a verifier-ből + \begin{itemize} + \item \texttt{code\_challenge = BASE64URL(SHA256(code\_verifier))} + \end{itemize} + \item Authorization kéréshez csatolja a \texttt{code\_challenge}-t + \item Token kéréshez csatolja a \texttt{code\_verifier}-t + \item Szerver ellenőrzi, hogy a verifier megfelel-e a challenge-nek + \end{enumerate} + + \vspace{0.5cm} + + \begin{alertblock}{Fontos!} + A PKCE ma már \textbf{minden} OAuth 2.0 flow-hoz ajánlott! + \end{alertblock} +\end{frame} + +\begin{frame}{Client Credentials Grant} + \begin{block}{Machine-to-Machine hitelesítés} + Amikor nincs felhasználói interakció, csak két rendszer kommunikál egymással. + \end{block} + + \vspace{0.5cm} + + \begin{itemize} + \item \textbf{Használati esetek:} + \begin{itemize} + \item Backend szolgáltatások közötti kommunikáció + \item Cron job-ok, scheduled task-ok + \item CLI eszközök + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Folyamat:} + \begin{enumerate} + \item Kliens küld egy POST kérést a token endpoint-ra + \item Csatolja a client\_id-t és client\_secret-et + \item Szerver visszaadja az access token-t + \item Kliens használja a token-t API kérésekhez + \end{enumerate} + \end{itemize} + + \vspace{0.3cm} + + \begin{alertblock}{Figyelem!} + Nincs refresh token, mert nincs felhasználói kontextus! + \end{alertblock} +\end{frame} + +\begin{frame}[fragile]{Client Credentials Grant - Példa} + \begin{block}{Token kérés} + \small + \begin{verbatim} +POST /oauth/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic BASE64(client_id:client_secret) + +grant_type=client_credentials& +scope=api.read + \end{verbatim} + \end{block} + + \vspace{0.3cm} + + \begin{block}{Token válasz} + \small + \begin{verbatim} +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_type": "Bearer", + "expires_in": 3600, + "scope": "api.read" +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Access Token és Refresh Token} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{Access Token} + \begin{itemize} + \item Rövid élettartam (pl. 1 óra) + \item API kérésekhez használt + \item Bearer token formátum + \item Gyakran JWT + \end{itemize} + \end{block} + + \vspace{0.3cm} + + \textbf{Használat:} + \small + \begin{verbatim} +Authorization: Bearer + ACCESS_TOKEN + \end{verbatim} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{Refresh Token} + \begin{itemize} + \item Hosszú élettartam (napok/hetek) + \item Új access token kéréséhez + \item Biztonságosan tárolva + \item Opaque token + \end{itemize} + \end{block} + + \vspace{0.3cm} + + \textbf{Token megújítás:} + \small + \begin{verbatim} +POST /oauth/token +grant_type=refresh_token& +refresh_token=REFRESH_TOKEN + \end{verbatim} + \end{column} + \end{columns} + + \vspace{0.5cm} + + \begin{alertblock}{Best Practice} + Az access token lejárta esetén a refresh token-nel új tokent kérünk, nem kell újra bejelentkeztetni a felhasználót! + \end{alertblock} +\end{frame} + +\begin{frame}{Scopes (Hatókörök)} + \begin{block}{Mi az a scope?} + A \textbf{scope} meghatározza, hogy a kliens milyen erőforrásokhoz és milyen műveletekhez férhet hozzá. + \end{block} + + \vspace{0.5cm} + + \begin{itemize} + \item \textbf{Granulált jogosultságok:} + \begin{itemize} + \item \texttt{read} - olvasási jogosultság + \item \texttt{write} - írási jogosultság + \item \texttt{delete} - törlési jogosultság + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Erőforrás specifikus:} + \begin{itemize} + \item \texttt{user:email} - email cím olvasása + \item \texttt{repo:write} - repository írása + \item \texttt{profile:read} - profil olvasása + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Best Practices:} + \begin{itemize} + \item Least privilege principle: csak a szükséges scope-ok + \item Egyértelmű elnevezés + \item Dokumentáció minden scope-hoz + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}{OpenID Connect (OIDC)} + \begin{block}{Authentikációs réteg OAuth 2.0 felett} + Az \textbf{OpenID Connect} kiegészíti az OAuth 2.0-t authentikációs funkcionalitással. + \end{block} + + \vspace{0.5cm} + + \begin{itemize} + \item \textbf{OAuth 2.0} = Autorizáció + \item \textbf{OpenID Connect} = Autorizáció + Authentikáció + + \vspace{0.3cm} + + \item \textbf{ID Token:} + \begin{itemize} + \item JWT formátumú token + \item Tartalmazza a felhasználó azonosító adatait + \item Claims: \texttt{sub}, \texttt{name}, \texttt{email}, \texttt{picture}, stb. + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{UserInfo Endpoint:} + \begin{itemize} + \item További felhasználói információk lekérdezése + \item Access token-nel hívható + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Használat:} "Sign in with Google", "Login with Facebook" + \end{itemize} +\end{frame} + +\begin{frame}{OAuth 2.0 Biztonsági best practices} + \begin{alertblock}{Kritikus biztonsági szempontok} + \begin{itemize} + \item \textbf{HTTPS kötelező} minden kommunikációhoz! + \item \textbf{State paraméter} használata CSRF védelem miatt + \item \textbf{Client secret} biztonságos tárolása (szerver oldalon) + \item \textbf{Redirect URI validáció} - csak engedélyezett URI-k + \item \textbf{Token lejárati idő} beállítása + \item \textbf{Scope minimalizálása} - least privilege + \end{itemize} + \end{alertblock} + + \vspace{0.3cm} + + \begin{block}{További ajánlások} + \begin{itemize} + \item PKCE használata minden flow-ban + \item Token revocation támogatása + \item Rate limiting az endpoint-okon + \item Token tárolás biztonságos helyen (HttpOnly cookie) + \item Regular security audit + \end{itemize} + \end{block} +\end{frame} + +\begin{frame}{Összefoglalás} + \begin{itemize} + \item \textbf{OAuth 2.0} = Autorizációs protokoll (nem authentikációs!) + \item \textbf{Szerepkörök:} Resource Owner, Client, Authorization Server, Resource Server + \item \textbf{Grant Types:} Authorization Code (PKCE), Client Credentials + \item \textbf{Tokenek:} Access Token (rövid), Refresh Token (hosszú) + \item \textbf{Scopes:} Granulált jogosultság kezelés + \item \textbf{OpenID Connect:} OAuth 2.0 + Authentikáció + \item \textbf{Biztonság:} HTTPS, state, PKCE, token biztonság + \end{itemize} + + \vspace{0.5cm} + + \begin{exampleblock}{Amikor OAuth 2.0-t használunk} + \begin{itemize} + \item Social login implementációhoz + \item Third-party API hozzáféréshez + \item Microservice authentikációhoz + \item Mobile és SPA alkalmazásokhoz + \end{itemize} + \end{exampleblock} +\end{frame} diff --git a/Backend_ppt/authn_z/services.tex b/Backend_ppt/authn_z/services.tex new file mode 100644 index 0000000..0dcd6ac --- /dev/null +++ b/Backend_ppt/authn_z/services.tex @@ -0,0 +1,233 @@ +\section{Service Layer} + +\begin{frame}[shrink=15]{Mi az a Service Layer?} + \begin{block}{Definíció} + Tervezési minta - üzleti logika elválasztása controller-ektől és data layer-től. + \end{block} + \begin{itemize} + \item Separation of Concerns + \item Reusability (újrafelhasználható logika) + \item Testability (könnyebb tesztelés) + \item Maintainability + \end{itemize} + \begin{exampleblock}{Architektúra} + Controller → Service → Repository → Database + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=15]{Háromrétegű architektúra} + \begin{columns} + \begin{column}{0.32\textwidth} + \begin{block}{Presentation} + \begin{small} + Controllers, Routes\\ + HTTP req/res\\ + Validáció + \end{small} + \end{block} + \end{column} + \begin{column}{0.32\textwidth} + \begin{block}{Business Logic} + \begin{small} + \textbf{Services}\\ + Üzleti szabályok\\ + Orchestration + \end{small} + \end{block} + \end{column} + \begin{column}{0.32\textwidth} + \begin{block}{Data Access} + \begin{small} + Repository\\ + ORM\\ + Database + \end{small} + \end{block} + \end{column} + \end{columns} + \begin{alertblock}{Fontos} + Controller \textbf{NEM} tartalmaz üzleti logikát, csak delegál! + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=15]{Service Layer előnyei} + \begin{enumerate} + \item \textbf{Separation}: Tiszta felelősségi körök + \item \textbf{Reusability}: Több controller használhatja + \item \textbf{Testability}: Könnyen unit tesztelhető + \item \textbf{Maintainability}: Változások izoláltak + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=20]{AuthService - Interface} + \begin{verbatim} +class AuthService { + async register(userData) { ... } + async login(email, password) { ... } + async logout(userId) { ... } + async refreshToken(refreshToken) { ... } + async verifyToken(token) { ... } + async resetPassword(email) { ... } +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{AuthService - Register} + \begin{verbatim} +class AuthService { + async register(userData) { + const exists = await this.userRepo.findByEmail(userData.email); + if (exists) throw new Error('User exists'); + + const hashed = await bcrypt.hash(userData.password, 10); + const user = await this.userRepo.create({ + ...userData, + password: hashed + }); + + return { id: user.id, email: user.email }; + } +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{AuthService - Login} + \begin{verbatim} +async login(email, password) { + const user = await this.userRepo.findByEmail(email); + if (!user) throw new Error('Invalid credentials'); + + const valid = await bcrypt.compare(password, user.password); + if (!valid) throw new Error('Invalid credentials'); + + const token = jwt.sign( + { userId: user.id, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '1h' } + ); + + return { token, user: { id: user.id, email: user.email } }; +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Controller használja Service-t} + \begin{verbatim} +class AuthController { + constructor(authService) { + this.authService = authService; + } + + async register(req, res) { + try { + const user = await this.authService.register(req.body); + res.status(201).json(user); + } catch (err) { + res.status(400).json({ error: err.message }); + } + } + + async login(req, res) { + try { + const result = await this.authService.login(req.body.email, req.body.password); + res.json(result); + } catch (err) { + res.status(401).json({ error: err.message }); + } + } +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{UserService példa} + \begin{verbatim} +class UserService { + constructor(userRepo) { + this.userRepo = userRepo; + } + + async getById(id) { + const user = await this.userRepo.findById(id); + if (!user) throw new Error('User not found'); + return user; + } + + async update(id, data) { + const user = await this.getById(id); + return await this.userRepo.update(id, data); + } + + async delete(id) { + await this.getById(id); + return await this.userRepo.delete(id); + } +} + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Dependency Injection} + \begin{verbatim} +// Repository +class UserRepository { + async findById(id) { /* DB query */ } + async create(data) { /* DB insert */ } +} + +// Service +class UserService { + constructor(userRepository) { + this.userRepo = userRepository; + } + // ... +} + +// DI container +const userRepo = new UserRepository(); +const userService = new UserService(userRepo); +const userController = new UserController(userService); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Service Layer Best Practices} + \begin{enumerate} + \item Service-ek ne dependáljanak controller-ektől + \item Egy service = egy domain (User, Auth, Order) + \item Dependency Injection használata + \item Service-ek ne ismerjék HTTP-t (req, res) + \item Hibakezelés service-ben (throw Error) + \item Transaction logika service-ben + \item Async/await következetes használata + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=20]{Transaction példa Service-ben} + \begin{verbatim} +class OrderService { + async createOrder(userId, items) { + const transaction = await db.transaction(); + + try { + const order = await this.orderRepo.create({ userId }, transaction); + + for (const item of items) { + await this.orderItemRepo.create({ orderId: order.id, ...item }, transaction); + } + + await transaction.commit(); + return order; + } catch (err) { + await transaction.rollback(); + throw err; + } + } +} + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Testing Service Layer} + \begin{itemize} + \item Service-ek unit tesztelése mock repository-val + \item Ne kelljen DB a unit teszthez + \item Integration teszt valódi DB-vel + \end{itemize} +\end{frame} diff --git a/Backend_ppt/authn_z/services_old.tex b/Backend_ppt/authn_z/services_old.tex new file mode 100644 index 0000000..6550e6b --- /dev/null +++ b/Backend_ppt/authn_z/services_old.tex @@ -0,0 +1,671 @@ +\section{Service Layer} + +\begin{frame}[shrink=5]{Mi az a Service Layer?} + \begin{block}{Definíció} + A \textbf{Service Layer} (szolgáltatási réteg) egy tervezési minta, amely elválasztja az üzleti logikát a controller-ektől és a data access layer-től. + \end{block} + + \begin{itemize} + \item \textbf{Separation of Concerns:} Felelősségek szétválasztása + \item \textbf{Reusability:} Újrafelhasználható üzleti logika + \item \textbf{Testability:} Könnyebb tesztelhetőség + \item \textbf{Maintainability:} Karbantarthatóság + \end{itemize} + + \begin{exampleblock}{MVC architektúrában} + Controller → \textbf{Service Layer} → Repository/Model → Database + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=5]{Háromrétegű architektúra} + \begin{columns} + \begin{column}{0.3\textwidth} + \begin{block}{Presentation Layer} + \begin{itemize} + \item Controllers + \item Routes + \item HTTP kérés/válasz + \item Validáció + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.35\textwidth} + \begin{block}{Business Logic Layer} + \begin{itemize} + \item \textbf{Services} + \item Üzleti szabályok + \item Adatmanipuláció + \item Orchestration + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.3\textwidth} + \begin{block}{Data Access Layer} + \begin{itemize} + \item Repository + \item Models + \item ORM + \item Database + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \begin{alertblock}{Fontos!} + A controller \textbf{NEM} tartalmaz üzleti logikát, csak delegál a service-eknek! + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=10]{Service Layer előnyei} + \begin{enumerate} + \item \textbf{Separation of Concerns} + \begin{itemize} + \item Tiszta felelősségi körök + \item Controller: HTTP kezelés + \item Service: Üzleti logika + \item Repository: Adatelérés + \end{itemize} + + \item \textbf{Reusability} + \begin{itemize} + \item Több controller használhatja ugyanazt a service-t + \item Különböző kontextusokban (API, CLI, Background job) + \end{itemize} + + \item \textbf{Testability} + \begin{itemize} + \item Service-ek könnyebben unit tesztelhetők + \item Mock-olható függőségek + \end{itemize} + + \item \textbf{Maintainability} + \begin{itemize} + \item Változások izoláltak + \item Könnyebb hibakeresés + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=5]{AuthService - Authentikációs szolgáltatás} + \begin{block}{AuthService felelősségei} + \begin{itemize} + \item Felhasználó regisztráció + \item Bejelentkezés (login) + \item Token generálás és validálás + \item Jelszó hash-elés + \item Kijelentkezés (logout) + \end{itemize} + \end{block} + + \begin{exampleblock}{AuthService példa} + \small + \begin{verbatim} +class AuthService { + async register(userData) { ... } + async login(email, password) { ... } + async logout(userId) { ... } + async refreshToken(refreshToken) { ... } + async verifyToken(token) { ... } + async resetPassword(email) { ... } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{AuthService implementáció - Register} + \begin{block}{Regisztráció üzleti logikája} + \small + \begin{verbatim} +class AuthService { + async register(userData) { + const existing = await this.userRepository + .findByEmail(userData.email); + if (existing) throw new Error('User exists'); + + const hashed = await bcrypt.hash(userData.password, 10); + const user = await this.userRepository.create({ + ...userData, + password: hashed + }); + + return { id: user.id, email: user.email }; + } +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{AuthService implementáció - Login} + \begin{block}{Bejelentkezés üzleti logikája} + \small + \begin{verbatim} +async login(email, password) { + const user = await this.userRepository.findByEmail(email); + if (!user) throw new Error('Invalid credentials'); + + const valid = await bcrypt.compare(password, user.password); + if (!valid) throw new Error('Invalid credentials'); + + const accessToken = jwt.sign( + { userId: user.id, email: user.email }, + process.env.JWT_SECRET, { expiresIn: '15m' } + ); + + const refreshToken = jwt.sign( + { userId: user.id }, + process.env.REFRESH_SECRET, { expiresIn: '7d' } + ); + + return { accessToken, refreshToken }; +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Controller használja az AuthService-t} + \begin{block}{Thin Controller - Fat Service} + \small + \begin{verbatim} +app.post('/api/auth/register', async (req, res) => { + try { + const user = await authService.register(req.body); + res.status(201).json({ user }); + } catch (error) { + res.status(400).json({ error: error.message }); + } +}); + +app.post('/api/auth/login', async (req, res) => { + try { + const result = await authService.login( + req.body.email, req.body.password + ); + res.cookie('refreshToken', result.refreshToken, + { httpOnly: true, secure: true }); + res.json({ accessToken: result.accessToken }); + } catch (error) { + res.status(401).json({ error: error.message }); + } +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{UserService - Felhasználó kezelés} + \begin{block}{UserService felelősségei} + \begin{itemize} + \item Felhasználói profil lekérdezés + \item Profil módosítás + \item Jelszó változtatás + \item Felhasználó törlés + \item Felhasználó lista (admin) + \end{itemize} + \end{block} + + \vspace{0.3cm} + + \begin{exampleblock}{UserService példa} + \small + \begin{verbatim} +class UserService { + constructor(userRepository) { + this.userRepository = userRepository; + } + + async getUserProfile(userId) { ... } + async updateProfile(userId, data) { ... } + async changePassword(userId, oldPassword, newPassword) { ... } + async deleteUser(userId) { ... } + async getAllUsers(filters) { ... } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=5]{UserService implementáció} + \begin{block}{Jelszó változtatás üzleti logikával} + \small + \begin{verbatim} +class UserService { + async changePassword(userId, oldPass, newPass) { + const user = await this.userRepository.findById(userId); + if (!user) throw new Error('User not found'); + + const valid = await bcrypt.compare(oldPass, user.password); + if (!valid) throw new Error('Incorrect password'); + + const hashed = await bcrypt.hash(newPass, 10); + await this.userRepository.update(userId, + { password: hashed }); + await this.tokenService.revokeAllTokens(userId); + + return { success: true }; + } +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{TokenService - Token kezelés} + \begin{block}{TokenService felelősségei} + \begin{itemize} + \item Access token generálás + \item Refresh token generálás + \item Token validálás + \item Token megújítás (refresh) + \item Token visszavonás (revoke) + \end{itemize} + \end{block} + + \vspace{0.3cm} + + \begin{exampleblock}{TokenService példa} + \small + \begin{verbatim} +class TokenService { + constructor(tokenRepository) { + this.tokenRepository = tokenRepository; + } + + generateAccessToken(payload) { ... } + generateRefreshToken(userId) { ... } + async verifyAccessToken(token) { ... } + async refreshAccessToken(refreshToken) { ... } + async revokeToken(token) { ... } + async revokeAllTokens(userId) { ... } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=5]{TokenService implementáció} + \begin{block}{Token refresh implementáció} + \small + \begin{verbatim} +class TokenService { + async refreshAccessToken(refreshToken) { + const decoded = jwt.verify( + refreshToken, process.env.REFRESH_SECRET); + + const stored = await this.tokenRepository + .findByToken(refreshToken); + if (!stored || stored.revoked) + throw new Error('Token revoked'); + + const user = await this.userRepository + .findById(decoded.userId); + return { + accessToken: this.generateAccessToken(user) + }; + } +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{AuthorizationService - Autorizáció} + \begin{block}{AuthorizationService felelősségei} + \begin{itemize} + \item Jogosultság ellenőrzés + \item Role-based access control (RBAC) + \item Permission-based access control (PBAC) + \item Resource ownership ellenőrzés + \end{itemize} + \end{block} + + \vspace{0.3cm} + + \begin{exampleblock}{AuthorizationService példa} + \small + \begin{verbatim} +class AuthorizationService { + async hasRole(userId, role) { ... } + async hasPermission(userId, permission) { ... } + async canAccessResource(userId, resourceId, action) { ... } + async isOwner(userId, resourceId) { ... } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile]{AuthorizationService implementáció} + \begin{block}{Jogosultság ellenőrzés} + \small + \begin{verbatim} +class AuthorizationService { + constructor(userRepository, permissionRepository) { + this.userRepository = userRepository; + this.permissionRepository = permissionRepository; + } + + async canAccessResource(userId, resourceId, action) { + const user = await this.userRepository.findById(userId); + + // 1. Admin mindent csinálhat + if (user.role === 'admin') { + return true; + } + + // 2. Ownership ellenőrzés + const resource = await this.resourceRepository.findById(resourceId); + if (resource.ownerId === userId && action === 'read') { + return true; + } + + // 3. Permission alapú ellenőrzés + const permissions = await this.permissionRepository.getByUserId(userId); + return permissions.some(p => p.action === action && p.resource === resourceId); + } +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Dependency Injection} + \begin{block}{Service-ek kapcsolata} + A service-ek más service-eket használnak. Dependency Injection segít a függőségek kezelésében. + \end{block} + + \vspace{0.3cm} + + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Rossz példa:} + \small + \begin{verbatim} +class AuthService { + constructor() { + this.userRepo = + new UserRepository(); + } +} + \end{verbatim} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Jó példa (DI):} + \small + \begin{verbatim} +class AuthService { + constructor(userRepo) { + this.userRepo = userRepo; + } +} + +const authService = + new AuthService(userRepo); + \end{verbatim} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile]{Service Container / DI Container} + \begin{block}{Automatikus Dependency Injection} + DI container-ek automatikusan kezelik a szolgáltatások létrehozását. + \end{block} + + \vspace{0.3cm} + + \begin{exampleblock}{Awilix library használata} + \small + \begin{verbatim} +const { createContainer, asClass } = require('awilix'); + +const container = createContainer(); +container.register({ + userRepository: asClass(UserRepository).singleton(), + tokenRepository: asClass(TokenRepository).singleton(), + authService: asClass(AuthService).singleton(), + userService: asClass(UserService).singleton(), + tokenService: asClass(TokenService).singleton() +}); + +const authService = container.resolve('authService'); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile]{Error Handling a Service Layer-ben} + \begin{block}{Custom Error osztályok} + \small + \begin{verbatim} +class AuthenticationError extends Error { + constructor(message) { + super(message); + this.statusCode = 401; + } +} + +class AuthorizationError extends Error { + constructor(message) { + super(message); + this.statusCode = 403; + } +} + +class NotFoundError extends Error { + constructor(resource) { + super(`${resource} not found`); + this.statusCode = 404; + } +} + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Error Handling - Service használat} + \begin{block}{Service dobja a custom error-t} + \small + \begin{verbatim} +class AuthService { + async login(email, password) { + const user = await this.userRepository + .findByEmail(email); + if (!user) { + throw new AuthenticationError('Invalid credentials'); + } + const valid = await bcrypt.compare( + password, user.password + ); + if (!valid) { + throw new AuthenticationError('Invalid credentials'); + } + // ... + } +} + \end{verbatim} + \end{block} + + \vspace{0.2cm} + + \begin{block}{Controller kezeli} + \small + \begin{verbatim} +app.post('/api/auth/login', async (req, res, next) => { + try { + const result = await authService.login( + req.body.email, req.body.password); + res.json(result); + } catch (error) { + next(error); + } +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}[fragile]{Global Error Handler Middleware} + \begin{block}{Központi hibaüzenet kezelés} + \small + \begin{verbatim} +app.use((error, req, res, next) => { + console.error(error); + + if (error.statusCode) { + return res.status(error.statusCode) + .json({ error: error.message }); + } + + res.status(500).json({ + error: 'Internal Server Error' + }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}{Service Layer Testing} + \begin{block}{Unit Testing} + A service-ek izoláltan tesztelhetők mock repository-kkal és service-ekkel. + \end{block} + + \vspace{0.3cm} + + \begin{itemize} + \item \textbf{Előnyök:} + \begin{itemize} + \item Gyors tesztek (nincs adatbázis) + \item Üzleti logika fókusz + \item Mock-olható függőségek + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Test framework-ök:} + \begin{itemize} + \item Jest + \item Mocha + Chai + \item Vitest + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Mocking library-k:} + \begin{itemize} + \item Sinon.js + \item Jest built-in mocks + \end{itemize} + \end{itemize} +\end{frame} + +\begin{frame}[fragile,shrink=5]{Service Unit Test példa} + \begin{block}{AuthService.login() teszt} + \small + \begin{verbatim} +describe('AuthService', () => { + let authService, mockUserRepo; + + beforeEach(() => { + mockUserRepo = { findByEmail: jest.fn() }; + authService = new AuthService(mockUserRepo); + }); + + test('login fails for invalid user', async () => { + mockUserRepo.findByEmail.mockResolvedValue(null); + await expect( + authService.login('test@test.com', 'pass') + ).rejects.toThrow('Invalid credentials'); + }); +}); + \end{verbatim} + \end{block} +\end{frame} + +\begin{frame}{Service Layer Best Practices} + \begin{enumerate} + \item \textbf{Single Responsibility:} Egy service egy felelősségi kör + \item \textbf{Dependency Injection:} Konstruktorban injektált függőségek + \item \textbf{Thin Controllers:} Controller csak HTTP kezel + \item \textbf{Error Handling:} Custom error osztályok használata + \item \textbf{Async/Await:} Tiszta aszinkron kód + \item \textbf{Validation:} Input validáció a service-ben is + \item \textbf{Transaction Management:} Adatbázis tranzakciók + \item \textbf{Logging:} Strukturált log-olás + \end{enumerate} +\end{frame} + +\begin{frame}{Projekt struktúra Service Layer-rel} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Fájl struktúra:} + \small + \begin{itemize} + \item \texttt{src/} + \begin{itemize} + \item \texttt{controllers/} + \begin{itemize} + \item \texttt{auth.controller.js} + \item \texttt{user.controller.js} + \end{itemize} + \item \texttt{services/} + \begin{itemize} + \item \texttt{auth.service.js} + \item \texttt{user.service.js} + \item \texttt{token.service.js} + \end{itemize} + \item \texttt{repositories/} + \begin{itemize} + \item \texttt{user.repository.js} + \item \texttt{token.repository.js} + \end{itemize} + \item \texttt{models/} + \item \texttt{middlewares/} + \item \texttt{utils/} + \end{itemize} + \end{itemize} + \end{column} + + \begin{column}{0.48\textwidth} + \textbf{Rétegek felelősségei:} + \begin{itemize} + \item \textbf{Controller:} + \begin{itemize} + \item HTTP kérés/válasz + \item Validáció (input) + \item Service hívás + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Service:} + \begin{itemize} + \item Üzleti logika + \item Adatmanipuláció + \item Orchestration + \end{itemize} + + \vspace{0.3cm} + + \item \textbf{Repository:} + \begin{itemize} + \item Adatbázis műveletek + \item Query-k + \item ORM interaction + \end{itemize} + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{Összefoglalás} + \begin{itemize} + \item \textbf{Service Layer} = Üzleti logika réteg + \item \textbf{Separation of Concerns:} Controller, Service, Repository + \item \textbf{AuthService:} Regisztráció, login, token kezelés + \item \textbf{UserService:} Felhasználó kezelés, profil, jelszó + \item \textbf{TokenService:} Token generálás, validálás, refresh + \item \textbf{AuthorizationService:} Jogosultság ellenőrzés + \item \textbf{Dependency Injection:} Konstruktor alapú DI + \item \textbf{Error Handling:} Custom error osztályok + \item \textbf{Testing:} Unit teszt mock-okkal + \item \textbf{Best Practices:} Thin controller, fat service + \end{itemize} + + \vspace{0.3cm} + + \begin{exampleblock}{Miért használjuk?} + Tiszta kód, újrafelhasználhatóság, tesztelhetőség, karbantarthatóság + \end{exampleblock} +\end{frame} diff --git a/Backend_ppt/authn_z/session.tex b/Backend_ppt/authn_z/session.tex new file mode 100644 index 0000000..73dd3b1 --- /dev/null +++ b/Backend_ppt/authn_z/session.tex @@ -0,0 +1,280 @@ +\section{Session} + +\begin{frame}{Mi az a session?} + \begin{block}{Definíció} + Szerver-oldali állapot, amely felhasználói adatokat tárol bejelentkezés után. + \end{block} + + \begin{itemize} + \item \textbf{Stateful} megközelítés + \item Kliens csak \textbf{session ID}-t tárol (cookie) + \item Tartalom a szerveren (memória/DB/Redis) + \end{itemize} + + \begin{alertblock}{Fontos} + Session auth nem token-alapú, szerver kezeli az állapotot. + \end{alertblock} +\end{frame} + +\begin{frame}{Session vs Token} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{Session} + \begin{itemize} + \item Szerver tárolja + \item Cookie-ban ID + \item Könnyű visszavonás + \item Nehezebb skálázás + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{Token (JWT)} + \begin{itemize} + \item Stateless + \item Kliens tárolja + \item Nehezebb visszavonás + \item Könnyebb skálázás + \end{itemize} + \end{block} + \end{column} + \end{columns} + + \begin{center} + Session $\leftrightarrow$ JWT: kontroll vs skálázhatóság + \end{center} +\end{frame} + +\begin{frame}{Session folyamat} + \begin{enumerate} + \item Bejelentkezés (username + password) + \item Adatok ellenőrzése + \item \textbf{Session} létrehozás + \item Session ID cookie-ban + \item Kliens csatolja minden kéréshez + \item Szerver ID alapján azonosít + \end{enumerate} + + \begin{alertblock}{Megjegyzés} + HTTPS kötelező, különben session ID elliopható! + \end{alertblock} +\end{frame} + +\begin{frame}{Session tárolás} + \begin{block}{Hol tároljuk?} + Session adatokat szerveren, több lehetőség: + \end{block} + + \begin{itemize} + \item \textbf{In-memory} - gyors, nem skálázható + \item \textbf{Redis} - gyors, skálázható, TTL + \item \textbf{DB} - tartós, lassabb + \item \textbf{Distributed cache} - nagy rendszerekhez + \end{itemize} + + \begin{alertblock}{Best Practice} + Élesben ne használj in-memory store-t! + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Redis Session Store - Telepítés} + \begin{block}{Redis előnyei session tároláshoz} + Gyors, skálázható, beépített TTL (Time To Live) támogatás + \end{block} + + \begin{verbatim} +npm install express-session connect-redis redis + +// Redis kliens és session store +const session = require('express-session'); +const RedisStore = require('connect-redis').default; +const { createClient } = require('redis'); + +const redisClient = createClient({ + host: 'localhost', + port: 6379 +}); +redisClient.connect(); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Redis Session - Konfiguráció} + \begin{verbatim} +app.use(session({ + store: new RedisStore({ client: redisClient }), + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: true, // HTTPS + maxAge: 1000 * 60 * 60 * 24 // 1 nap + } +})); + +app.post('/login', async (req, res) => { + const user = await authenticateUser(req.body); + req.session.userId = user.id; + req.session.role = user.role; + res.json({ success: true }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Redis Session - Műveletek} + \begin{verbatim} +// Session olvasása +app.get('/profile', (req, res) => { + if (!req.session.userId) { + return res.status(401).json({ error: 'Not logged in' }); + } + res.json({ userId: req.session.userId }); +}); + +// Session módosítása +app.post('/settings', (req, res) => { + req.session.theme = req.body.theme; + res.json({ success: true }); +}); + +// Session törlése (logout) +app.post('/logout', (req, res) => { + req.session.destroy((err) => { + if (err) return res.status(500).send('Error'); + res.clearCookie('connect.sid'); + res.json({ message: 'Logged out' }); + }); +}); + \end{verbatim} +\end{frame} + +\begin{frame}[shrink=15]{Redis Session előnyei} + \begin{columns} + \begin{column}{0.48\textwidth} + \textbf{Előnyök:} + \begin{itemize} + \item Villámgyors (in-memory) + \item Automatikus TTL támogatás + \item Skálázható (cluster mode) + \item Persistence opcionális + \item Pub/Sub támogatás + \end{itemize} + \end{column} + \begin{column}{0.48\textwidth} + \textbf{Használati esetek:} + \begin{itemize} + \item Session store + \item Rate limiting + \item Real-time analytics + \item Cache layer + \item Message queue + \end{itemize} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{Session élettartam} + \begin{block}{Session Timeout} + Session-ök lejárnak idő vagy inaktivitás után. + \end{block} + + \begin{itemize} + \item \textbf{Absolute:} fix lejárat (pl. 24h) + \item \textbf{Idle:} aktivitás hiánya + \item \textbf{Sliding:} aktivitás meghosszabbítja + \end{itemize} + + \begin{exampleblock}{Példa} + Online bank: 5 perc inaktivitás $\to$ kiléptetés. + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Express session példa} + \begin{exampleblock}{express-session használata} + \tiny + \begin{verbatim} +const session = require('express-session'); + +app.use(session({ + secret: 'super-secret-key', + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: true, + maxAge: 1000 * 60 * 60 // 1 óra + } +})); + +app.post('/login', (req, res) => { + req.session.userId = user.id; + res.json({ message: 'Logged in' }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{Cookie biztonság} + \begin{block}{Mitől biztonságos?} + Session ID cookie biztonságos beállításokkal. + \end{block} + + \begin{itemize} + \item \textbf{HttpOnly}: JS nem fér hozzá + \item \textbf{Secure}: csak HTTPS + \item \textbf{SameSite}: CSRF védelem + \item \textbf{Rotálás}: ID frissítés (fixation ellen) + \end{itemize} + + \begin{alertblock}{Session Fixation} + Támadó előre beállított ID-t "ráerőltet" a felhasználóra. + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=5]{Session alapú autorizáció} + \begin{block}{Jogosultságok session-ben} + A session tárolhatja a felhasználó szerepköreit és jogosultságait. + \end{block} + + \begin{itemize} + \item \textbf{Példa:} \texttt{req.session.role = 'Admin'} + \item Middleware ellenőrzi a role-t + \item Ha nincs jogosultság: \texttt{403 Forbidden} + \end{itemize} + + \vspace{0.4cm} + + \begin{exampleblock}{Minta ellenőrzés} + \texttt{if (req.session.role !== 'Admin') return res.status(403);} + \end{exampleblock} +\end{frame} + +\begin{frame}{Session Best Practices} + \begin{itemize} + \item HTTPS használata mindenhol + \item HttpOnly + Secure + SameSite cookie beállítások + \item Session store: Redis vagy DB + \item Rövid idle timeout érzékeny rendszereknél + \item Session ID rotálás bejelentkezéskor + \item Rate limiting és brute force védelem + \end{itemize} + + \vspace{0.5cm} + +\end{frame} + +\begin{frame}{Összefoglalás - Session} + \begin{itemize} + \item Session = szerver-oldali állapot (cookie csak ID) + \item Kényelmes, de skálázásnál kihívás + \item Cookie biztonsági beállítások kritikusak + \item \textbf{Redis a leggyakoribb session store} - gyors, skálázható + \item Timeout és rotáció védelem session támadások ellen + \item Redis TTL automatikus session lejárathoz + \end{itemize} + + \begin{exampleblock}{Produkciós környezet} + Redis cluster + persistence + backup = megbízható session kezelés + \end{exampleblock} +\end{frame} \ No newline at end of file