Tehnologija Vodič

Kako da zaštitite svoje JavaScript aplikacije

javascript

JavaScript je programski jezik koje je danas neizostavan deo svakog modernog web okruženja. U početku je bio namenjen isključivo za frontend, odnosno za izvršavanje u browser-u, ali je vremenom proširio svoju primenu i na backend. Danas ga koristimo svuda, od manipulacije DOM-om i dinamičkog generisanja sadržaja, pa do kompleksnih SPA (Single-Page Application) rešenja i server-side okruženja – korišćenjem Node.js. 

Nažalost, upravo ova popularnost je dovela do toga da je JavaSript često meta napada. Ovo se pre svega odnosi na činjenicu da se JavaScript kod izvršava uglavnom u browseru klijenta. Ukoliko napadači uspeju da pristupe browseru korisnika, onda mogu da probaju i da manipulišu kodom, da iskoriste njegove ranjivosti ili da se ubace u komunikacioni tok između servera i klijenta.

U ovom tekstu ćemo detaljno predstaviti neke od najboljih sigurnosnih praksi i saveta koji vam mogu pomoći da zaštitite svoje JavaScript web aplikacije. 

Proćićemo kroz najefikasnije metode zaštite, tehnologije i alate koji igraju važnu ulogu u bezbednosti JavaScript aplikacija, sa posebnim fokusom na najčešće probleme kao što su Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF), manipulacija kolačićima, nebezbedno čuvanje tokena i slično.

Zašto je JavaScript posebno ranjiv?

Kao što smo već pomenuli, JavaScript se, za razliku od drugih jezika koji se izvršavaju samo na serveru, izvršava uglavnom u okruženju korisničkog browsera (klijentska strana). To znači da krajnji korisnici (ali nažalost i zlonamerni napadači) imaju potencijalno direktan pristup kodu, iako je on u praksi najčešće minimizovan ili bundle-ovan. 

JavaScript se u velikoj meri oslanja na dinamički generisan sadržaj, interakciju sa DOM-om, asinhronu komunikaciju, što u nekim slučajevima može da otvori prostor za napade ukoliko se na vreme ne primene odgovarajući bezbednosni mehanizmi.

Pored toga, mnogi web sajtovi i aplikacije koriste JavaScript framework-ove i biblioteke, poput React-a, Angular-a ili Vue.js-a, gde jedan propust ili neodgovarajuća konfiguracija mogu biti iskorišćeni za napade.

Važno je da imate u vidu da se JavaScript kod može menjati od strane napadača na više načina:

  • Modifikacija u transportu: Ako sajt nije na HTTPS-u, napadač može presresti HTTP saobraćaj i ubaciti zlonameran kod.
  • DevTools manipulacija: Napredniji korisnici ili napadači mogu da koriste alate poput Chrome DevTools-a da čitaju i menjaju trenutno učitane skripte.
  • Eksterni resursi: Ako se biblioteke učitavaju sa neproverenih CDN linkova, postoji rizik da će neki CDN biti kompromitovan i da će se preko njega distribuirati maliciozni kod.

Česte vrste napada

  • Cross-Site Scripting (XSS) – Ubacivanje malicioznog koda na web stranicu/web-sajt, kako bi se iz korisnikovog browsera ukrali kolačići, tokeni ili izvršavale neželjene skripte.
  • Cross-Site Request Forgery (CSRF) – Prevara preko browsera kako bi poslao neovlašćeni zahtev ka serveru, predstavljajući se kao legitimni korisnik.
  • Clickjacking – Manipulacija izgledom ili sadržajem web stranice/web sajta kako bi korisnici kliknuli na nešto čime omogućavaju neovlašćeni pristup.
  • Napadi na sesije i kolačiće – Krađa ili manipulacija kolačićima sesija i tokenima autentifikacije.
  • Napadi na biblioteke/packet-e – Uvođenje zlonamernog koda kroz eksterne biblioteke, zavisnosti i plugine.

Kada govorimo o sigurnosnim pretnjama, dobro je imati na umu da se one ne odnose isključivo na standardne web aplikacije, već i na hibridne mobilne aplikacije ili Electron aplikacije koje koriste JavaScript na desktopu. Osnovni princip je isti: svuda gde se pokreće JavaScript, mogu da se pojave slični obrasci napada.

U nastavku teksta ćemo se detaljnije pozabaviti navedenim vrstama napada i načinima zaštite od njih.

Cross-Site Scripting (XSS) i kako ga sprečiti

