Repository navigation
Expand file tree
/
Copy path7.html
More file actions
304 lines (275 loc) · 14.9 KB
/
Copy path7.html
File metadata and controls
304 lines (275 loc) · 14.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="AnnaWiktoria">
<meta name="copyright" content="AnnaWiktoria">
<meta name="description" content="Interaktywny kurs bezpieczeństwa aplikacji webowych. Licencja CC BY-NC-SA.">
<meta http-equiv="Content-Security-Policy" content="default-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
script-src 'self' 'unsafe-inline';
img-src 'self' data:;">
<title>Kurs bezpieczeństwa aplikacji webowych - Lekcja 07: JWT</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<style>
/* Akcent: Cyber Gold (Tokens/Keys) */
:root {
--accent: #ffd600;
--accent-glow: rgba(255, 214, 0, 0.3);
}
/* Kolory do wizualizacji JWT */
.jwt-header {
color: #fb015b;
}
/* Czerwony */
.jwt-payload {
color: #d63aff;
}
/* Fioletowy */
.jwt-sig {
color: #00b9f1;
}
/* Niebieski */
</style>
</head>
<body>
<div class="container">
<header>
<h1>JSON WEB TOKENS</h1>
<p>MODUŁ 07: Anatomia tokena, Algorytm 'None' i Łamanie podpisów</p>
</header>
<!-- 1. ANATOMIA -->
<section>
<h2>> ANATOMIA JWT</h2>
<p>JWT to nie jest "losowy ciąg znaków". To ustrukturyzowany format danych (JSON), zakodowany w Base64Url i
podpisany cyfrowo.</p>
<div class="card">
<h3>STRUKTURA: AAAAA.BBBBB.CCCCC</h3>
<p>Token składa się z 3 części rozdzielonych kropkami:</p>
<div
style="background:#111; padding:15px; border-radius:5px; font-family:monospace; word-break:break-all; font-size:1.1rem;">
<span class="jwt-header">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span>.<span
class="jwt-payload">eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ</span>.<span
class="jwt-sig">SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</span>
</div>
<ul>
<li><strong class="jwt-header">1. HEADER (Czerwony):</strong> Metadane. Mówi "Jestem JWT i używam
algorytmu HS256".</li>
<li><strong class="jwt-payload">2. PAYLOAD (Fioletowy):</strong> Dane użytkownika (Claims). Tu jest
ID, rola, data ważności.</li>
<li><strong class="jwt-sig">3. SIGNATURE (Niebieski):</strong> Podpis cyfrowy. Gwarantuje, że nikt
nie zmienił Payloadu.</li>
</ul>
</div>
<div class="card">
<h3>BASE64 != SZYFROWANIE</h3>
<p>To najważniejsza rzecz do zapamiętania. <strong>Każdy może odczytać treść JWT.</strong> Wystarczy
wrzucić go do <code>jwt.io</code> lub użyć <code>base64 -d</code>.</p>
<p><span class="highlight">Wniosek:</span> Nigdy nie trzymaj haseł ani danych wrażliwych w Payloadzie!
</p>
</div>
</section>
<!-- 2. JAK TO DZIAŁA -->
<section>
<h2>> MECHANIZM PODPISU</h2>
<p>Skoro każdy może edytować Base64, dlaczego nie mogę zmienić sobie <code>"admin": false</code> na
<code>true</code>?</p>
<p>Bo serwer sprawdza <strong>PODPIS (Signature)</strong>.</p>
<pre><code>Signature = HMACSHA256(
base64(Header) + "." + base64(Payload),
SECRET_KEY
)</code></pre>
<p>Jeśli zmienisz choć jeden znak w Payloadzie, podpis przestanie pasować (bo nie znasz
<code>SECRET_KEY</code>, żeby wygenerować nowy). Serwer odrzuci taki token.</p>
</section>
<!-- 3. ATAKI -->
<section>
<h2>> ATAKI NA JWT</h2>
<p>Co może pójść nie tak? Całkiem sporo.</p>
<div class="card">
<h3>1. ALGORITHM: NONE (Obejście podpisu)</h3>
<p>Niektóre biblioteki JWT wspierają algorytm <code>none</code> (używany do debugowania).</p>
<p><strong>Atak:</strong>
<ol>
<li>Bierzesz token.</li>
<li>Zmieniasz w Headerze: <code>"alg": "none"</code>.</li>
<li>Zmieniasz w Payloadzie: <code>"role": "admin"</code>.</li>
<li>Usuwasz podpis (zostawiasz kropkę na końcu).</li>
<li>Wysyłasz do serwera. Jeśli backend jest źle skonfigurowany -> Jesteś adminem.</li>
</ol>
</p>
</div>
<div class="card">
<h3>2. WEAK SECRET (Łamanie HMAC)</h3>
<p>Jeśli programista użył słabego sekretu (np. "secret", "12345", "key"), możesz go złamać offline
używając <strong>Hashcat</strong> lub <strong>John the Ripper</strong>.</p>
<p>Gdy poznasz sekret, możesz generować własne, poprawne tokeny dla dowolnego użytkownika.</p>
</div>
<div class="card">
<h3>3. BRAK WERYFIKACJI PODPISU</h3>
<p>Najgłupszy błąd, ale się zdarza. Serwer dekoduje Payload, ufa mu, ale... zapomina sprawdzić
Signature. Wtedy możesz wpisać tam cokolwiek.</p>
</div>
</section>
<!-- CHECKLIST -->
<section>
<h2>CHECKLISTA / RED FLAGS</h2>
<div class="card checklist-card">
<div class="checklist-item">
<span class="check-icon">⚠</span>
<div><strong>Wrażliwe dane w Payload:</strong> PESEL, adresy, numery telefonów w Base64. To wyciek
danych (Information Disclosure).</div>
</div>
<div class="checklist-item">
<span class="check-icon">⚠</span>
<div><strong>Algorytm 'None':</strong> Zawsze sprawdzaj, czy serwer przyjmie token z wyłączonym
podpisywaniem.</div>
</div>
<div class="checklist-item">
<span class="check-icon">⚠</span>
<div><strong>Brak daty ważności (exp):</strong> Token, który nigdy nie wygasa, jest niebezpieczny.
Jeśli ktoś go ukradnie, ma dostęp na zawsze.</div>
</div>
<div class="checklist-item">
<span class="check-icon">⚠</span>
<div><strong>Token w LocalStorage:</strong> JWT trzymany w LocalStorage jest podatny na kradzież
przez XSS (tak jak ciastka bez HttpOnly).</div>
</div>
</div>
</section>
<!-- QUIZ -->
<section id="quiz-section">
<h2>🎓 WERYFIKACJA WIEDZY</h2>
<div id="quiz-container"></div>
</section>
<footer>
<p style="text-align: center; margin-top: 50px; color: #555;">© 2026 Hack & Learn: Web Edition | Moduł 07</p>
</footer>
</div>
<!-- DANE DO QUIZU -->
<script>
const quizData = [
{
question: "Z ilu części składa się token JWT i czym są rozdzielone?",
options: [
"Z dwóch części rozdzielonych myślnikiem.",
"Z trzech części rozdzielonych kropką (Header.Payload.Signature).",
"To jeden ciąg znaków zaszyfrowany AES.",
"Z nagłówka i ciała, jak HTML."
],
correct: 1,
explanation: "Struktura to AAAAA.BBBBB.CCCCC. Pierwsza część to nagłówek, druga to dane (payload), trzecia to podpis cyfrowy."
},
{
question: "Czy dane w sekcji Payload (środek tokena) są zaszyfrowane?",
options: [
"Tak, są bezpieczne i nikt ich nie odczyta.",
"Nie. Są tylko zakodowane w Base64Url. Każdy może je odkodować i przeczytać.",
"Tak, ale tylko serwer ma klucz.",
"Zależy od przeglądarki."
],
correct: 1,
explanation: "To kluczowe dla pentestera. Base64 to kodowanie (jak tłumaczenie na inny język), a nie szyfrowanie. Wrzuć token do jwt.io, a zobaczysz wszystko."
},
{
type: "scenario",
question: "Przechwyciłeś token JWT. Zmieniasz w nim 'user_id' z 100 na 1 (admin), ale nie ruszasz podpisu. Wysyłasz go do serwera. Co się stanie (w poprawnie zabezpieczonym systemie)?",
options: [
"Serwer zaakceptuje token i da Ci uprawnienia admina.",
"Serwer odrzuci token, ponieważ podpis nie będzie pasował do zmienionego Payloadu.",
"Serwer naprawi podpis automatycznie.",
"Wystąpi błąd 500."
],
correct: 1,
explanation: "Podpis jest matematyczną sumą oryginału. Zmiana danych sprawia, że stary podpis staje się nieważny. Bez sekretnego klucza nie wygenerujesz nowego, poprawnego podpisu."
},
{
question: "Na czym polega atak 'None Algorithm'?",
options: [
"Na wysłaniu pustego tokena.",
"Na zmianie nagłówka tokena na {'alg': 'none'} i usunięciu podpisu, licząc na to, że serwer pominie weryfikację.",
"Na usunięciu Payloadu.",
"Na ataku DDoS."
],
correct: 1,
explanation: "To błąd implementacji w niektórych bibliotekach. Jeśli serwer widzi 'alg: none', może uznać: 'Aha, ten token nie wymaga podpisu' i zaufać danym."
},
{
question: "Do czego służy pole 'exp' (Expiration Time) w JWT?",
options: [
"Określa datę ważności tokena. Po tym czasie serwer powinien go odrzucić.",
"To skrót od 'export'.",
"Zawiera hasło użytkownika.",
"Określa poziom doświadczenia (experience) w grze."
],
correct: 0,
explanation: "Tokeny JWT (zazwyczaj) nie mogą być unieważnione (revoked) przed czasem. Dlatego 'exp' powinien być krótki (np. 15 min), aby zminimalizować ryzyko w razie kradzieży."
},
{
type: "scenario",
question: "Znalazłeś token JWT, który w Payloadzie zawiera: 'eyJlbWFpbCI6ImphbkBmaXJtYS5wbCIsInBlc2VsIjoiOTAwMTAx...'. Dekodujesz go i widzisz numery PESEL. Jakie to ryzyko?",
options: [
"Critical - Wyciek danych osobowych (Information Disclosure).",
"Low - Base64 jest bezpieczny.",
"None - To normalna praktyka.",
"Medium - Tylko jeśli PESEL jest prawdziwy."
],
correct: 0,
explanation: "Przechowywanie danych wrażliwych (PII) w JWT to błąd. Tokeny często lądują w logach serwera, historii przeglądarki lub LocalStorage, gdzie są łatwo dostępne."
},
{
question: "W jaki sposób można złamać podpis JWT (HS256) offline?",
options: [
"Nie da się, to niemożliwe.",
"Używając narzędzi typu Hashcat/John the Ripper do ataku słownikowego (brute-force) na sekretny klucz.",
"Wysyłając milion zapytań do serwera.",
"Zmieniając datę w systemie."
],
correct: 1,
explanation: "Jeśli klucz (Secret) jest słaby (np. 'secret123'), haker może na swoim superkomputerze sprawdzać miliony kombinacji na sekundę, aż znajdzie tę, która pasuje do podpisu."
},
{
type: "scenario",
question: "Dlaczego trzymanie JWT w LocalStorage jest uważane za ryzykowne?",
options: [
"Bo zajmuje dużo miejsca.",
"Bo każdy kod JavaScript (w tym ten wstrzyknięty przez XSS) ma dostęp do LocalStorage i może ukraść token.",
"Bo LocalStorage kasuje się po restarcie komputera.",
"Bo nie działa w trybie Incognito."
],
correct: 1,
explanation: "To główna wada JWT na frontendzie. Ciasteczka `HttpOnly` są chronione przed XSS. LocalStorage jest otwarte dla każdego skryptu JS."
},
{
question: "Co to jest 'alg' w nagłówku JWT?",
options: [
"Algorytm użyty do podpisania tokena (np. HS256, RS256).",
"Algebraiczna suma kontrolna.",
"Skrót od 'algorithm' oznaczający szyfrowanie danych.",
"Nazwa użytkownika (alias)."
],
correct: 0,
explanation: "Nagłówek mówi serwerowi: 'Użyj algorytmu X, aby sprawdzić mój podpis'. Ataki polegają często na manipulacji tym polem (None attack, Algo Confusion)."
},
{
type: "scenario",
question: "Serwer używa asymetrycznego RS256 (Klucz prywatny do podpisu, publiczny do weryfikacji). Atakujący zmienia w tokenie algorytm na symetryczny HS256 i podpisuje token KLUCZEM PUBLICZNYM (który jest jawny). Serwer to akceptuje. Co to za atak?",
options: [
"SQL Injection.",
"Key Confusion / Algorithm Confusion Attack.",
"Brute Force.",
"Man in the Middle."
],
correct: 1,
explanation: "To zaawansowany i piękny atak. Serwer myśli: 'Mam zweryfikować HS256? Ok, użyję mojego klucza'. Ale w RS256 kluczem weryfikującym jest plik publiczny. Atakujący wymusił użycie klucza publicznego jako 'sekretu' HMAC."
}
];
</script>
<script src="quiz.js"></script>
</body>
</html>