authn_z
This commit is contained in:
@@ -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}
|
||||
Reference in New Issue
Block a user