Šta je XSS?

Cross-Site Scripting (XSS) omogućava napadaču da ubaci sopstveni JavaScript kod na neku stranicu. Taj kod se onda izvršava u browseru krajnjeg korisnika. Ovako ubačeni kod može da ukrade osetljive podatke iz kolačića, lokalne memorije browsera, ili da iskoristi druge bezbednosne rupe.

Postoji više vrsta XSS napada, ali najčešće se pominju:

  1. Reflektovani (Reflected XSS): Maliciozni kod se nalazi u URL parametru ili obrascu (formi), a server ne vrši adekvatnu validaciju i vrati taj kod korisniku.
  2. Skladišteni (Stored XSS): Maliciozni kod se trajno skladišti na serveru (npr. u bazi). Kada neko poseti web stranicu/web sajt, kod se učitava i izvršava.
  3. DOM-based XSS: Napad se odvija isključivo na klijentskoj strani kroz manipulaciju DOM-om, najčešće kroz nebezbedne metode poput innerHTML ili document.write.

DOM-based XSS se može desiti čak i ako server ispravno enkodira sadržaj, ukoliko se klijentski skript naknadno osloni na nečist unos i ubaci ga u DOM. Na primer, ako parse-ujemo location.hash ili location.search i ubacimo vrednost u DOM, moguće je da će napadač da kreira zlonamerni link sa #<script>... i da na taj način izvrši napad.

Metodologija zaštite od XSS-a

  1. Escape i sanitizacija – Bilo kakav korisnički unos (obrasci, parametri u URL-u, poruke, komentari) mora biti escape-ovan i/ili sanitize-ovan pre nego što se ubaci u DOM ili prikaže.
  2. Content Security Policy (CSP) – Postavljanje Content Security Policy zaglavlja na serveru kako bi se ograničio izvor skripti, stilova i ostalih resursa. Ovo značajno otežava napadačima da ubace i pokrenu maliciozni kod.
  3. Izbegavanje eval i sličnih funkcija – Funkcije poput eval(), new Function(), setTimeout(string) i setInterval(string) mogu da izvrše proizvoljni JavaScript kod i tako postanu mete napada.
  4. Korišćenje odgovarajućih biblioteka – Popularni JavaScript frejmvork-ovi (React, Vue, Angular) imaju ugrađene zaštite od XSS-a, ali pod uslovom da su pravilno konfigurisanе i da se ne koriste rizične metode ubacivanja HTML-a.

Neke biblioteke, poput Angular-a, imaju svoj mehanizam tzv. santize-inga ugrađen u template sistem. Ipak, ako eksplicitno koristite funkcije kao što su bypassSecurityTrustHtml, važno je da znate da time zaobilazite podrazumevanu zaštitu, što može dovesti do propusta ukoliko se unosi ne proveravaju na drugi način.

Primeri potencijalnih XSS ranjivosti

Pretpostavimo da imamo jednostavan Express server koji prima parametar name iz GET zahteva i ispisuje ga na strani:

// Primer potencijalno ranjivog koda (nemojte koristiti u praksi)

app.get('/greet', (req, res) => {

  const name = req.query.name; // Unos korisnika

  res.send(`

    <html>

      <body>

        <h1>Pozdrav, ${name}!</h1>

      </body>

    </html>

  `);

});

Ako korisnik prosledi URL poput:

http://nekisajt.rs/greet?name=<script>alert('XSS');</script>

Browser će izvršiti <script>alert('XSS');</script>, što predstavlja jasan XSS.

Iako je alert('XSS') suviše jednostavan primer, u stvarnosti napadači mogu da ubace složeniji kod koji krade kolačiće, lokalno skladištene token-e i sl., ili vrši phishing napade direktno sa ugrožene stranice.

Kako se zaštititi od XSS napada?

1. Escape ili enkodiranje korisničkog unosa
Možete da koristite biblioteke kao što su escape-html u Node.js ili da izgradite sopstvenu funkciju za enkodiranje specijalnih karaktera <, >, &, ', " i sl. Na primer:

function sanitizeInput(str) {

  return str

    .replace(/&/g, '&amp;')

    .replace(/</g, '&lt;')

    .replace(/>/g, '&gt;')

    .replace(/"/g, '&quot;')

    .replace(/'/g, ''');

}

Zatim:

app.get('/greet', (req, res) => {

  const rawName = req.query.name || '';

  const safeName = sanitizeInput(rawName);

  res.send(`

    <html>

      <body>

        <h1>Pozdrav, ${safeName}!</h1>

      </body>

    </html>

  `);

});

2. Implementacija Content Security Policy (CSP)
Na primer, u Express aplikaciji možemo postaviti headere:

app.use((req, res, next) => {

  res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self'");

  return next();

});

Ovo ograničava izvršavanje skripti samo na one koje potiču sa istog domena ('self'). U složenijim aplikacijama, CSP može da bude dosta detaljniji.

3. Izbegavanje rizičnih DOM metoda
Umesto innerHTML, bolje je da koriste textContent ili neki drugi proveren način ubacivanja HTML-a (poput biblioteka za šabloniranje i sanitizaciju).

Čak i ako koristite innerHTML isključivo za određene segmente koda (npr. rendirovanje sigurnih template-a), vremenom se u kodu može desiti da se innerHTML iskoristi pogrešno. Stoga je preporuka da se generalno izbegava ova metoda i da umesto nje koristite neke sigurnije metode.

Cross-Site Request Forgery (CSRF)

CSRF je napad kod kojeg se browser korisnika navodi da izvrši nenamerne zahteve ka serveru, koristeći prethodno uspostavljenu autentifikaciju ili sesiju. Napadač najčešće postavlja link ili skript koji nevidljivo šalje zahtev (PUT, POST, DELETE, itd.) ka legitimnom web sajtu ili aplikaciji na kojoj je korisnik već prijavljen. Kako browser automatski šalje kolačiće za verifikaciju, server misli da zahtev potiče od legitimnog korisnika.

Tipičan primer je scenario gde korisnik ode na zlonamerni sajt na kojem recimo postoji <img src="http://nekisajt.rs/deleteUser?userId=123">. Browser će automatski poslati kolačiće ka nekisajt.rs, i server može da izvrši brisanje, misleći da je zahtev poslao legitimni korisnik.

Tehnike zaštite od CSRF napada

  1. CSRF tokeni – Ovo je najpopularniji pristup. Server generiše jedinstveni token (npr. u sesiji ili baziran na ključu), ubacuje ga u formu ili glavni HTML, a nakon toga proverava da li token u zahtevu odgovara tokenu sa servera.
  2. SameSite kolačići – Kolačić sa opcijom SameSite=Lax ili Strict sprečava većinu CSRF napada, jer se kolačić ne šalje prilikom zahteva sa drugog domena.
  3. Dvosmerna validacija – Kombinacija tokena, provere referera i origin zaglavlja, uz upotrebu API ključeva ili JWT.

SameSite atribut je odlična linija odbrane, ali nije univerzalno rešenje. Nekim aplikacijama je potrebno da kolačići budu poslati i sa eksternih domena (npr. ako imate integracije), pa u tim slučajevima SameSite = None mora da se koristi, što otvara mogućnost CSRF-a i zahteva povratak na klasične CSRF tokene.

CSRF token primer u Express-u

Korišćenjem biblioteke poput csurf, možemo brzo da dodamo CSRF zaštitu:

const express = require('express');

const session = require('express-session');

const csrf = require('csurf');

const app = express();

app.use(session({ secret: 'tajna', resave: false, saveUninitialized: true }));

app.use(csrf());

app.get('/form', (req, res) => {

  // CSRF token koji se ubacuje u formu

  res.send(`

    <form action="/process" method="POST">

      <input type="hidden" name="_csrf" value="${req.csrfToken()}">

      <input type="text" name="username" placeholder="Korisničko ime">

      <button type="submit">Pošalji</button>

    </form>

  `);

});

app.post('/process', (req, res) => {

  // Ako je token neispravan ili nedostaje, 'csurf' baca grešku

  res.send('Uspešno poslato!');

});

app.listen(3000, () => console.log('Server pokrenut na portu 3000'));

Ovde se generiše CSRF token, koji se čuva u sesiji i umeće u formu. Prilikom svakog POST zahteva, biblioteka proverava podudaranje tokena.

U RESTful API okruženjima, ponekad se CSRF brani tako što se zahtevaju tzv. double submit cookie mehanizmi ili se oslanja na JSON-based komunikaciju gde ne postoji implicitno slanje kolačića. Kako god, klasični CSRF tokeni i SameSite kolačići ostaju standardno rešenje za web forme.

Zaštita kolačića i sesije

  1. HttpOnly: Sprečava JavaScript na klijentskoj strani da pristupi kolačiću, pa je teže ukrasti sesiju preko XSS-a.
  2. Secure: Omogućava slanje kolačića samo preko HTTPS-a, čime se smanjuje rizik presretanja na mreži.
  3. SameSite: Sprečava slanje kolačića sa drugih domena, čime se ublažava CSRF.

Vrednost SameSite može biti Strict, Lax ili None. Strict je najrestriktivniji (kolačić se ne šalje nikad ako se pristupa sa strane trećeg lica), Lax dozvoljava slanje kolačića za bezbedne tipove zahteva (npr. GET za navigaciju), a None znači da se kolačić uvek šalje, ali uz obavezni Secure atribut.

Dobri primeri u Node.js (Express Session)

app.use(session({

  secret: 'dovoljnoJakaTajnaRec',

  resave: false,

  saveUninitialized: false,

  cookie: {

    httpOnly: true,

    secure: true,  // Neophodno da bi radilo preko HTTPS

    sameSite: 'strict'

  }

}));

Ovim pristupom sesioni kolačić će imati sva tri bitna atributa: HttpOnly, Secure i SameSite.

Kada se radi lokalni razvoj, secure: true može biti problematičan ako nemate lokalni HTTPS sertifikat. U tom slučaju privremeno možete isključiti secure, ali obavezno ga uključite nakon migracije na produkciono okruženje.

Zašto je važno zaštititi sesiju?

Ako napadač dođe u posed kolačića sesije, on potencijalno može da se predstavi kao legitimni korisnik. Ovo važi ne samo za XSS ili CSRF, već i za situacije poput presretanja saobraćaja (ako nije obezbeđen HTTPS), neodgovarajućeg čuvanja tokena u lokalnoj memoriji browsera i sl.

U nekim slučajevima je preporučljivo periodično rotirati session ID (na primer, nakon uspešnog logovanja), kako bi se smanjila šansa da stari session ID bude iskorišćen. Takode, ako korisnik ručno klikne na Odjavi se, potrebno je invalidirati session na serveru, a ne samo obrisati kolačić na klijentu.

Manipulacija DOM-om

Sa pojavom popularnih framework-ova (React, Vue.js, Angular), ručna manipulacija DOM-om je ređe potrebna, ali i dalje postoji mnogo aplikacija koje koriste čisti JavaScript ili jQuery. Najčešća greška je upotreba innerHTML sa neproverenim unosom.

// Rizičan primer

const userInput = document.getElementById('some-input').value;

document.getElementById('content').innerHTML = userInput;

Ako userInput sadrži maliciozni skript, on će se izvršiti. Zato se preporučuje ili sanitizacija pre ubacivanja ili korišćenje textContent.

jQuery funkcije kao na primer .html() i .append() takode mogu da dovedu do XSS napada ako ubacuju neproveren HTML. Takođe, u starijim aplikacijama možete naići čak i na document.write(), što je još rizičnije jer piše direktno u DOM toka stranice.

Sigurni načini za prikaz korisničkog unosa

  • textContent: Ako ne moramo da prikažemo HTML, textContent je najbezbedniji način.
  • Korišćenje template engine-a: Na serveru se obrađuje korisnički unos, a zatim renderuje kroz npr. EJS, Handlebars ili slične engine-e koji imaju podrazumevano enkodiranje karaktera.
  • Biblioteke za sanitizaciju: Na klijentskoj strani možemo da koristimo biblioteke poput DOMPurify koje čiste maliciozne tagove i atribute iz HTML-a.

React, Vue i Angular

Moderni framework-ovi imaju sopstvene mehanizme za zaštitu od XSS-a. Recimo u React-u, sve što se renderuje u JSX-u se automatski enkodira. Međutim, problem nastaje kad koristite dangerouslySetInnerHTML. Tada morate da budete sigurni da je taj HTML bezbedan.

U Vue.js-u, podrazumevano dvostruko vezivanje (v-bind) enkodira HTML, ali v-html direktiva ubacuje HTML onakav kakav je i mora da se pažljivo koristi. U Angular-u slično važi za [innerHTML]. Sve su to mesta koja zahtevaju dodatnu opreznost.

Content Security Policy (CSP)

Content Security Policy je HTTP zaglavlje (Content-Security-Policy) koje omogućava administratoru sajta da kontroliše koji izvori JavaScript koda, stilova, slika, fontova i drugih resursa mogu da se učitavaju. Na taj način se sprečava da aplikacija učitava ili izvrši resurse iz neproverenih izvora.

CSP nudi dodatne direktive, poput upgrade-insecure-requests, koja automatski preusmerava sve resurse sa HTTP na HTTPS. Tu je i report-uri ili report-to za slanje izveštaja o kršenjima CSP-a na određeni server.

Kako funkcioniše?

Kroz CSP direktive možete da postavite pravila. Na primer:

  • default-src 'self' znači da svi resursi, ako nije drugačije navedeno, treba da dolaze sa istog domena.
  • script-src 'self' znači da su skripte dozvoljene samo ako potiču sa istog domena.
  • style-src 'self' 'unsafe-inline' dozvoljava stilove i inline stilove, ali inline stilovi se uglavnom ne preporučuju ako želite što veću sigurnost.

Primer implementacije CSP u Express aplikaciji

app.use((req, res, next) => {

  res.setHeader("Content-Security-Policy", 

    "default-src 'self'; " +

    "script-src 'self' https://cdn.nekisajt.rs; " +

    "style-src 'self' 'unsafe-inline'; " +

    "img-src 'self' data:; " +

    "object-src 'none'; " +

    "frame-ancestors 'none';");

  next();

});

Ovakva konfiguracija je samo primer – prilagodićete je prema konkretnim potrebama aplikacije.

object-src 'none' sprečava učitavanje flash ili plug-in objekata. frame-ancestors 'none' sprečava da sajt bude embedovan u iframe-ove. Možete koristiti nonce ili hash u script-src kako biste dozvolili samo skripte sa određenim potpisom.

Report-Only mode

Za testiranje je korisno koristiti Content-Security-Policy-Report-Only zaglavlje, koje neće blokirati resurse već će samo prijavljivati kršenja CSP-a, što pomaže u fazi razvoja i debagovanja.

U Report-Only modu dobićete detaljne informacije u konzoli browsera o svim prekršajima – npr. ako neka eksterna skripta nije dozvoljena, videćete tačan izveštaj. Ovo pomaže da postepeno pooštrite CSP pre nego što ga postavite u produkciju.

Upravljanje zavisnostima i sigurnost paketa

Često se u JavaScript projektima koristi veliki broj eksternih paketa iz NPM-a. Iako to značajno ubrzava razvoj, svaka biblioteka može da sadrži ranjivosti ili maliciozni kod. Napadači tako mogu da ubace trojanca u neku popularnu biblioteku, nakon čega veliki broj aplikacija postaje ranjiv.

Supply chain napadi postaju sve češći. Napadač kompromituje nalog popularnog paketa, ubaci zlonameran kod u novi release, a zatim korisnici, nesvesni opasnosti, npm install-iraju i povlače kompromitovani paket.

Dobre prakse

  1. Redovno ažuriranje: Zastarele verzije biblioteka često imaju poznate sigurnosne propuste.
  2. Provera reputacije: Pogledajte koliko je biblioteka popularna, ko je održava, koliko ima pozitivnih review-a, koliko često se ažurira, da li postoji istorijat bezbednosnih incidenata.
  3. Lock fajlovi: U projektima koristite package-lock.json ili yarn.lock da biste sprečili nenamerno ažuriranje paketa.
  4. Sigurnosna skeniranja: Alati poput npm audit, snyk, yarn audit mogu automatski da pronađu poznate ranjivosti.
  5. Pregled koda (ako je moguće): Za ključne biblioteke od suštinskog značaja, dobra je ideja da napravite interni code review, makar na nivou osnovnih stvari.

U ozbiljnim kompanijama postoji dev sec ops kultura gde sve nove biblioteke i nove verzije prolaze kroz automatizovane testove i skenere. Ukoliko se otkrije ranjivost ili maliciozan potpis, integracioni sistem blokira taj update i obaveštava tim.

Primera radi – npm audit

npm audit

# Rezultat može da prikaže listu paketa sa ranjivostima i

# uputstva za ažuriranje ili patch.

npm audit fix pokušava automatski da popravi ranjivosti, ali važno je da budete oprezni – ponekad ažuriranje određene zavisnosti može da obori aplikaciju. Uvek testirajte pre nego što primenite takve izmene u produkciji.

Sigurna konfiguracija Node.js servera i hosting okruženja

Kako bi se sprečilo presretanje podataka (uključujući kolačiće sesije), preporuka je da uvek koristite HTTPS. Možete da koristite besplatan SSL sertifikat (npr. Let’s Encrypt) ili neki plaćeni sertifikat.

Bez HTTPS-a, svi zahtevi i odgovori, uključujući sesione kolačiće, putuju bez enkripcije kroz mrežu. Na javnim Wi-Fi mrežama ili kompromitovanim ruterima to je nebezbedno i potencijalno izlaže korisnike session hijacking-u.

Rate limiting i zaštita od bruteforce-a

Napadači mogu da pokušaju da obore server ili da pogode lozinke brute force napadom. Korišćenjem express-rate-limit ili sličnih biblioteka možemo ograničiti broj zahteva po IP adresi.

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({

  windowMs: 15 * 60 * 1000, // 15 minuta

  max: 100, // Maksimalan broj zahteva

});

app.use(limiter);

U produkciji je korisno imati i napredniju verziju rate limiting-a, npr. integrisanu sa Redis-om, koja pamti IP adrese i usporava ili blokira zahteve. Takode se može kombinovati sa CAPTCHA-om za zaštitu formulara za prijavljivanje.

Helmet

helmet je Express middleware koji postavlja razna sigurnosna zaglavlja automatski (uključujući i CSP, HSTS, XSS zaštitu i sl.):

const helmet = require('helmet');

app.use(helmet());

Time dobijamo višeslojnu zaštitu, jer helmet podrazumevano postavlja X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security i druga bezbednosna zaglavlja.

helmet() je modularan i možete da deaktivirate ili prilagodite određene zaštite. Na primer, ako vam treba da dozvolite iframe sa istog domena, konfigurišite frameguard: { action: 'sameorigin' }.

Upravljanje autentifikacijom i autorizacijom

  1. Jake lozinke – Uputite korisnike da kreiraju jake lozinke.
  2. Dvofaktorska autentifikacija (2FA) – SMS, TOTP (Google Authenticator) ili FIDO2 standard.
  3. Hash-ovanje lozinki – Na serveru lozinke treba čuvati u hash-ovanom obliku uz jaki algoritam (npr. bcrypt, Argon2).

Jaka lozinka treba da podrazumeva minimalnu dužinu (npr. 8 do 12 karaktera), kombinaciju slova, brojeva i posebnih karaktera. Ipak, bolje je usmeriti korisnike na dugačku passphrase politiku (npr. 20+ karaktera)..

Bezbedni API ključevi i tokeni

Ako koristite autentifikaciju zasnovanu na tokenu (npr. JWT), obavezno je:

  • Korišćenje HTTPS-a kako token ne bi bio presretnut.
  • Kratak rok trajanja tokena (short-lived tokens).
  • Obnovljivi (refresh) tokeni koji se čuvaju na bezbedan način.
  • Provera potpisivanja i aud, iss, exp polja unutar JWT-a.

Kada koristite JWT, preporuka je da stavite što je moguće kraći exp (npr. 15 minuta), a da se koristite refresh token mehanizam za produženje sesije. Time smanjujete vremenski prozor u kome može da se iskoristi ukradeni token.

Česta greška – čuvanje tokena u localStorage

Ako čuvate JWT u localStorage, rizikujete da dođe do krađe tokena preko XSS napada, jer je localStorage dostupan JavaScript-u. Alternativa može biti čuvanje tokena u HttpOnly kolačiću (mada treba paziti i na CSRF rizike).

Ako baš morate da koristite localStorage, obavezno vodite računa da XSS napad ne omogući čitanje tog tokena. To zahteva striktnu kontrolu DOM-a i implementaciju jakog CSP-a. Ipak, u praksi je češće i sigurnije držati tokene u HttpOnly kolačićima sa SameSite i Secure atributima.

Clickjacking i zaštita od iframe-ova

Kod clickjacking-a napadač ubacuje legitimnu stranicu unutar nevidljivog iframe sloja, a iznad postavlja lažno dugme ili transparentne elemente, navodeći korisnika da klikne na nešto sasvim drugo, dok se zapravo klik izvršava na legitimnoj stranici.

Ovakav napad može da se iskoristi i za like-jacking na društvenim mrežama, npr. korisnik misli da klikne na neko bezazleno dugme, dok zapravo klikne na Like na nekoj društvenoj mreži.

Zaštitni mehanizmi

  1. X-Frame-Options – Zaglavlje koje može biti DENY (zabranjuje učitavanje stranice u iframe) ili SAMEORIGIN (dozvoljava samo ako je iframe na istom domenu).
  2. CSP frame-ancestors – Ograničava koje domene mogu učitati stranicu u iframe.
app.use((req, res, next) => {

  res.setHeader('X-Frame-Options', 'SAMEORIGIN');

  next();

});

Ili u slučaju CSP:

res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");

X-Frame-Options je stariji mehanizam, podržan u većini browsera, dok frame-ancestors spada u CSP standard. Možete da koristite oba radi maksimalne kompatibilnosti.

Ostale bitne tehnike i saveti

Validacija unosa na serverskoj strani

Nikada ne treba da se oslanjate samo na proveru na klijentskoj strani. Uvek vršite validaciju tipa podataka, veličine, formata i sl. na samom serveru.

Validacija na strani klijenta je korisna za UX, ali lako može da se zaobiđe slanjem direktnog HTTP zahteva (npr. uz cURL ili Postman). Zato je serverska validacija obavezna.

Sprečavanje Directory traversal-a i čuvanje fajlova

Ako aplikacija omogućava upload fajlova, obavezno treba ograničiti tip fajla, veličinu i putanju gde se čuva. U suprotnom, moguće su zloupotrebe gde se ubacuju exe fajlovi ili se preskače u strukturi direktorijuma.

Biblioteke za upload (npr. multer u Express-u) treba konfigurisati tako da snimaju fajlove van foldera koji je javno dostupan, i da proveravaju MIME tip, ekstenziju i veličinu. U suprotnom, neko može da postavi .php, .exe i sl.

Logovanje i praćenje incidenata

Detaljno logovanje zahteva i grešaka omogućava bržu reakciju ako dođe do napada. Dobro je koristiti servise za agregaciju logova (npr. ELK stack) i alate za monitoring.

Pored prikupljanja logova, postavljanje real-time upozorenja (alarm sistema) na osnovu neočekivanih šablona ponašanja (npr. veliki broj neuspelih prijava) znatno pomaže u ranom otkrivanju napada.

Redovni sigurnosni pregledi

Pored automatizovanih alata (npr. npm audit), korisno je povremeno raditi pen testove, koristiti statičku i dinamičku analizu koda, i pratiti CVE objave za korišćene biblioteke.

Alati poput OWASP ZAP ili Burp Suite mogu otkriti mnoge poznate ranjivosti. S vremena na vreme, preporuka je da angažujete neki nezavisni tim za bezbednost radi sveobuhvatne provere aplikacije.

Uloga Node.js i server-side JavaScript sigurnosti

Iako JavaScript često povezujemo sa klijentskom stranom, preko Node.js je postao izuzetno popularan i na serverskoj strani.

  • Iste ranjivosti (XSS, CSRF) su i dalje bitne, ali sada morate da vodite računa i o server-side napadima tipa deserializacije, RCE (Remote Code Execution) kroz child_process i slično.
  • Konfiguracija: Node.js server treba da bude konfigurisan tako da koristi cluster ili process manager (PM2, forever), da loguje greške na bezbedan način i da nikada ne otkriva neophodne osetljive podatke prilikom pada (stack trace prema klijentu je loša praksa).
  • Upotreba ENV varijabli: Kredencijale (npr. API ključeve) treba da čuvate u bezbednim mehanizmima (Vault, Kubernetes Secret, Docker Secrets), a ne direktno u kodu.

Node.js aplikacije takode mogu da budu mete DoS napada pomoću prevelikog broja istovremenih zahtevа ili zlonamernih payload-ova (npr. JSON koji je ogroman i dovodi do memorijskog prepunjavanja). Uvek treba da postavite odgovarajuća ograničenja (body parser limit, rate limit i sl.).

Najčešće greške i kako ih izbeći

Evo kratkog pregleda najčešćih propusta u JavaScript aplikacijama i saveta kako ih izbeći:

  1. Nepostojanje CSP-a
    Rešenje: Postavite barem osnovni CSP (default-src 'self'; script-src 'self') i prilagodite potrebama.
  2. Korišćenje innerHTML bez sanitizacije
    Rešenje: Zamenite sa textContent ili koristite provere i biblioteke za sanitizaciju.
  3. Neadekvatna ili nepostojeća odbrana od XSS
    Rešenje: Uvek enkodirajte izlaz, koristite framework-ove koji imaju ugrađene zaštite, postavite CSP.
  4. Korišćenje eval() ili sličnih funkcija
    Rešenje: Izbegavajte takve funkcije. Ako vam treba dinamičko izvršavanje koda, potražite sigurniju alternativu.
  5. Nezaštićeni kolačići
    Rešenje: Postavite HttpOnly, Secure, SameSite atribute i koristite HTTPS.
  6. Ignorisanje CSRF rizika
    Rešenje: Implementirajte CSRF tokene ili se oslonite na SameSite kolačiće i server-side provere.
  7. Korišćenje zastarelih biblioteka
    Rešenje: Redovno održavajte projekat, pratite npm audit i instalirajte ažuriranja.
  8. Oslanjanje samo na klijentsku validaciju
    Rešenje: Klijentska validacija je korisna radi interfejsa i brzine, ali serverska je obavezna radi sigurnosti.
  9. Čuvanje tokena u localStorage
    Rešenje: Ako možete, koristite HttpOnly kolačiće za JWT ili smanjite rizik XSS-a i upotrebite dodatnu zaštitu.
  10. Prikazivanje previše detalja o greškama
    Rešenje: Prikažite generičke poruke krajnjem korisniku. Detaljne logove čuvajte na serverskoj strani.


Većina ovih grešaka proizilazi iz kombinacije neiskustva, žurbe i oslanjanja na podrazumevane postavke. Redovna edukacija i revizija koda mogu značajno smanjiti mogućnost pojave istih.

Kratka studija slučaja

Scenario: Napad kroz chat aplikaciju

Zamislite da razvijate real-time chat aplikaciju sa Node.js i Socket.IO. Korisnik unosi poruke koje se odmah prikazuju ostalim korisnicima.

  • Nevalidiran unos: Ako ne postavite sanitizaciju na serveru i klijentu, neko može poslati poruku tipa <script>...malware...</script>.
  • Posledica: Svi koji prime i prikažu tu poruku (npr. ubacivanje u DOM putem innerHTML) mogu postati žrtve XSS-a.
  • Rešenje: Sanitizujte poruku na serveru pomoću biblioteka, a klijentu pošaljite samo plain tekst ili siguran HTML.

Chat aplikacije su posebno osetljive jer je sadržaj vrlo dinamičan. Korisnici stalno unose nove poruke i linkove, a sve se prenosi i prikazuje u realnom vremenu. Jedna mala greška u sanitizaciji može ugroziti sve online korisnike.

Scenario: Nepažljivo učitavanje ekstenzija

U nekoj Node.js aplikaciji koristite veliki broj paketa. Jedan od njih postane kompromitovan (npr. supply chain attack).

  • Posledica: Kada ažurirate paket, ubacujete i maliciozni kod koji može krasti API ključeve i slati ih napadaču.
  • Rešenje: Redovno praćenje verovatnoće ovakvih napada, pregled dizajniranih mehanizama iz NPM-a, upotreba proverenih paketa, ručni pregled koda kritičnih biblioteka i sl.

Moguće je postaviti i alate koji odmah upoređuju checksum pre i posle instalacije, te obaveštavaju tim ako se pojavi sumnjiva izmena. Što je paket bitniji (npr. za autentfiikaciju), to je važnije vršiti takve kontrole.

Zaključak

Kao što ste videli, sigurnost JavaScript aplikacija zahteva multidisciplinaran pristup: od čiste klijentske strane i manipulacije DOM-om, do server-side kontrola, pravilne konfiguracije kolačića i snažne zaštite API-ja. Iako su popularne biblioteke i framework-ovi poput React-a, Angular-a ili Vue.js-a u velikoj meri ublažili mogućnosti grešaka, i dalje ostaje veliki prostor za ranjivosti ako se ne poštuju osnovni principi.

Najvažniji saveti za zaštitu JavaScript aplikacija koje treba da imate na umu:

  1. Enkodirajte i sanitizujte svaki spoljašnji unos.
  2. Postavite Content Security Policy (CSP) i druga sigurnosna zaglavlja (X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security).
  3. Koristite CSRF zaštitu za sve osetljive POST/PUT/DELETE zahteve.
  4. Konfigurišite kolačiće tako da budu HttpOnly, Secure i SameSite.
  5. Izbegavajte eval() i slično, i pazite sa innerHTML.
  6. Redovno ažurirajte zavisnosti i proveravajte poznate ranjivosti.
  7. Vodite računa o tokenima i API ključevima, čuvajte ih na odgovarajući način (ENV varijable, Vault i sl.).
  8. Implementirajte jak sistem logovanja i redovnog pen testiranja.

Posmatrajte bezbednost ne kao jednokratnu aktivnost, već kao stalni proces: pratite novine, redovno obučavajte tim, učestvujte u zajednici (OWASP i slične organizacije) i budite spremni da brzo reagujete kada se pojave nove vrste napada.

Ostavi komentar

Vaša adresa neće biti objavljena