471 lines
12 KiB
TeX
471 lines
12 KiB
TeX
\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}
|
|
|