-
Notifications
You must be signed in to change notification settings - Fork 1
OWASP Top 10 Analysis
U web aplikacijama je često potrebna mogućnost dinamničke redirekcije korisnika u zavisnosti od podataka koje je uneo u aplikaciju. Da razjasnimo, dinamička redirekcija podrazumeva da je klijent uneo URL kao parametar unutar requesta aplikaciji. Kada aplikacija primi request, korisnik je redirektovan na URL naznačen u requestu. Na primer:
http://www.primer.com/redirect?url=http://www.primer_neki_drugi_sajt.com/reklame
Request od gore bi redirektovao korisnika na http://www.primer_neki_drugi_sajt.com/reklame . Ovde bi bezbednosni problem bio taj da se iskoristi poznata marka organizacije da bi se phish-ovao korisnik i prevario da poseti nebezbedan sajt, u nasem slucaju http://www.primer_neki_drugi_sajt.com/reklame
Ukoliko nemamo neki custom helper koji bi nam pomogao da se izborimo sa ovim, ili whitelist, sledeci pristup bi bio u redu:
begin
if path = URI.parse(params[:url]).path
redirect_to path
end
rescue URI::InvalidURIError
redirect_to '/'
end
Ukoliko baš moramo da match-ujemo input korisnika sa listom odobrenih sajtova ili TLD-ova sa regularnim izrazima, common sense bi bio da iskoristimo neku biblioteku kao što je URI.parse da bi dobili host-a i njegovu vrednost provukli kroz regularan izraz. Ukoliko ti regularni nisu napisani kako treba, postoji šansa da napadač zaobiđe validaciju (ali to nije security issue Rails-a).
Primer:
require ‘uri’
host = URI.parse(“#{params[:url]}”).host
validation_routine(host) if host
def validation_routine(host)
# regexp po kojem validiramo
# takođe bi bilo dobro uporediti sa nekim whitelist-om
end
Takođe, redirekcija bez provere parametra bi mogla da dovede do XSS-a.
Primer:
redirect_to params[:to]
http://primer.com/redirect?to[status]=200&to[protocol]=javascript:alert(0)//
Tako da bi očigledno rešenje za ovu ranjivost bilo da se TLD-ovi definišu statički, ili mapiraju na hash.
Primer:
OKEJ_URL_OVI = {
‘okej_url_1’ => “https://www.primer.com/fine_pare”,
‘okej_url_2’ => “https://www.primer_2.com/okej_url”
}
def redirect
url = OKEJ_URL_OVI[“#{params[:url]}”]
redirect_to url if url
end
U 92 gem-a koja koristimo u api-ju pronadjeno je 10 ranjivosti:
Oznaka | Gem | Kategorija | Izvor | Ozbiljnost |
---|---|---|---|---|
CVE-2015-7581 | rails (4.2.4) | Resource Management | NIST NVD | Severe |
CVE-2015-1820 / OSVDB-119878 | rest-client (1.6.7) | Session Setting | GitHub | Severe |
CVE-2015-7576 | rails (4.2.4) | Other | NIST NVD | Moderate |
CVE-2015-7577 | rails (4.2.4) | Other | NIST NVD | Severe |
CVE-2015-7578 | rails-html-sanitizer (1.0.3) | Cross-Site Scripting | NIST NVD | Moderate |
CVE-2016-2098 | rails (4.2.4) | Attribute Restriction | NIST NVD | Critical |
CVE-2016-0753 | rails (4.2.4) | Input Validation | NIST NVD | Severe |
CVE-2015-3448 / OSVDB-117461 | rest-client (1.6.7) | Information Disclosure | NIST NVD | Moderate |
CVE-2016-0751 | rails (4.2.4) | Resource Management | NIST NVD | Severe |
CVE-2016-0752 | rails (4.2.4) | File Access | NIST NVD | Severe |
Rails ima ugrađenu podršku za CSRF tokene. Da bi se ona omogućila potrebno je u Application controller-u uraditi sledeće:
class ApplicationController < ActionController::Base
protect_from_forgery
Iako Rails već poseduje gore navedenu podršku za CSRF tokene, zbog toga što je naša aplikacija samo API koristimo devise_token_auth u bekendu i ng-token-auth.
U Railsu se ovo obično dešava ukoliko filteri na nivou kontrolera nisu podešeni kako treba. Na primer, ukoliko neko ko nije ulogovan ne sme da pristupi određenim akcijama kontrolera, a nije setovan odgovarajuci filter, doći će do toga da korisnik bez prava pristupa pristupi određenim akcijama kojima ne bi trebao da ima pristup.
Primer jednog filtera:
before_filter :authenticate_user!
Ovaj filter možemo iskoristiti na dva načina:
- Zaštititi celu aplikaciju tako što će se on postaviti u ApplicationController i skipovati ga samo tamo gde je potrebno
- Stavljati ga u svaki kontroler i skipovati akcije koje ne zelimo da zastitimo.
Takođe, ukoliko radimo na aplikaciji koja ima više tipova korisnika (admin i običan korisnik) moramo se osigurati da tip korisnika koji je nižeg nivoa nema pristupa akcijama i metodama korisnika višeg nivoa. To možemo sprečiti proverom tipa korisnika uz pomoć npr CanCanCan gem-a.
U okviru razmatranog sistema, pod osetljive podatke spadaju:
- računi poslovih parnetra/razmatranog preduzeća
- spisak transakcija(porudžbina/potražnje) između posmatranog preduzeća i poslovnih partnera
- spisak faktura, koje primamo ili koje izdajemo
Pošto razmatrani sistem mrežno komunicira sa eksternim entitetima kao što su banka i poslovni partneri, kako neko ne bi prisluškivao(sniffing
) poverljive podatke koje smo naveli, potrebno je enkriptovati komunikaciju. Komunikacija između eksternog entiteta i našeg preduzeća implementirana je preko HTTPS protokola, čime se garantuje tajnost informacija, i nemogućnost da se cipher-text
pretvori u plain-text
u nekom razumnom vremenu. Međutim kako smo spomenuli u analizi pod brojem 5. debugging mode
na glavnom back-end-u aplikacije nije isključen i korisnik ima mogućnosti da vidi spisak svih ruta, gde je uz pojedine akcije u mogućnosti da dođe do nama važnih poverljivih podataka.
Primer otkrivanja takvih podataka je predstavljen u 2 koraka:
-
Korisnik šalje zahtev
http://localhost:3000/neki_nepostojeci_url
kako bi dobio informacije o svim rutama koje aplikacija podržava. -
Sada je samo neophodno uočiti rute koje vode ka resursima od interesa, i u zavisnosti od toga poslati novi zahtev. Npr. ako želimo da imamo spisak svih ulaznih faktura izdatih od strane poslovnih partnera poslaćemo get zahtev sa web browser-om na URL
https://localhost:3001/api/input_invoices
gde ćemo dobiti spisak svih izdatih faktura u JSON formatu.
Podešavanje samog servera je nekada dostupno developer-u npr. ukoliko imamo pristup računaru na kom je pokrenut server tada je neophodno proći kroz dobro poznate check-list-e. Pošto je server pokrenut na Ubuntu 15.0, neophodno je ispoštovati određene procedure. Lynis je alat za proveravanje sigurnostih postavki sistema. Pokrenuta je pretraga operativnog sistema sa ./lynis audit system
, gde su dobijeni odgovarajući rezultati. Detaljnija analiza sistema koji je deploy-ovan na heroku nije omogućena pošto je heroku PaaS, pa je korišćen online alat SSL Server, gde su dobijeni sledeći [rezultati] (https://www.ssllabs.com/ssltest/analyze.html?d=receipt-yourself.herokuapp.com).
Lako se može uočiti da debugging mode
nije isključen što daje uvid napadaču u unutrašnju strukturu sistema. Test nalozi sa standardnim kredencijalima tipa admin/admin
ili admin/1234
ili slično ne postoje u sistemu.
Onemogućen je pristup izlistavanja sadržaja direktorijuma(standardno file-system pretraživanje), zbog definisanih pravila pristupa resursima pomenutih u analizi pod 4.
Aplikacija neće raditi sa stream-ovanjem pa bi bilo poželjno isključiti UserDatagram Protocol
zbog potecijalnog DDOS napada.
Koristiti Principle of Least Privilege
što znači da treba isključiti sve funkcionalnosti po default
-u, a uključiti samo one koje su nam neophodne. Ukoliko nismo sigurni, pustimo sistem da funkcioniše, pa ćemo kroz samo funkcionisanje sistema biti u mogućnosti da zaključimo koje su nam funkcionalnosi vitane za pravilan rad sistema.
Rails po default
u koristi RESTFul URI strukturu za pristup resursima, definisanu u datoteci conf/routes.rb
. Zbog konfiguracije pristupa resursa(definisanjem ruta u prethodno navedenoj datoteci) onemogućen je direktan pristup resursima koji nisu opisani rutama. Dakle onemogućen je direktni pristup datotekama npr: https://localhost:3001/config/routes.rb
ili navigacija putem file system-a https://localhost:3001/../some_file.dat
.
Konkretno svi korisnici ovog sistema pripadaju istoj grupi korisnika(sa istim pravima pristupa i modifikacije). Međutim ukoliko bi se u sistem uvelo više uloga putem RBAC-a, pored zaštite na UI(da korisnik u zavisnosti od tipa uloge tj. prava u sistemu, vidi samo za njega relevantne stvari). Zaštita na UI treba da se pravi tako da oni delovi UI koji ne pripadaju korisniku u zavisnosti od njegovih privilegija, budu direktno izbačeni iz DOM stabla. Taj efekat se dobija korišćenjem <div ng-if="expression">admin rights...</div>
, ukoliko bi koristili umesto ng-if
, ng-show
na taj način bi se lako mogao prikazati i taj UI sadržaj, i samim tim omogućiti korisniku da izvršava nedozvoljene operacije nad sistemom. Kao dodatna sigurnost, neophodno je uraditi double-check tako što će u kontrolere biti ubačen dodatni mehanizam zaštite koji će određivati da li će se kontroler izvršavati u zavisnosti od tipa korisnika na sesiji, ili neće. CanCanCan
je primer biblioteke koja štiti kontrolere od neovlašćenog pristupa.
Prevencija CSS-a je podržana u Rails-u od verzije 3.0. Kada se određeni string prikazuje u view delu aplikacije, on biva automatski sanitiziran. Naravno, postoje slučajevi kada se ovo može zaobići, a obično je greška programera!
<%= raw @product.name %>
<%= @product.name.html_safe %> # ovo je primer kako ne treba raditi!
<%= content_tag @product.name %>
Ovo se dešava kada želimo klijentu da omogućimo "Rich text editing", tj. kada klijentu omogućimu da unosi HTML
tagove unutar input polja. Da bismo podržali ovaj feature, i ostavili mogućnost korisnicima da unose HTML
sadržaj, trebali bismo to omogućiti preko nekih drugim "markup" jezika kao što su Markdown ili Textile.
Ako baš ne možemo da sprečimo korisnika da unosi HTML, Rails ima metodu #sanitize
ali ni ovo nije kompletno rešenje.
Za view deo koji je pisan u Angularu možemo koristiti kombinaciju ng-bind
directive za input polja. Na ovaj
način, sav sadržaj koji se nađe unutar input polja sa ng-bind
atributom će biti provučen kroz Angular-ov
filter sanitize
koji će sanitizirati svaki maliciozan HTML koji je korisnik uneo.
Ugrađeno ponašanje Rails-a je da koristi sesiju baziranu na korisničkom Cookie-ju (sesija se čuva u cookie-ju). Ako ne promenimo podešavanja servera, ovo znači da sesija neće nikad isteći na serveru. Ovo implicira da će aplikacije biti ranjiva na "replay" napade. Takođe, osetljive informacije ne bi trebalo stavljati u sesiju iz ovih razloga.
Lak način da se izbegne ovo je da se sesija čuva u bazi. Ovo je lako izvesti u Rails-u uz pomoć:
Project::Application.config.session_store :active_record_store
podešavanja unutar aplikacije. Ovim kažemo da se sesija čuva u bazi umesto u cookie-ju.
Rails ne podržava autentifikaciju "out of the box", ali se ona može lako uvesti u aplikaciju uz pomoć gem-ova kao što su Devise i AuthLogic. Razlike između ova dva su minimalne što se tiče bezbednosti. U našem projektu mi smo koristili Devise gem. Da bismo autentifikovali korisnika koristili smo autentifikaciju pre zvanja svake akcije koju imamo u aplikaciji. Ovo smo postigli stavljajući:
class RandomController < ApplicationController
before_filter :authenticate_user
Filter pre svake akcije proverava da li je pravi korisnik ulogovan. Ako korisnik nije ulogovan, redirektujemo ga na 404 stranicu.
Ruby ima funkciju eval
koja može biti jako opasna i može dovesti do lakog command injectiona unutar aplikacije. eval
funkcija evaluira string koji joj je prosleđen u Ruby kod. Ovo može biti veoma opasno.
Takođe postoji i funkcija preko kojeg možemo pozivati sistemske komande zvana System
. Ovoj funkciji možemo proslediti bilo kakve komande koje operativni sistem ispod aplikacije može da izvrši. Takođe postoji i Kernel
klasa sa svojom #exec
metodom koja radi sličnu stvar sa kernelom ispod aplikacije.
eval("ruby code here")
System("os command here")
Kernel.exec("os command here")
Iako su ovi pozivi veoma moćni, korišćenje se ne preporučuje i uglavnom je veoma loša ideja. Preporuka je da se ne koriste ako je moguće. Ako baš moraju da se koriste preporučuje se da se napravi whitelist komandi koje su dozvoljene za izvršavanje i da se radi provera komande pre nego što se izvrši.
Rails se obično koristi sa ORM-om (Object Relationship Mapping) koji se zove ActiveRecord
. ActiveRecord
je odlično zaštićen od SQL Injection napada, mada se često dešava da početnici pišu kod uz pomoć koga je veoma
lako izvršiti ovaj napad.
Klasični primer lošeg koda je kad upitima direktno prosledimo neobrađene parametre koji su stigli sa klijentske strane.
U primeru ispod vidimo kako se upitu direktno prosleđuje parametar id
. Ovo dozvoljava
korisniku da prosledi bilo koji string unutar id
parametra i tako korumpira našu bazu podataka.
params[:id] = "admin = 't'"
User.find_by params[:id]
Ovo će dati sledeći SQL:
Query
SELECT "users".* FROM "users" WHERE (admin = 't') LIMIT 1
Result
#<User id: 30, name: "Admin", password: "supersecretpass", age: 25, admin: true, created_at: "2016-01-19 16:12:11", updated_at: "2016-01-19 16:12:11">
tj. na lak način smo dobili korisnika koji je admin aplikacije.
Ovo možemo sprečiti koristeći AREL rešenje:
User.find_by("id = ?", params[:id])
ili
User.find_by_id params[:id]
U Rails-u se lako može pogrešiti i napisati SQL Injection friendly kod zbog velike količine
sintaksnog šećera koji je uveden u ActiveRecord
. Na ovaj način programeri zaboravljaju šta se dešava
ispod sintakse koje kucaju tj. kakvi SQL upiti se formiraju na osnovu njihovo koda. Ovo se može sprečiti
edukacijom i pregledom najčešćih grešaka u pisanju koda koji dovlači nešto iz baze podataka. Dobro mesto
za početak je Rails SQL Injection sajt koji predstavlja najčešće greške koje se
javljaju u kucanju takvog koda.