Sigurnosna analiza NodeJS platforme za izradu skalabilnih aplikacija
Članovi: Danijel Vincijanović
--Danijel Vincijanović 15:32, 6. studenog 2015. (CET)
Sadržaj |
Uvod
Javascript se godinama koristi na klijentskoj strani kako bi pružio bogatije i kvalitetnije korisničko iskustvo. Zadnjih nekoliko godina pojavili su se interesi za korištenjem javascripta ne samo na klijentskoj strani već i na server strani. Stoga danas postoje SSJS (Server-side Javascript) mogućnosti korištene kod servera baza podataka poput CouchCB, servera datoteka kao Opera Unite i web servera (Node.js)
Node JS je platforma za razvoj skalabilnih web aplikacija. Izrađen je na Google V8 Javascript sustavu uz implementaciju web poslužiteljskog dijela. Kao takav sadrži web poslužiteljsku podršku pa nije potrebno drugo web poslužiteljsko programsko rješenje. Ideja na kojoj se zasniva Node je non-blocking i event-driven I/O model koji ga čine jednostavnim i brzim. Kreator Node-a - Ryan Dahl, težio je kreiranju real-time web aplikacija sa push tehnologijom, a nit vodilja su mi bile aplikacije poput Gmail-a. Real-time web aplikacije sa mogućnosti push tehnologije preko web socketa čine srž Node js-a. Ono što je važno napomenuti je kako Node.js nije nova platforma koja će zavladati web svijetom već je ona tu da upotpuni određene potrebe. Slabost Node.js-a je u obavljanju procesorski intezivnih zadataka gdje će sve njegove prednosti biti zagušene. Rad na samo jednoj dretvi je razlog zbog kojega procesorski intezivni zadaci nisu poželjni jer bi stvarali probleme za sve klijente koji šalju zahtjev koji će biti blokirani sve dok zadatak nije završen. Stoga je Node.js najbolje iskoristiti kada razvijamo aplikaciju koja će biti zadužena za upravljanje velikim brojem povezivanja gdje se ne obavljaju procesorski teške operacije.
Node donosi određene sigurnosne prednosti u odnosu na ostale paradigme tradicionalnog razvoja server strane. Npr. sljedeći kod prikazuje kostur jednostavnog web servera kojemu je zadatak da radi upravo ono za što je konfiguriran i ništa više od toga za razliku od drugih web servera poput Tomcat, Apache ili IIS. Naime, ovakav način razvoja web servera predstavlja prednost jer povećanjem broja mogućnosti web servera, povećava se i mogućnost napada. [1]
Each new developer hire is a potential set of vulnerabilities, patch them with education. — Lift Security Team
GitHub repozitorij - NodeJS Security projekt
Hello World
Za početak ćemo se držati programerske tradicije i izraditi aplikaciju koja će ispisati 'Hello World'. Server sluša port 8080.
http = require('http'); http.createServer(function(request, response) { response.writeHead(200, {'Content-Type': 'text/html'}); response.write('<p>Hello World</p>'); response.end(); }).listen(8080); console.log('Server running at http://127.0.0.1:8080/');
PHP verzija 'Hello World' aplikacije:
<?php echo "<p>Hello World</p>"; ?>
Zatim ćemo usporediti brzinu kojom će Node.js i Apache server + PHP obraditi 100 000 zahtjeva na 'Hello World' aplikaciju. Testiranje radimo pomoću Apache Bench alata. Apache Bench ćemo instalirati i pokrenuti sljedećim naredbama u terminalu:
$sudo aptitude install apache2-utils $ab -r -n 100000 -c 100 <url>
Umjesto <url> stavljamo url kojega testiramo. Prethodna naredba će izvršiti 100 000 zahtjeva sa ukupno 1000 zahtjeva koji će se desiti u isto vrijeme. Rezultati dobiveni testiranjem Node.js-a:
Concurrency Level: 1000 Time taken for tests: 10.352 seconds Complete requests: 100000 Failed requests: 0 Total transferred: 11800000 bytes HTML transferred: 1800000 bytes Requests per second: 9659.89 [#/sec] (mean) Time per request: 103.521 [ms] (mean) Time per request: 0.104 [ms] (mean, across all concurrent requests) Transfer rate: 1113.15 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 73 450.4 0 7016 Processing: 3 22 22.5 21 433 Waiting: 3 22 22.5 21 433 Total: 7 95 459.1 21 7441 Percentage of the requests served within a certain time (ms) 50% 21 66% 24 75% 28 80% 29 90% 33 95% 68 98% 1030 99% 1220 100% 7441 (longest request)
Sada ćemo ponoviti isto sa Apache serverom i PHP-om nakon čega dobivamo sljedeće rezultate:
Concurrency Level: 1000 Time taken for tests: 25.685 seconds Complete requests: 100000 Failed requests: 0 Total transferred: 20600000 bytes HTML transferred: 1800000 bytes Requests per second: 3893.35 [#/sec] (mean) Time per request: 256.848 [ms] (mean) Time per request: 0.257 [ms] (mean, across all concurrent requests) Transfer rate: 783.23 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 13 158.2 0 3004 Processing: 2 75 947.7 6 25659 Waiting: 1 75 947.7 6 25659 Total: 5 87 1004.3 6 25677 Percentage of the requests served within a certain time (ms) 50% 6 66% 6 75% 7 80% 7 90% 7 95% 8 98% 11 99% 1006 100% 25677 (longest request)
Zaključujemo kako Node može obraditi više zahtjeva u jedinici vremena što predstavlja njegovu snagu. Kada bi u aplikaciju dodali određeni procesorski zahtjevniji zadatak Node bi izgubio svoju prednost i trebalo bi mu znatno više vremena da obradi sve zahtjeve jer bi obradom jednog drugi bili blokirani. Node zahvaljujući svojoj arhitekturi koristi malo sistemskog resursa za obradu zahtjeva. Stoga, utvrđivanjem zahtjeva aplikacije možemo saznati pruža li nam Node željene prednosti te da li ga ima smisla koristiti u razvoju iste.
Kostur web servera
Slijedi primjer prilično jednostavnog kostura web servera koji dohvaća i prikazuje datoteke ovisno o putanji. Ukoliko takve ne postoje vraća 404 - Not Found. Server sluša port 8080.
var http = require("http"); var fs = require('fs'); var url = require('url'); http.createServer(function (request, response) { var path = url.parse(request.url).pathname.substr(1); fs.readFile(path, function(error, data){ if (error){ rSesponse.writeHead(404, {'Content-Type':'text/html'}); response.end(); } else { response.writeHead(200, {'Content-Type':'text/html'}); response.write(data.toString()); response.end(); } }); }).listen(8080); console.log('Server running at http://127.0.0.1:8080/');
Cross Site Scripting
Cross Site Scripting (XSS) je napad koji se dogodi kada stranica pokazuje podatke koji nisu prethodno provjereni. Na taj način napadač može u stranicu unijeti zlonamjerni Javascript kod koji će se izvršiti prilikom korisnikovog posjeta stranici. XSS ranjivost napadaču omogućuje pristup povjerljivim informacijama, kolačićima, slanje lažnih zahtjeva itd.. Postoje dvije glavne vrste XSS napada: [2]
- Reflected XSS ili non-persistent XSS
- Stored XSS ili persistent XSS
Reflected XSS
Kod Reflected XSS-a zlonamjerna skripta se šalje korisniku tako što korisnik sam pošalje zahtjev prema web aplikaciji. Napadač na taj način prevari korisnika da sam izvrši napad putem email-a, društvenih mreža ili bilo koje druge web stranice. Rezultat toga je da korisnik pristupi zlonamjernoj poveznici i potvrdi formu ili napravi zahtjev prema malicioznoj stranici. [2]
Sljedeći dijagram prikazuje na koji način se izvršava reflected XSS:
Na dijagramu možemo vidjeti kako je implementiran pretraživač na endpointu /search?=unos
Napadač na ovaj način može saznati da se unos ne provjerava čime se otvaraju mogućnosti za napad. Napad se može izvršiti na način da napadač na svojoj stranici postavi poveznicu sa zlonamjernim kodom prema ranjivoj stranici.
Napadač je vlasnik /index.html stranice koja izgleda ovako:
<html> <a href="https://site.com/search?q=<script>alert('hacked')</script>"> Click Here </a> </html>
Ukoliko korisnik dođe na tu stranicu i klikne na poveznicu preglednik će napraviti zahtjev prema toj stranici zajedno sa malicioznim Javascript kodom. U ovome slučaju 'maliciozni' kod je samo alert
funkcija, ali napadač može napraviti bilo koju drugu radnju pomoću Javascripta. Također je moguće napad izvršiti bez poveznice već automatskim preusmjeravanjem korisnika na ranjivu stranicu.
Stored XSS
Kod Stored XSS napada maliciozna skripta se unosi u bazu ranjive web aplikacije. Uneseni maliciozni kod će se izvršiti kada korisnik dođe na određenu rutu stranice koja dohvaća maliciozni kod iz baze i ubacuje ga u HTML.
Na dijagramu je vidljivo da je omogućen POST zahtjev na endpoint /comment
. Stranica koja je XSS ranjiva ne provjerava korisnikov unos pa je tako omogućeno napadaču da unese maliciozni kod.
Nakon toga korisnik dođe na stranicu i šalje zahtjev kojim se dohvaća maliciozni kod iz baze. [2]
XSS - zaštita
- Korištenjem nekog od template engine-a, u našem slučaju JADE, možemo se zaštititi od XSS napada. Svaki od template engine-ova imaju različitu zaštitu od XSS napada. Tako npr. JADE koristi sljedeći kod kako bi pretvorio potencijalno maliciozne znakove u njihove HTML ekvivalente:
exports.escape = function escape(html){ var result = String(html) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"'); if (result === '' + html) return html; else return result; };
- Provjera korisnikovog unosa. Primijeniti whitelisting gdje god je to moguće.
- HTTPOnly kolačić zastavica - korištenjem ove astavice u zaglavlju onemogućavamo pristup kolačiću na klijentskoj strani
- Implementiranje CSP-a - CSP je mehanizam preglednika koji dopušta whitelisting resursa korištenih na stranici. Npr. moguće je definirati za Javascript, CSS, slike i ostalo iz kojih izvora su dopušteni.
- Enkodirati output ovisno o kontekstu:
Kontekst | Primjer koda | Enkodiranje |
---|---|---|
HTML Entity | <span>NEPOVJERLJIVI SADRŽAJ</span> | Promijeniti & u & Promijeniti < u < Promijeniti > u > Promijeniti " u " Promijeniti ' u ' Promijeniti / u / |
HTML enkodiranje atributa | <input type='text' name='name' value='NEPOVJERLJIVI SADRŽAJ' | escape-ati sve znakove (osim alfanumeričkih) sa HTML entitetom formata &#xHV; gdje HV predstavlja hex. vrijednost. |
URl enkodiranje | <a href='/site/search?value= 'NEPOVJERLJIVI SADRŽAJ'> klikni me </a> | escape-ati sve znakove (osim alfanumeričkih) sa ASCII vrijednošću manjom od 256 sa HTML entitetom formata &#xHV gdje HV predstavlja hex. vrijednost |
Javascript enkodiranje | <script>var currentValue='NEPOVJERLJIVI SADRŽAJ';</script> <script>someFunction('NEPOVJERLJIVI SADRŽAJ');</script> | Osigurati da su varijable pod navodnicima. escape-ati sve znakove (osim alfanumeričkih) sa ASCII vrijednošću manjom od 256 sa \uXXXX unicode formatom gdje je X integer ili xHV format gdje je HV hex. vrijednost |
CSS enkodiranje | <div style="width: NEPOVJERLJIVI SADRŽAJ;">Selection</div> | escape-ati sve znakove (osim alfanumeričkih) sa ASCII vrijednošću manjom od 256 sa \HV formatom gdje je HV hex. vrijednost |
Detaljnije o zaštiti od XSS napada - https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
Server-Side JavaScript Injection
Node je naslijedio kontroverznu javascript funkciju eval. Sintaksa funkcije eval je: eval(string) Funkcija eval izvršava proslijeđeni argument
//nakon što se izvrši naredbeni redak varijabla 'x' će sadržavati broj 4. var x = eval("2 + 2");
Eval() funkcija predstavlja ranjivost na strani klijenta tako što je moguće napraviti Cross-site Scripting (XSS) napad, a sada je pomoću iste moguće napraviti server side injection. Prema OWASP-u, XSS se trenutno nalazi na drugom mjestu opasnih prijetnji web aplikacija.
Kako bi provjerili je li web aplikacija ranjiva na server side injection kao request ćemo poslati:
response.end('Ranjiv sam')
Nakon toga će server izvršiti naredbu i ispisati 'Ranjiv sam' kao tijelo HTTP odgovora. Ukoliko uspijemo na ovaj način dobiti odgovor od servera znači da je on ranjiv. [5]
SSJI DoS
Kako se Node izvodi na jednoj dretvi jednostavna greška može prouzročiti Denial of Service (DOS). Zbog toga je moguće srušiti server na način da kao request proslijedimo npr. while(1)
gdje će eval funkcija izvršiti takvu naredbu i server će koristiti 100% svoga procesorskog vremena na beskonačnu petlju i time će biti u nemogućnosti procesuirati bilo kakav zahtjev sve dok administrator ručno ne resetira proces.
U ovome slučaju napadač ne mora izvršiti milijune zahtjeva kako bi onesposobio server već je potreban samo jedan HTTP zahtjev i server je srušen. Alternativa prethodne verzije bi bilo ubijanje ili izlazak iz procesa: process.exit()
ili process.kill(process.pid)
[4]
SSJI Pristup lokalnim datotekama
Sljedeća opasnost eval() funkcije je mogućnost pristupa lokalnim datotekama. Pristup datotekama sustava je moguć uključivanjem 'fs' modula. Moduli se mogu uključivati bilo kada što otvara mogućnosti napadaču da ju uključi i iskoristi za pristup datotekama. Na ovaj način je moguće dobiti popis svih datoteka u sustavu te ih čitati ili pisati u njih.
Kako bi dobili popis svih datoteka unutar trenutnog direktorija poslati ćemo sljedeće:
response.end(require('fs').readdirSync('.').toString())
Nakon toga je jednostavno otvoriti ili mijenjati postojeće datoteke pomoću naredbi 'fs' modula. Valja napomenuti kako funkcije poput setTimeout() i setInterval() također mogu primiti kod u stringu kao argument te time prouzročiti iste probleme kao i eval() funkcija. [4]
Video primjer Server Side Injection-a: https://www.youtube.com/watch?v=krOx9QWwcYw
SSJI - zaštita
- Validacija korisnikovog unosa na klijentskoj i server strani prije procesiranja.
- ne koristiti eval() i slične funkcije za parsanje korisnikovog unosa
- umjesto eval() funkcije sigurnija alternativa je JSON.parse()
- uključiti 'use strict' (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)
ReDoS
ReDos – Regular expression Denial of Service je vrsta Denial of Service napada koji leži na činjenici da se izvršavanje regex može znatno usporiti gdje je brzina izvršavanja eksponencijalno povezana sa korisnikovim unosom. Na ovaj način se napadaču otvara mogućnost da onemogući server na neko vrijeme. Algoritam regexa provjerava sve moguće kombinacije dok ne pronađe poklapanje pa tu završava ili drugi slučaj kada prolazi kroz sve kombinacije i ne pronađe poklapanje. Što je korisnikov unos veći to je i veći broj mogućnosti koje se moraju provjeriti. Npr. Za regex ^(a+)+$
i unos 'aaaa' postoji ukupno 16 mogućnosti dok za unos 'aaaaaaaaaaaaaaaa' i isti regex postoji ukupno 65536 mogućnosti te se taj broj duplo povećava sa svakim dodatnim slovom 'a'. [10]
Za regex:
/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
provjeriti ćemo vrijeme koje je potrebno da se ispitaju sljedeći unosi:
'dvincija@foi.hr' => validan e-mail pa je potrebno kratko vrijeme za testiranje
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbbbbbbbbbbbbbbbbbbbbbbb.5555555555555555555555555555555555555555555555555555{' => nije validan e-mail pa je potrebno puno više vremena dok se ispitaju sve mogućnosti
Dobiveni rezultat testiranja:
true [ 0, 4435278 ] false [ 3, 351614980 ]
ReDoS - zaštita
- koristiti modul 'safe-regex' koji provjerava potencijalno katastrofalan regex https://www.npmjs.com/package/safe-regex
- koristiti OWASP listu regexa za validaciju često korištenih unosa https://www.owasp.org/index.php/OWASP_Validation_Regex_Repository
Korištenje safe-regex paketa je vrlo jednostavno. Prvo je potrebno instalirati paket naredbom: [19]
$ npm install safe-regex
Zatim u kodu pozivom jedne funkcije doznajemo da li je regex potencijalno katastrofalan:
var safe = require('safe-regex'); var regex = /^[a-z0-9_-]{3,16}$/; console.log(safe(regex));
Node Package Manager
NPM je package manager za Javascript te kao takav čini neizostavni dio Node aplikacije. Komponente poput biblioteka, frameworka i ostalih softverskih modula u većini slučajeva za pokretanje zahtjevaju puna prava pa valja biti oprezan. Važno je znati kako neke komponente koje instaliramo putem npm-a mogu biti ranjive odnosno sadržavati nesiguran kod i kao takve predstavljati napadaču priliku za napad. Oko 14% npm paketa sadrži određene ranjivosti, a ova brojka je proizašla iz testiranja velikog broja najpopularnijih paketa. Napadač na taj način može kreirati i pokrenuti skripte u raznim stadijima instalacije i korištenja paketa, također može čitati, brisati, ažurirati i brisati datoteke sa sustava kao i prikupljati osjetljive podatke.
Kako bi spriječili da se ovo dogodi aplikacije ne treba pokretati sa root privilegijama. Potrebno je koristiti module koji sadrže statičku analizu koda, provedene unit testove i recenzije. Preporučljivo je pregledati kod da li pristupa nekim datotekama ili bazi podataka te prije korištenja istražiti koliko je popularan paket i koja je trenutna verzija. Osim toga, poželjno je pratiti github repozitorije na kojima možemo saznati ukoliko se otkriju određene ranjivosti.
Korisni alati za analizu node web aplikacije i npm paketa:
http://retirejs.github.io/retire.js/
Insecure Direct Object References
Nesiguran direktan pristup prema objektima poput datoteka, direktorija ili ključevima baze podataka. Bez kontrole pristupa napadač može manipulirati navedenim referencama i tako ostvariti neautorizirani pristup podacima. Ukoliko web aplikacija koristi ime ili određeni ključ objekta prilikom generiranja stranice te ne provjerava autorizaciju korisnika prema ciljanom objektu tada napadač može doći do nesigurnih referenci. Napadač tada može iskoristiti ranjivost i mijenjajući parametre doći do podataka za koje nema ovlašten pristup.
Kao primjer možemo navesti web aplikaciju koja prilikom generiranja web stranice dohvaća 'id' korisnika koji je proslijeđen kao url parametar te zatim dohvaća podatke toga korisnika. Na ovaj način napadač može promijeniti url i doći do podataka drugih korisnika. [8]
app.get('/:id', function (request, response) { var id = request.params.id; // ...dohvati korisnika response.render('index.jade', {user: user}); }
Ovu ranjivost možemo otklonuti kontrolom pristupa korisnika određenom objektu. Sigurniji način od ovoga je dohvaćanje potrebnih ključeva iz sesije.
Brute Force
Brute force napad predstavlja sustavno pronalaženje svih mogućih kandidata za rješenje i uzastopnim pokušavanjem svakog od njih. Ovakav napad je jednostavan za implementaciju i često se koristi za probijanje zaporki ili raznih enkripcija. Ograničenje brute force napada su vrijeme i resursi koji su potrebni za rješavanje problema jer oni rastu povećanjem broja mogućih kandidata za rješenje.
Kod web aplikacija login endpointovi su prilika za napadača za brute force napad. Kako bi spriječili brute force moramo na neki način omogućiti veliki broj requestova na način da ih ograničimo na određeni broj ili da eksponencijalno povećavamo vrijeme potrebno za svaki dodatni zahtjev. U ovom slučaju ćemo koristiti express-brute paket (https://www.npmjs.com/package/express-brute). Ukoliko se dogodi veći broj zahtjeva prema ruti '/login' server će vratiti 429 grešku, a vrijeme između dva zahtjeva će se povećavati u fibonaccijevom nizu čime se onemogućuje brute force napad. [8]
Instalacija express-brute paketa:
$ npm install express-brute
Jednostavan primjer implementacije express-brute paketa:
var ExpressBrute = require('express-brute'), var express = require('express'); var store = new ExpressBrute.MemoryStore(); // MemoryStore za pohranu podataka lokalno, koristiti samo za razvoj! var bruteforce = new ExpressBrute(store); var app = express(); //ukoliko se dogodi veći broj zahtjeva prema /login ruti server će vratiti 429 grešku app.post('/login', bruteforce.prevent, function (req, res, next) { res.send('Success!'); } );
Cross-site Request Forgery
CSRF predstavlja vrstu napada koji se izvodi posredstvom klijentskog programa. Direktno sa zloćudne stranice moguće je pokrenuti zahtjev prema drugoj ranjivoj stranici u ime korisnika koji se koristi web preglednikom. Takva zloćudna stranica sadrži kod za pokretanje određenog zahtjeva koji će se automatski učitati i pokrenuti prilikom učitavanja stranice. Stranica može postati ranjivom na CSRF napad ukoliko se ne izvode provjere sigurnosti i identiteta korisnika gdje će se zahtjev poslan sa zloćudne stranice obraditi kao da ga je poslao klijent.
Da bi napad imao učinka koriste se GET i POST zahtjevi. GET zahtjev je predviđen za dohvat podataka te kao takav se ne provjerava dovoljno što otvara mogućnost podmetanja GET zahtjeva koji će izazvati štetne izmjene podataka na stranici. CSRF napadi se najčešće izvode putem internetskih foruma koje koristi veliki broj korisnika. [13]
CSRF napad se može izvoditi pomoću: [14]
- zlonamjerno oblikovanog HTML objekta
- skriptnog koda umetnutog u HTML
- zlouporabe automatskog generiranja zahtjeva u pregledniku
CSRF - zaštita
- Onemogućiti CORS - zabraniti cross-origin zahtjeve. Ukoliko je potrebno omogućiti CORS onda to učiniti samo za OPTIONS, HEAD i GET zahtjeve
- Koristiti GET zahtjeve samo za dohvaćanje podataka, a ne za rad i promjenu nad bazom podataka.
- Koristiti PUT, PATCH i DELETE umjesto POST zahtjeva gdje je to moguće jer se time smanjuje broj vektora napada
- Ne koristiti method-override prilikom PUT, PATCH i DELETE zahtjeva
- Izbjegavati starije preglednike jer ne podržavaju CORS i sigurnosne politike
CSRF - zaštita pomoću tokena
Korištenjem jedinstvenog, tajnog i nasumičnog tokena koji se nalazi skriven u HTML-u web aplikacije moguće je otklonuti prijetnje od CSRF napada. Takav token se nakon poslanog zahtjeva provjerava na serverskoj strani.
Način rada CSRF tokena:
- Server šalje token klijentu
- Klijent šalje zahtjev server zajedno sa tokenom
- Server prihvaća zahtjev ukoliko je token validan
CSRF tokene ne smije se moći dohvatiti AJAX-om, odnosno nije preporučljivo kreirati rutu /csrf samo kako bi dohvatili token. Ukoliko stranica ne podržava CORS onda ne postoji mogućnost da napadač dođe do CSRF tokena.
Instalacija csurf paketa:
$ npm install csurf
Zatim u aplikaciji dodamo sljedeći kod pomoću kojega ćemo generirati token za formu na klijentskoj strani: [18]
var express = require('express'); var cookieParser = require('cookie-parser') var bodyParser = require('body-parser') var csrf = require('csurf') var app = express(); // postavljanje middleware-a var csrfProtection = csrf({ cookie: true }); var parseForm = bodyParser.urlencoded({ extended: false }); // kako bi csurf funkcionirao potrebno je prvo inicijalizirati session middleware ili cookie-parser // ova linija je potrebna jer je "cookie" true u csrfProtection-u. app.use(cookieParser()); // postavljanje /form rute // GET zahtjev će prikazati traženu stranicu (pogled) i proslijediti joj csrf token koji ćemo kasnije koristiti u formi app.get('/form', csrfProtection, function(req, res) { res.render('form', { csrfToken: req.csrfToken() }); }); // Potvrdom forme izvršava se POST zahtjev prema /process ruti zajedno sa tokenom i ostalim podacima forme app.post('/process', parseForm, csrfProtection, function(req, res) { res.send('data is being processed'); });
Ovime smo dovršili serversku stranu. Sada je potrebno u pogledu definirati formu koja će imati skriveno polje sa dobivenim tokenom: (napomena: način prosljeđivanje vrijednosti tokena u skriveno polje ovisi o korištenom template engine-u, u našem slučaju to je JADE)
form(action="/process", method="POST") input(type="hidden", name="_csrf", value="#{csrfToken}") input(type="text", name="text"> | Insert data button(type="submit) Submit
Konfiguriranje HTTP zaglavlja
Konfiguriranje HTTP zaglavlje na web serverima Apache ili nginx se može podesiti u samoj konfiguraciji web servera. Konfiguriranje HTTP zaglavlja u Node-u se izvršava u kodu aplikacije. HTTP zaglavlja koja je potrebno podesiti: [11]
- Strict-Transport-Security – prisiljava sigurnu vezu preko SSL/TLS na server
- X-Frame-Options – clickjacking zaštita
- X-XSS-Protection – omogućuje Cross-site scripting filter ugrađen u novije verzije preglednika
- X-Content-Type-Options – onemogućuje MIME-sniffing
- Content-Security-Policy – onemogućuje napade poput cross-site scriptinga, cross-site injectiona, neželjenog praćenja itd.
- X-Powered-By – ovo zaglavlje je potrebno ukloniti jer napadač može iskoristiti ranjivosti Node-a ukoliko zna da se stranica vrti na Express-u ili nekom drugom Node frameworku.
Prethodno navedena konfiguriranja se jednostavno mogu podesiti korištenjem Helmet paketa. https://www.npmjs.com/package/helmet
Instalacija helmet npm paketa:
$ npm install helmet --save
Zatim u aplikaciji dodamo sljedeći kod kojim ćemo konfigurirati HTTP zaglavlje: [17]
var express = require('express'); var helmet = require('helmet'); var app = express(); app.use(helmet());
U zadanim postavkama helmet paket ne uključuje konfiguriranje Content-Security-Policy, HTTP Public Key Pinning i No-Cache zaglavlja. Budući da Content-Security-Policy zaglavlje može uvelike spriječiti XSS i ostale unose malicioznog koda u stranicu konfigurirati ćemo ga u nastavku. Konfiguriranjem CSP zaglavlja omogućen nam je whitelisting izvora stranice. Npr. možemo znati da je skripta sa stranice https://apis.google.com/js/plusone.js povjerljiva te da ne sadrži maliciozni kod, ali to preglednik ne može znati već će on učitati i izvršiti sve skripte koje mu damo. [20] Zbog toga postoji CSP zaglavlje kojim ćemo definirati kojim izvorima vjerujemo te ćemo samo od njih dopuštati učitavanje datoteka: [17]
app.use(helmet.csp({ directives: { defaultSrc: ["'self'", 'default.com'], scriptSrc: ["'self'", "'unsafe-inline'"], styleSrc: ['style.com'], imgSrc: ['img.com', 'data:'], sandbox: ['allow-forms', 'allow-scripts'], reportUri: '/report-violation', objectSrc: [] } }));
Literatura
- Node.js službena stranica - https://nodejs.org/en/
- Secure node apps against owast top 10 - http://scottksmith.com/blog/2015/06/22/secure-node-apps-against-owasp-top-10-cross-site-scripting/
- XSS Prevention Cheat Sheet - https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
- Server side javascript injection - https://media.blackhat.com/bh-us-11/Sullivan/BH_US_11_Sullivan_Server_Side_WP.pdf
- Node.js Security - the good, bad and ugly - http://bishankochher.blogspot.hr/2011/12/nodejs-security-good-bad-and-ugly.html
- Javascript injection detection - https://blog.gdssecurity.com/labs/2015/4/15/nodejs-server-side-javascript-injection-detection-exploitati.html
- OWASP Node.js Goat Project - https://www.owasp.org/index.php/Projects/OWASP_Node_js_Goat_Project
- OWASP Top 10 Node Goat Tutorial - http://nodegoat.herokuapp.com/tutorial
- Udemy Node JS Security -https://www.udemy.com/nodejs-security-pentesting-and-exploitation
- Regular expression dos and Node.js - https://blog.liftsecurity.io/2014/11/03/regular-expression-dos-and-node.js
- Node.js Security Checklist - https://blog.risingstack.com/node-js-security-checklist
- Simple steps to secure your express Node application - http://scottksmith.com/blog/2014/09/04/simple-steps-to-secure-your-express-node-application/
- Understanding CSRF - https://github.com/pillarjs/understanding-csrf
- CSRF - http://www.cert.hr/sites/default/files/NCERT-PUBDOC-2010-04-297.pdf
- Lift Security Blog - https://blog.liftsecurity.io/
- Jade - Template Engine - http://jade-lang.com/
- NPM Helmet paket - https://www.npmjs.com/package/helmet
- NPM csurf paket - https://www.npmjs.com/package/csurf
- NPM safe-regex paket - https://www.npmjs.com/package/safe-regex
- Content-Security-Policy - http://www.html5rocks.com/en/tutorials/security/content-security-policy/
tl;dr
- Izbjegavati eval(), biti oprezan sa setTimeout() i setInterval() funkcijama, koristiti JSON.parse za parsanje korisnikovog unosa
- Izvršiti validaciju korisnikovog unosa na klijentskoj i serverskoj strani
- Izbjegavati procesorski zahtjevne zadatke
- Preporučljivo koristiti neki od template engine-a (Jade Mustache.js EJS...)
- Kontrolirati pristup korisnika određenim objektima
- id sesije ne smije biti vidljiv u URL
- Postaviti timeout za sesije
- Lozinke, id sesije i ostali kredencijali ne smiju biti slani putem nesigurne veze
- Hashirati lozinke i koristiti enkripciju
- Provjeriti pouzdanost NPM paketa prije instalacije/korištenja
- Konfigurirati HTTP zaglavlje - helmet
- CSRF zaštita - csurf
- Brute Force zaštita - express-brute
- Provjeriti Regex - safe-regex
- Koristitit OWASP listu Regexa - OWASP Validation Regex Repository
- Proučiti OWASP Node Goat Tutorial