Skip to main content
Практика завдань

Що таке JWT і як він працює?

JWT (JSON Web Token) - це відкритий стандарт (RFC 7519) для безпечної передачі інформації між сторонами у вигляді JSON об'єкта. Зазвичай використовується для автентифікації та обміну інформацією.

Структура JWT

JWT складається з трьох частин, розділених крапками (.):

``` xxxxx.yyyyy.zzzzz ```

1. Header (Заголовок)

Містить тип токена та алгоритм підпису:

```json { "alg": "HS256", "typ": "JWT" } ```

2. Payload (Корисне навантаження)

Містить claims (дані користувача):

```json { "sub": "1234567890", "name": "Іван Петренко", "email": "ivan@example.com", "iat": 1516239022, "exp": 1516242622 } ```

3. Signature (Підпис)

Забезпечує що токен не було підроблено:

```javascript HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) ```

Повний приклад

``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ```

Як працює автентифікація

1. Логін користувача

```javascript // Backend (Node.js/Express) app.post('/api/login', async (req, res) => { const { email, password } = req.body;

// Перевірка облікових даних const user = await User.findByEmail(email); if (!user || !await user.checkPassword(password)) { return res.status(401).json({ error: 'Невірні облікові дані' }); }

// Генерація JWT const jwt = require('jsonwebtoken'); const token = jwt.sign( { userId: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET, { expiresIn: '24h' } );

res.json({ token }); }); ```

2. Клієнт зберігає токен

```javascript // Frontend const login = async (email, password) => { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) });

const { token } = await response.json();

// Зберігання в localStorage localStorage.setItem('token', token); // Або в пам'яті для кращої безпеки // sessionStorage.setItem('token', token); }; ```

3. Клієнт відправляє токен

```javascript // Включення токена в запити const getData = async () => { const token = localStorage.getItem('token');

const response = await fetch('/api/protected', { headers: { 'Authorization': `Bearer ${token}` } });

return response.json(); }; ```

4. Сервер перевіряє токен

```javascript // Middleware для перевірки JWT const jwt = require('jsonwebtoken');

const verifyToken = (req, res, next) => { const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Токен не надано' }); }

const token = authHeader.substring(7);

try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (error) { return res.status(401).json({ error: 'Невірний токен' }); } };

// Захищений роут app.get('/api/protected', verifyToken, (req, res) => { res.json({ data: 'Секретні дані', user: req.user }); }); ```

JWT Claims

Стандартні Claims (зареєстровані)

  • `iss` (issuer): Видавець токена
  • `sub` (subject): ID користувача
  • `aud` (audience): Призначений отримувач
  • `exp` (expiration): Час закінчення дії
  • `iat` (issued at): Час створення токена
  • `nbf` (not before): Токен не дійсний до цього часу

```javascript const token = jwt.sign( { iss: 'myapp.com', sub: '123', aud: 'myapp-api', exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 година iat: Math.floor(Date.now() / 1000) }, secret ); ```

Найкращі практики

1. Короткий час дії

```javascript // Access token: короткоживучий const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' });

// Refresh token: довгоживучий, більш безпечний const refreshToken = jwt.sign(payload, refreshSecret, { expiresIn: '7d' }); ```

2. Безпечне зберігання

```javascript // ❌ Погано: localStorage вразливий до XSS localStorage.setItem('token', token);

// ✅ Краще: httpOnly cookie res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'strict' });

// ✅ Найкраще: Короткоживучі токени + механізм оновлення ```

3. Патерн оновлення токена

```javascript // Frontend let accessToken = ''; let refreshToken = '';

const refreshAccessToken = async () => { const response = await fetch('/api/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }) });

const { accessToken: newToken } = await response.json(); accessToken = newToken; };

// Axios interceptor для автоматичного оновлення axios.interceptors.response.use( response => response, async error => { if (error.response?.status === 401) { await refreshAccessToken(); // Повторити оригінальний запит return axios(error.config); } return Promise.reject(error); } ); ```

Типові питання на співбесіді

  1. П: JWT vs автентифікація на основі сесій? В: JWT без стану (немає серверного зберігання), краще масштабується. Сесії зберігають стан на сервері, легше відкликати. JWT краще для мікросервісів/API.

  2. П: Чи можна розшифрувати JWT? В: JWT ПІДПИСАНИЙ, а не зашифрований. Будь-хто може декодувати та прочитати payload. Для чутливих даних використовуйте JWE (JSON Web Encryption).

  3. П: Як анулювати JWT? В: JWT не можна анулювати (без стану). Рішення: короткий час дії, чорний список (втрачає перевагу без стану), або використання refresh токенів.

  4. П: Де зберігати JWT? В: HttpOnly cookies (найкраще), пам'ять (скидається при оновленні), localStorage (вразливий до XSS). Ніколи не зберігайте в localStorage для чутливих додатків.

Міркування безпеки

РОБІТЬ:

  • Використовуйте сильні секрети (256+ біт)
  • Встановлюйте короткий час дії
  • Використовуйте лише HTTPS
  • Валідуйте токени на кожному запиті
  • Впроваджуйте ротацію refresh токенів

НЕ РОБІТЬ:

  • Зберігайте чутливі дані в payload
  • Використовуйте слабкі секрети
  • Пропускайте валідацію expiration
  • Довіряйте клієнтському декодуванню
  • Зберігайте в localStorage для чутливих даних

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань