PHP security
Uvod
PHP je močan jezik i prevoditelj (eng. interpreter), bio uključen u web poslužitelju (eng. server) kao modul ili izvršen (eng. executed) kao zasebni binarni CGI (eng. CGI binary), može pristupiti datotekama i otvoriti mrežne veze na poslužitelju. Ova svojstva čine sve što je pokrenuto na web poslužitelju nesigurnim. PHP je posebno osmišljen kako bi bio sigurniji jezik za pisanje CGI programa za razliku od Perl-a ili C-a, a uz točan izbor compile-time i runtime konfiguracija, i uz pravi način kodiranja može vam dati upravo kombinaciju slobode i sigurnosti koju trebate.
Budući da postoji mnogo različitih načina korištenja PHP-a, postoje mnoge postavke konfiguracija koje kontroliraju njegovo ponašanje. Veliki izbor opcija jamči da možete koristiti PHP za mnoge svrhe, ali to također znači da postoje kombinacije ovih postavki i konfiguracija poslužitelja koji dovode do nesigurnog postavljanja (eng. setup).
Fleksibilnost konfiguracije PHP jednako je konkurentna fleksibilnošću koda. PHP se može koristiti za izgradnju kompletnih poslužiteljskih aplikacija, sa svim snagama korisnika ljuske (eng. shell), ili se može koristiti za jednostavne SSI (eng. server-side inculdes), uz malo rizika u čvrsto kontroliranom okruženju. Kako graditi tu okolinu, i koliko je sigurna, uglavnom ovisi o PHP developeru.
Opća razmatranja
Potpuno siguran sustav je virtualna nemogućnost, tako da je balansiranje rizika i upotrebljivost pristup koji se često koristi u sigurnosnoj profesiji. Ako svaka varijabla koju je korisnik poslao zahtijeva dva oblika biometričke valjanosti (kao što je mrežno skeniranje i otisak prsta), imat ćete iznimno visoku razinu odgovornosti. Trebalo bi i pola sata da napuni prilično složena forma koja bi potaknula korisnike da pronađu načine da zaobiđu sigurnost.
Najbolja sigurnost često je dovoljno neupadljiva da zadovolji zahtjeve bez da se korisniku spriječi ostvarivanje njihovog posla ili prekomjerno opterećenje autora koda prekomjernom složenosti. Doista, neki sigurnosni napadi samo su iskorištavanje ove vrste pretjerano izgrađene sigurnosti.
Izraz vrijedan pamćenja: Sustav je jednako dobar kao najslabija karika u lancu. Ako su sve transakcije strogo napisane na temelju vremena, lokacije, vrste transakcija itd., ali korisnik je potvrđen samo na temelju jednog kolačića (eng. cookies), valjanost vezivanja korisnika u zapisnik transakcija je ozbiljno oslabljena. Prilikom testiranja imajte na umu da nećete moći testirati sve mogućnosti čak i za najjednostavnije stranice. Unos koji možete očekivati neće biti u potpunosti nepovezan s unosom kojeg daje nezadovoljni zaposlenik. Zato je najbolje pregledati kôd iz logičke perspektive da biste vidjeli gdje se mogu unijeti neočekivani podaci, a zatim pratiti kako se mijenja, smanjuje ili pojačava.
Internet je pun ljudi koji se žele dokazati kršenjem koda, rušenjem vaše web stranice, objavljivanjem neprimjerenog sadržaja i na drugi način učiniti vaš dan zanimljivim. Nije bitno ako imate malu ili veliku web lokaciju, cilj ste jednostavno biti na mreži, tako da imate poslužitelj na koji se možete povezati. Mnogi programi pucanja ne razlikuju se po veličini, oni jednostavno prevoze masovne IP blokove koji traže žrtve.
Problemi PHP jezika
Slabopisanje (eng. weak typing)
PHP je slabo pisan, što znači da će automatski pretvoriti podatke netočnog tipa u očekivani tip. Ova značajka često maskira pogreške od strane razvojnog programera ili injekcije neočekivanih podataka, što dovodi do ranjivosti.
Pokušajte upotrijebiti funkcije i operatore koji ne čine implicitne konverzije tipa (npr. ===, a ne ==). Nemaju svi operatori stroge verzije (primjerice veći od i manji), a mnoge ugrađene funkcije (kao što su in_array) koriste slabo upisane usporedne funkcije prema zadanim postavkama, što otežava pisanje ispravnog koda.
Iznimke i rukovanje greškama (eng. Exceptions and error handling)
Gotovo sve PHP ugrađene metode, i mnogi PHP library-i, ne koristiti iznimke, već prijavljuju pogreške na druge načine (npr. putem obavijesti) koje omogućuju neispravnom kodu da nastavi raditi što ima za posljedicu maskiranje mnogih grešaka. U mnogim drugim jezicima i većini jezika visoke razine koji su kompetitivni s PHP-om, pogreške koje su uzrokovane pogreškama razvojnih programera ili pogrešaka izvođenja koje programer nije uspio predvidjeti, uzrokovat će prestanak rada programa, što je najsigurnije raditi.
Razmislite o sljedećem kodu koji pokušava ograničiti pristup određenoj funkciji pomoću upita baze podataka koji provjerava je li korisničko ime na crnom popisu (eng. blacklist):
$db_link = mysqli_connect('localhost', 'dbuser', 'dbpassword', 'dbname');
function can_access_feature($current_user) { global $db_link; $username = mysqli_real_escape_string($db_link, $current_user->username); $res = mysqli_query($db_link, "SELECT COUNT(id) FROM blacklisted_users WHERE username = '$username';"); $row = mysqli_fetch_array($res); if ((int)$row[0] > 0) { return false; } else { return true; } }
if (!can_access_feature($current_user)) { exit(); } // Kod za ovo svojstvo (eng. feature) ide ovdje
Postoje razne pogreške u runtime koje bi se mogle pojaviti u ovom slučaju - primjerice, veza s bazom podataka može puknuti zbog pogrešne lozinke ili gašenja poslužitelja itd., ili je veza mogla biti zatvorena od strane poslužitelja nakon što je otvorena strana klijenta. U takvim slučajevima, mysqli_ funkcije će prema zadanim postavkama izdavati upozorenja ili obavijesti, ali neće izuzeti iznimke ili kobne pogreške. To znači da kôd jednostavno i dalje radi! Varijabla $row postaje NULL, a PHP će procijeniti $row[0] isto kao NULL, i (int) $row[0] kao 0, zbog lošeg pisanja. Na kraju funkcija can_access_feature vraća true, dajući pristup svim korisnicima, bez obzira na to jesu li na crnoj listi ili ne.
Ako se koriste izvorni API-ji baze podataka, u svakoj se točki treba dodati provjera pogrešaka. Međutim, budući da to zahtijeva dodatni rad i lako se propušta, to je prema zadanim postavkama nesigurno. Također zahtijeva mnogo predložaka (eng. boilerplate). Zbog toga se pristup podacima baze podataka uvijek mora obaviti korištenjem PHP Data Objects (PDO) navedenih s ERRMODE_WARNING ili ERRMODE_EXCEPTION zastavama, osim ako postoji jasno uvjerljiv razlog za korištenje izvornih upravljačkih programa i pažljivu provjeru pogrešaka.
Često je najbolje potaknuti prijavljivanje pogrešaka što je više moguće pomoću funkcije error_reporting i nikad ne pokušavajte ukloniti poruke o pogreškama - uvijek pratite upozorenja i napišite kod koji je robustan.
php.ini
Ponašanje PHP koda često ovisi o vrijednostima mnogih konfiguracijskih postavki, uključujući i temeljne promjene stvari poput načina rukovanja pogreškama. To može otežati pisanje koda koji ispravno radi u svim okolnostima. Različiti library-i mogu imati različita očekivanja ili zahtjeve za tim postavkama, što otežava ispravno korištenje koda od treće strane.
Ugrađene funkcije od male koristi
PHP dolazi s mnogim ugrađenim funkcijama, kao što su addslashes, mysql_escape_string i mysql_real_escape_string, za koje se čini da pružaju sigurnost, ali često su puni grešaka (eng. "buggy") i zapravo su neupotrebljivi način rješavanja sigurnosnih problema. Neki od tih ugrađenih funkcija se obustavljaju i uklanjaju, ali zbog politika kompatibilnosti to traje dugo vremena.
Framework problemi
URL usmjeravanje (eng. routing)
PHP-ov ugrađeni mehanizam za usmjeravanje URL-ova služi za korištenje datoteka koje završavaju s ".php" u strukturi direktorija. Routing otvara nekoliko ranjivosti:
Upravljanje unosom (eng. input handling)
Umjesto da obrađuje HTTP ulaz kao jednostavne nizove (eng. strings), PHP će izraditi polja iz HTTP unosa, pod kontrolom klijenta. To može dovesti do zbrke o podacima i može lako dovesti do sigurnosnih grešaka. Na primjer, uzmite u obzir ovaj pojednostavljeni kôd iz mehanizma "jednokratna korekcija" koji bi se mogao upotrijebiti primjerice u kodu za poništavanje zaporke:
$supplied_nonce = $_GET['nonce']; $correct_nonce = get_correct_value_somehow(); if (strcmp($supplied_nonce, $correct_nonce) == 0) { // Go ahead and reset the password } else { echo 'Sorry, incorrect link'; }
Ako napadač koristi upit ovako:
http://example.com/?nonce[]=a
onda završimo s $supplied_nonce kao poljem. Funkcija strcmp() tada će vratiti NULL (umjesto bacanja iznimke, što bi bilo puno korisnije), a potom zbog slabog tipkanja i korištenja operatora == (jednakosti) umjesto === (identitet), usporedba će nam vratiti true (budući da je izraz NULL == 0 istinit prema PHP-u), i napadač će moći poništiti lozinku bez pružanja točne korekcije (eng. nonce).
Jezik predloška
PHP je u osnovi jezik predloška. Međutim, prema zadanim postavkama HTML se ne izbjegava, što ga čini vrlo problematičnim za upotrebu u web aplikaciji.
Ostali nedostatci
Postoje i druge važne stvari koje bi web-framework trebao dostaviti, kao što je mehanizam za zaštitu od CSRF-a koji je prema zadanim postavkama uključen. Budući da PHP dolazi s rudimentarnim framework-om koji je dovoljno funkcionalan da ljudima omogućuje stvaranje web stranica, mnogi će to učiniti bez ikakvog znanja da im je potrebna zaštita od CSRF-a.
Third party PHP kod
Library-i i projekti pisani u PHP-u često su nesigurni zbog gore navedenih problema, pogotovo kada se ne koriste odgovarajući okviri (eng. framework). Ne vjerujte PHP kodu koji se nalazi na webu, jer se mnoge sigurnosne ranjivosti mogu sakriti u naizgled nevinim kodovima.
Loše napisan PHP kod često rezultira upozorenjima koja se emitiraju, što može uzrokovati probleme. Zajedničko je rješenje isključiti sve obavijesti, što je upravo suprotno od onoga što treba učiniti (vidi gore), a vodi do progresivno lošijih kodova.
Postavke (eng. Configuration)
Ponašanje PHP-a jako je pod utjecajem konfiguracije, što se može učiniti kroz datoteku "php.ini", Apache konfiguracijske smjernice i mehanizme izvođenja (eng. runtime).
SetHandler
PHP kod bi trebao biti konfiguriran za pokretanje pomoću 'SetHandler' direktive. U mnogim je slučajevima pogrešno konfiguriran pomoću direktive "AddHandler". To funkcionira, ali čini i druge datoteke izvršne kao PHP kod - primjerice, naziv datoteke "foo.php.txt" će se rukovati kao PHP kod, što može biti vrlo ozbiljna ranjivost izvršavanja remotely ako "foo.php.txt" nije namjeravan za izvršavanje ili je došao iz zlonamjerno prenesene datoteke.
Nepouzdani podaci (eng. Untrusted data)
Svi podaci koji su proizvod ili podizvedbeni korisničkog unosa nisu pouzdani. Moraju se potvrditi, koristeći ispravnu metodologiju ili filtrirati, prije nego što ih se može smatrati "čistim".
Super globali kojima ne treba vjerovati su $_SERVER, $_GET, $_POST, $_REQUEST, $_FILES i $_COOKIE. Korisnik ne može krivotvoriti sve podatke u $_SERVER, ali znatna količina se može, posebno sve što se odnosi na HTTP zaglavlja (počinju s HTTP_).
Upload datoteka
Datoteke primljene od korisnika predstavljaju različite sigurnosne prijetnje, posebno ako drugi korisnici mogu preuzeti te datoteke. Posebno:
Uobičajene greške u obradi polja $_FILES
Uobičajeno je pronaći isječke koda koji rade nešto slično sljedećem kodu:
if ($_FILES['some_name']['type'] == 'image/jpeg') { //Nastavi s prihvačanjem datoteke kao ispravne slike
Međutim, tip se ne određuje korištenjem heuristike koja ga potvrđuje, već jednostavno čitanjem podataka poslanih HTTP zahtjevima, koje je stvorio klijent. Bolji, ali ne i savršen način validiranja tipa datoteka je korištenje finfo klase.
$finfo = new finfo(FILEINFO_MIME_TYPE); $fileContents = file_get_contents($_FILES['some_name']['tmp_name']); $mimeType = $finfo->buffer($fileContents);
Gdje je $mimeType bolja provjerena vrsta datoteke. Na taj se način upotrebljava više resursa na poslužitelju, ali se može spriječiti korisnika da šalje opasnu datoteku i zavarava kôd u pouzdanju da je riječ o slici, što bi se normalno smatralo sigurnom vrstom datoteke.
Korištenje $_REQUEST
Korištenje $ _REQUEST-a jako je obeshrabrujuče. Ovaj super global nije preporučljiv jer uključuje ne samo POST i GET podatke, već i kolačiće poslane zahtjevom. Svi se ovi podaci kombiniraju u jedan niz, čineći gotovo nemogućim utvrditi izvor podataka. To može dovesti do konfuzije i čini vaš kôd sklon pogreškama, što može dovesti do sigurnosnih problema.
Sigurnost datotečnog sustava (eng. filesystem)
PHP podliježe sigurnosti ugrađenoj u većini poslužiteljskih sustava s obzirom na dozvole na temelju datoteka i direktorija. To vam omogućuje da kontrolirate koje datoteke u datotečnom sustavu se mogu pročitati. Treba voditi računa o svim datotekama koje su čitljive svijetu, kako bi se osiguralo da su sigurne za čitanje svih korisnika koji imaju pristup tom datotečnom sustavu.
Budući da je PHP osmišljen kako bi omogućio pristup korisničkoj razini u datotečni sustav, potpuno je moguće napisati PHP skriptu koja će vam omogućiti čitanje datoteka sustava kao što su /etc/passwd, mijenjanje ethernet veza, slanje velikih poslova pisaču itd. To ima neke očite implikacije, zbog toga morate osigurati da datoteke iz kojih čitati i u koje pišete su odgovarajuće.
Razmislite o sljedećoj skripti u kojoj korisnik naznačuje da želi izbrisati datoteku u svojem home direktoriju. Ovo pretpostavlja situaciju u kojoj se PHP web sučelje redovito koristi za upravljanje datotekama, tako da Apache korisnik može izbrisati datoteke u korisničkim home direktoriju.
Primjer 1
<?php // remove a file from the user's home directory $username = $_POST['user_submitted_name']; $userfile = $_POST['user_submitted_filename']; $homedir = "/home/$username"; unlink("$homedir/$userfile"); echo "The file has been deleted!"; ?>
Sigurnost baze podataka
Danas su baze podataka kardinalne komponente bilo koje aplikacije temeljene na webu, omogućujući web stranicama da pružaju različite dinamičke sadržaje. Budući da se vrlo osjetljive ili tajne informacije mogu pohraniti u bazi podataka, trebali biste snažno razmotriti zaštitu vaših baza podataka.
Da biste preuzeli ili spremili sve informacije koje se trebaju povezati s bazom podataka, pošaljite legitiman upit, dohvatite rezultat i zatvorite vezu. Danas, uobičajeni jezik upita u ovoj interakciji je Strukturirani jezik upita (SQL).
Imajte na umu ovo jednostavno pravilo: obrana u dubini. Što više mjesta na kojima se poduzme nešto kako biste povećali zaštitu svoju bazu podataka, to je manja vjerojatnost napadača da uspije otkriti ili zloupotrijebiti pohranjene podatke. Dobar dizajn sheme baze podataka i aplikacije bavi se vašim najvećim strahovima.
Nikad ne spajati ili interpolirati podatke u SQL-u
Nikad ne izrađujte SQL upit koji uključuje korisničke podatke, bilo povezivanjem:
$sql = "SELECT * FROM users WHERE username = '" . $username . "';";
ili interpolacijom, što je u biti isto:
$sql = "SELECT * FROM users WHERE username = '$username';";
Ako je "$username" došao iz nepouzdanog izvora (i morate pretpostaviti da je, budući da to u izvornom kodu ne možete lako vidjeti), može sadržavati znakove kao što su ' koji će omogućiti napadaču izvršavanje vrlo različitih upita uključujući brisanje cijele baze podataka itd. Korištenje pripremljenih izjava i vezanih parametara mnogo je bolje rješenje.
Izbjegavati Escaping
mysql_real_escape_string nije sigurno za korištenje. Ne treba se oslanjati na to za prevenciju SQL injection-a.
Kada koristite mysql_real_escape_string na svakoj varijabli, a zatim ga povezujete sa svojim upitom sigurno ćete zaboraviti barem jednom to izvesti, i jednom je sve što je potrebno.Osim toga, morate osigurati i korištenje kvota u SQL-u, što nije prirodno učiniti ako pretpostavite da su podaci, primjerice, numerički. Umjesto toga upotrijebite pripremljene izjave ili ekvivalentne API-je koji uvijek izvode ispravnu vrstu SQL escepinga za vas. (Većina ORM-ova će to učiniti esceping, kao i stvaranje SQL upita za vas).
Uporada pripremljenih upita
Pripremljeni upiti su vrlo sigurni. U pripremljenom upiti, podaci se odvajaju od naredbe SQL, tako da se svi korisni ulazi smatraju podacima i stavljaju u tablicu onako kako je to bilo.
Gdje pripremljeni upiti ne rade
Problem je kada trebate izgraditi dinamičke upite ili trebate postaviti varijable koje nisu podržane kao pripremljena varijabla ili vaš engine baze podataka ne podržava pripremljene upite. Na primjer, PDO MySQL ne podržava ? kao LIMIT specifikator. Osim toga, ne mogu se koristiti za stvari poput naziva tablica ili stupaca u upitima "SELECT". U tim slučajevima, trebate koristiti alat za generiranje upita koje nudi okvir, ako je dostupan. Ako nije, nekoliko paketa dostupno je za upotrebu preko Composera i Packagista.
ORM
ORM (Object Relational Mappers) su dobra sigurnosna praksa. Ako koristite ORM (poput Doktrine) u svom PHP projektu, još uvijek ste skloni SQL napadima. Iako je injection upita u ORM-ovima puno teže, imajte na umu da povezivanje ORM upita uzrokuje iste nedostatke koje i povezivanje SQL upita, s toga nikad ne povezujte nizove koji se šalju u bazu podataka. ORM-ova podrška također je pripremila upite.
Uvijek budite sigurni da procijenite kod bilo koji ORM-a koji koristite za provjeru valjanosti načina na koji se izvršava generirani SQL upit. Pobrinite se da se ne povezuje vrijednosti i umjesto toga koristi pripremljene izjave interno, kao i praćenje dobre sigurnosne prakse.
Problemi kodiranja (eng. encoding)
Koristite UTF8
Mnogi novi napadački vektori oslanjaju se na zaobilaženje kodiranja. Upotrijebite UTF-8 za svoju bazu podataka i aplikaciju osim ako nemate obavezan zahtjev za upotrebom drugog kodiranja!
$DB = new mysqli($Host, $Username, $Password, $DatabaseName); if (mysqli_connect_errno()) trigger_error("Unable to connect to MySQLi database."); $DB->set_charset('utf8');
Ostale vrste injection-a
Osim SQL injekcija, postoji još nekoliko mogućih injekcija koje su uobičajene u PHP-u:
Shell injection
Nekoliko PHP funkcija naime:
Ostalo
LDAP, XPath i sve druge aplikacije trećih strana koje pokreću niz, ranjive su na ubrizgavanje. Uvijek imajte na umu da neki nizovi nisu podaci nego naredbe i tako moraju biti sigurne prije prelaska u biblioteke (eng. library) trećih strana.
XSS
Postoje dva scenarija kada je u pitanju XSS, od kojih se svaki treba ublažiti:
Bez oznaka
Većinu vremena, nema potrebe za korisničkim podacima koji sadrže neizbrisane HTML oznake prilikom izlaza (eng. output). Na primjer, kada namjeravate dump-ati vrijednost tekstualnog okvira ili izbaciti korisničke podatke u ćeliju.
Ako koristite standardni PHP za predložak ili `echo` itd., tada možete ublažiti XSS u ovom slučaju primjenom 'htmlspecialchars' na podatke. Međutim, to se ne preporučuje. Problem je u tome što se morate sjetiti da to primijenite svaki put, i ako jednom zaboravite, imate XSS ranjivost. Metodologije koje su nesigurne po defaultu moraju se tretirati kao nesigurne.
Umjesto toga, trebali biste upotrijebiti template engine koji primjenjuje HTML escaping prema zadanim postavkama. Čitav HTML treba proći kroz template engine.
Ako se ne možete prebaciti na sigurni template engine, možete upotrijebiti funkciju u nastavku na svim nepouzdanim podacima.
Imajte na umu da ovaj scenarij neće ublažiti XSS kada upotrebljavate korisnički unos u opasnim elementima (style, script, src image, itd.). Također imajte na umu da se svaki izlaz koji ne namjerava sadržavati HTML oznake treba poslati pregledniku filtriranom pomoću sljedeće funkcije.
//xss mitigation functions function xssafe($data,$encoding='UTF-8') { return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401, $encoding); } function xecho($data) { echo xssafe($data); }
//usage example <input type='text' name='test' value='<?php xecho ("'onclick='alert(1)");?>' />
Nepouzdane oznake
Kada korisnicima trebate omogućiti opskrbu HTML oznaka koje se upotrebljavaju u izlazu, kao što su komentari s richtext-om, forumski postovi, postovi na blogu i sl., ali se ne mogu pouzdati u korisnika, morate koristiti biblioteku za Secure Encoding. To je obično teško i sporo, i zato većina aplikacija ima XSS ranjivosti u njima. OWASP ESAPI ima hrpu kodeka za kodiranje različitih dijelova podataka. Tu je i OWASP AntiSammy i HTMLPurifier za PHP. Svaki od njih zahtijeva puno konfiguracije i učenja za dobro funkcioniranje, ali vam je potrebno kada želite zaštiti aplikaciju od XSS-a.
Templating engines
Postoji nekoliko templating engine-a koji mogu pomoći programeru (i dizajneru) za izlaz podataka i zaštitu od većine XSS ranjivosti. Iako njihov primarni cilj nije sigurnost, nego poboljšanje iskustva u dizajniranju, najvažniji templating engine-i automatski izbjegavaju varijable na izlazu i prisiljavaju razvojnog programera da izričito ukazuje postoji li varijabla koja se ne bi smjela izbjegavati. To čini izlaz varijabli da imaju "dobro" ponašanje. Postoji nekoliko engine-a. Popularni template engine-i su Smarty, Haanga i Rain TPL.
Savjeti
CSFR
Smanjenje CSRF-a je jednostavno u teoriji, ali je teško implementirati ispravno. Nekoliko savjeta o CSRF-u:
Upravljanje autentifikacijom i sesijama
PHP se ne isporučuje s lako dostupnim modulom za provjeru autentičnosti, morate implementirati vlastiti ili koristiti PHP okvir, nažalost, većina PHP okvira nije savršena na ovaj način, jer je razvijena zajednicom razvojnih programera otvorenog koda, a ne od sigurnosnih stručnjaka.
Upravljanje sesijama
PHP-ovi zadani objekti sesije smatraju se sigurnima, generirani ID za PHPSession dovoljno je slučajan, ali pohrana nije nužno sigurna:
Sprječavanje preuzimanja sesija
Dobra je praksa vezati sesije na IP adrese, što bi spriječilo većinu scenarija otmice sesija (ali ne i sve), no neki korisnici mogu koristiti alate za anonimnost (poput TOR-a) te bi oni imali problema s vašom uslugom.
Da biste to implementirali, jednostavno spremite klijentsku IP adresu u sesiju po prvi puta kada je stvorena te potvrdite da sesija bude ista kasnije. Isječak koda u nastavku vraća IP adresu klijenta:
$IP = getenv ( "REMOTE_ADDR" );
Imajte na umu da se u lokalnim okruženjima valjana IP adresa se ne vraća, a obično se može pojaviti niz :::1 ili :::127, time morate prilagoditi logiku provjere IP-a. Također čuvajte se inačice ovog koda koji upotrebljavaju varijablu HTTP_X_FORWARDED_FOR budući da su ti podaci korisnički uneseni i time podložni spoofingu.
Nevažeći ID sesije
Trebali biste poništiti (isključivanje kolačića, poništiti pohranu sesije, ukloniti tragove) sesije svaki put kad dođe do kršenja (npr. 2 IP adrese se primjećuje). Log je tu izuzetno koristan. Mnoge aplikacije također obavještavaju prijavljenog korisnika.
==== Zakretanje (eng. rolling) ID-a sesije
Trebali biste izmijeniti ID sesije kad god se dogodi elevacija, npr. kada se korisnik prijavi ID sesije se treba promijeniti, jer se mijenja njezina važnost.
Izložene ID sesije
ID-ovi sesije smatraju se povjerljivima, aplikacija ih ne smije izlagati nigdje (posebno kada je vezana za prijavljenog korisnika). Pokušajte ne koristiti URL kao medij sesija.
Prenesite ID sesije preko TLS-a kad god sesija sadrži povjerljive informacije, inače će pasivni napadač moći izvršiti otmicu sesije.
Fiksiranje sesije
Učinite ID sesije nevažećim nakon prijave korisnika (ili čak i nakon svakog zahtjeva) s session_regenerate_id() naredbom.
Istek sesije
Sesija treba istjecati nakon određene količine neaktivnosti i nakon određenog vremena aktivnosti. Proces isteka znači poništenje i uklanjanje sesije i stvaranje nove kada se ostvari drugi zahtjev.
Također neka vam gumb za odjavu bude blizu i uklonite sve tragove sesije pri odjavljanju.
Vrijeme neaktivnosti
Istek sesija ako je trenutni zahtjev X sekundi kasniji od zadnjeg zahtjeva. Za to trebate ažurirati podatke sesije s vremenom zahtjeva svaki put kada se podnese zahtjev. Vrijeme uobičajene prakse je 30 minuta, ali ovisi o kriterijima primjene.
Ovo isticanje pomaže kada je korisnik prijavljen na javno dostupno računalo, ali se zaboravi odjaviti. Također pomaže pri otvaranju sesija.
Opći vremenski rok
Sesija ističe ako je trenutačna sesija aktivna određeno vrijeme. To pomaže u praćenju stvari, te da biste to implementirali, potrebno je pohraniti vrijeme početka sesije.
Kolačići (eng. cookies)
Baratanje kolačićima unutar PHP sadrži neke trikove
Nikad ne raditi serijalizaciju (eng. serialize)
Nikad ne serializirajte podatke pohranjene u kolačićima. Moguće je jednostavno manipulirati, što će rezultirati dodavanjem varijabli u vaš opseg (eng. scope).
Pravilno brisanje
Za sigurno brisanje kolačića upotrijebite sljedeći isječak:
setcookie ($name, "", 1); setcookie ($name, false); unset($_COOKIE[$name]);
Prva linija osigurava da kolačić istekne u pregledniku, druga linija je standardni način uklanjanja kolačića (stoga ne možete unijeti lažne podatke u kolačić). Treća linija uklanja kolačić iz skripte. Mnogi upućuju razvojnim programerima da koriste time()-3600 za istek, ali to možda neće funkcionirati ako vrijeme preglednika nije točno.
HTTP Only
Većina modernih preglednika podržava HTTP-Only cookies. Ovi kolačići dostupni su samo putem zahtjeva HTTP-a, a ne JavaScripta, pa im XSS isječci ne mogu pristupiti. Korištenje HTTP-Only cookies je vrlo dobra praksa, ali nije zadovoljavajuća jer se u glavnim preglednicima otkrivaju mnogobrojni nedostaci koji dovode do otkrivanja HTTP-Only cookies-a putem JavaScript-a.
Da biste koristili HTTP-Only cookies u PHP-u (5.2+), ručno postavite kolačiće sesije (ne upotrebljavajte session_start):
#prototype bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
#usage if (!setcookie("MySessionID", $secureRandomSessionID, $generalTimeout, $applicationRootURLwithoutHost, NULL, NULL,true)) echo ("could not set HTTP-only cookie");
Parametar putanje postavlja putanju na kojoj je kolačić važeći, npr. ako imate svoju web stranicu na adresi example.com/some/folder putanja bi trebala biti /some/folder, no druge aplikacije koje se nalaze na example.com mogu također vidjeti vaš kolačić. Ako ste na cijeloj domeni, ne morate se opterećivati. Ako je postavljen siguran parametar, kolačić se može prenijeti samo preko HTTPS-a. Pogledajte primjer u nastavku:
$r=setcookie("SECSESSID","1203j01j0s1209jw0s21jxd01h029y779g724jahsa9opk123973",time()+60*60*24*7 /*a week*/,"/","owasp.org",true,true); if (!$r) die("Could not set session cookie.");
Internet Explorer
Mnoge inačice Internet Explorera obično imaju problema s kolačićima. Uglavnom postavljanje isteka vremena na 0 popravlja probleme.
Autentifikacija
Zapamti me (eng. remember me)
Mnoge web stranice imaju ranjive "zapamti me" značajke. Često su ove ranjivosti trivijalno iskoristive (npr. spremanje userid = 13 ili username = paul & password = abcdefg).
Nikad ne spremajte korisničko ime / zaporku ili bilo kakve relevantne podatke u kolačić.
Umjesto toga, trebali biste upotrebljavati slučajni (eng. random) token (najmanje 16 znakova iz random_bytes() naredbe, kodiranih kako god želite), koji bi trebao biti pohranjen na samo dva mjesta: korisnikov kolačić i zapis podataka baze podataka. Međutim, čak je i ova strategija puna opasnosti.
Da biste izbjegli neželjene "kanale", razmislite o prihvaćanju ove strategije za "zapamti me" tokene:
1. Generirati dva tokena: Selector i verifier pomoću CSPRNG. 2. Pohranite selector i SHA-384 hash verifier-a u bazu podataka. 3. Pohranite selector i verifier u kolačić (recimo: rememberme).
Kada korisnik posjeti vašu web stranicu i oni nisu ovjereni, ali imaju cookie "rememberme", pokrenite ovaj postupak:
1. Podijelite kolačić u selector i verifier komponente. 2. Pretražite bazu podataka pomoću selectora. 3. Ako se pronađe redak, zgrabite taj zapis. 4. Usporedite SHA-384 korisničkog verfiera s vrijednošću pohranjenom u bazi podataka, koristeći hash_equals(). 5. Ako se podudaraju, automatski autentificirajte korisnika.
Mogući napadi (Apache)
Kada se PHP koristi kao Apache modul nasljeđuje Apache korisnička dopuštenja (obično one korisnika "nitko"). To ima nekoliko utjecaja na sigurnost i autorizaciju. Na primjer, ako koristite PHP za pristup bazi podataka, osim ako ta baza podataka nema ugrađenu kontrolu pristupa, morat ćete napraviti bazu podataka dostupnom korisniku "nitko". To znači da zlonamjerna skripta može pristupiti i mijenjati bazu podataka, čak i bez korisničkog imena i zaporke. Posve je moguće da bi web pauk (eng. web-spider) mogao posrnuti preko web stranice administratora baze podataka i ispustiti sve vaše baze podataka. Možete se zaštititi od toga s autorizacijom programa Apache ili možete izraditi vlastiti pristupni model pomoću LDAP, .htaccess datoteka i sl. Te uključiti taj kôd kao dio vaših PHP skripti.
Često, kada se sigurnost utvrdi do točke u kojoj PHP korisnik (u ovom slučaju, korisnik Apachea) ima vrlo malo rizika, otkriva se da je PHP sada spriječen od pisanja bilo koje datoteke u korisnički direktorije. Ili je možda spriječeno pristupanje ili mijenjanje baza podataka. Jednako je osigurano od pisanja dobrih i loših datoteka, ili unosa dobrih i loših transakcija baze podataka.
Česta sigurnosna pogreška u ovom trenutku je dopustiti apache root dopuštenja ili eskalirati apacheove sposobnosti na neki drugi način. Povećanje ovlasti Apache korisnika za root je iznimno opasno i može ugroziti cijeli sustav, tako da su sudoing, chroot'ing ili inače pokretanje kao root ne trebaju uzeti u obzir oni koji nisu sigurnosni stručnjaci. Postoje jednostavnija rješenja. Korištenjem open_basedir možete kontrolirati i ograničiti koji se direktoriji mogu koristiti za PHP. Također možete postaviti područja kao apache-only, kako biste ograničili sve aktivnosti na webu u datotekama koje su ne-korisničke ili ne-sistemske.
Error Reporting (izvještavanje o pogrešakama)
Sa PHP sigurnost, postoje dvije strane za izvještavanje o pogreškama. Jedno je korisno za povećanje sigurnosti, a drugo je štetno.
Standardna taktika napada uključuje profiliranje sustava tako što se unose nepravilni podaci i provjeravaju vrste i kontekst pogrešaka koje se vraćaju. To omogućava osobi koja krekira sustav za ispitivanje informacija o poslužitelju, kako bi se utvrdile moguće slabosti. Na primjer, ako je napadač prikupio informacije o stranici na temelju prethodnog slanja obrasca, može pokušati nadjačati varijable ili ih mijenjati:
<form method="post" action="attacktarget?username=badfoo&password=badfoo"> <input type="hidden" name="username" value="badfoo" /> <input type="hidden" name="password" value="badfoo" /> </form>
PHP pogreške koje se obično vraćaju mogu biti vrlo korisne za razvojnog programera koji pokušava ispraviti skriptu, što ukazuje na stvari kao što su funkcija ili datoteka koja nije uspjela, datoteka PHP nije uspjela i broj čvora u kojem je došlo do kvara. Ovo su sve informacije koje se mogu iskoristiti. Nije neuobičajeno da PHP programer upotrebljava show_source(), highlight_string() ili highlight_file() kao mjeru za uklanjanje pogrešaka, ali na živom web mjestu to može otkriti skrivene varijable, neprovjerenu sintaksu i druge opasne informacije. Posebno je opasno pokrenuti kôd iz poznatih izvora s ugrađenim vodičima za uklanjanje pogrešaka ili upotrebom uobičajenih tehnika uklanjanja pogrešaka. Ako napadač može utvrditi koja opća tehnika koju koristite, možda će pokušati prisiliti (eng. brute-force) stranicu, šaljući različite uobičajene nizove za uklanjanje pogrešaka:
<form method="post" action="attacktarget?errors=Y&showerrors=1&debug=1"> <input type="hidden" name="errors" value="Y" /> <input type="hidden" name="showerrors" value="1" /> <input type="hidden" name="debug" value="1" /> </form>
Bez obzira na način vođenja pogrešaka, sposobnost sustava za ispitivanje pogrešaka dovodi do pružanja informacija o napadaču. Na primjer, vrlo stil generičke pogreške u PHP-u ukazuje na to da sustav koristi PHP. Ako je napadač pregledavao .html stranicu i htio probati za back-end (tražiti poznate slabosti u sustavu), hranjenjem pogrešnih podataka oni bi mogli odrediti da je sustav izgrađen s PHP-om.
Pogreška u funkciji može ukazati može li sustav pokrenuti određeni engine baze podataka ili dati tragove o tome kako je web stranica programirana ili dizajnirana. To omogućuje dublju istragu u otvorenim portovima baze podataka ili traženju specifičnih grešaka ili slabosti na web stranici. Na primjer, hranjenjem različitih dijelova loših podataka, napadač može odrediti redoslijed provjere autentičnosti u skripti (od pogrešaka u redcima brojeva), kao i sonda (eng. probe) za iskorištavanje koja se može iskoristiti na različitim mjestima u skripti.
Datotečni sustav ili opća PHP pogreška može ukazivati na dopuštenja koja ima web poslužitelj, kao i strukturu i organizaciju datoteka na web poslužitelju. Pogrešni kôd napisan od strane razvojnog programera može pogoršati taj problem, što dovodi do jednostavnog iskorištavanja nekadašnjih "skrivenih" informacija.
Postoje tri glavna rješenja ovog problema. Prvo je proučavanje svih funkcija i pokušaj nadoknade većine pogrešaka. Drugi je onemogućiti izvješćivanje o pogreškama u potpunosti o pokretanju koda. Treći je da koristite PHP prilagođene funkcije za rukovanje pogreškama kako biste stvorili vlastiti upravljač pogrešaka. Ovisno o sigurnosnim pravilima, sva tri mogu se primijeniti na vašu situaciju.
Jedan od načina kako uhvatiti propust prije vremena je iskoristiti PHP vlastite error_reporting(), kako bi vam pomogao da zaštitite kôd i pronađete promjenjivu uporabu koja može biti opasna. Testiranjem koda, prije implementacije, s E_ALL, se brzo može pronaći područja na kojima vaša varijabla može biti otvorena za trovanje ili izmjenu na druge načine. Kada budete spremni za implementaciju, trebali biste potpuno ili potpuno onemogućiti izvješćivanje o pogreškama postavljanjem error_reporting () na 0 ili isključivanjem zaslona pogreške pomoću opcije php.ini display_errors kako biste izdvojili kôd od sondiranja. Ako odlučite izvršiti potonji, trebali biste definirati put do svoje log datoteke pomoću error_log ini datoteke i uključiti log_errors.
<?php if ($username) { // Not initialized or checked before usage $good_login = 1; } if ($good_login == 1) { // If above test fails, not initialized or checked before usage readfile ("/highly/sensitive/data/index.html"); } ?>
Skrivanje PHP-a
Općenito, sigurnost opskurnošću (eng. obscurity) jedan je od najslabijih oblika sigurnosti. Ali u nekim slučajevima, svaka dodatna sigurnost je poželjna.
Nekoliko jednostavnih tehnika može pomoći sakriti PHP, možda usporavajući napadača koji pokušava otkriti slabosti u vašem sustavu. Ako postavite expose_php na 0 u datoteci php.ini, smanjujete količinu informacija koje su im dostupne. Druga taktika je da konfigurirate web poslužitelje kao što je apache za analiziranje različitih vrsta datoteka kroz PHP, bilo s .htaccess-om, ili u apache konfiguracijskoj datoteci. Zatim možete upotrijebiti zavaravajuća proširenja datoteka:
# Make PHP code look like other code types AddType application/x-httpd-php .asp .py .pl
Ili ga u potpunosti učiniti neprimjetnim:
# Make PHP code look like unknown types AddType application/x-httpd-php .bop .foo .133t
Ili ga sakrijte kao HTML kôd, mana je što ima slabu izvedbu, jer će se sav HTML analizirati kroz PHP engine:
# Make all PHP code look like HTML AddType application/x-httpd-php .htm .html
Da bi to djelotvorno funkcioniralo, morate preimenovati PHP datoteke s gore navedenim proširenjima. Iako je to oblik sigurnosti kroz neprimjetnost, to je manja preventivna mjera s nekoliko nedostataka.
Održavati svijest o novijim verzijama
PHP, kao i svaki drugi veliki sustav, pod stalnom je kontrolom i poboljšanjem. Svaka nova inačica često uključuje i velike i male izmjene kako bi se poboljšala sigurnost i popravili svi nedostatci, konfiguracijske pogreške i druga pitanja koja će utjecati na ukupnu sigurnost i stabilnost vašeg sustava.
Poput drugih jezika i programa skriptnih jezika na razini sustava, najbolji je pristup ažurirati često i održavati svijest o najnovijim verzijama i njihovim promjenama.