Kriptimi
Të dhënat në Web transferohen në formë të fajllave tekstualë. Edhe nëse është përdorur SSL për ta shifruar komunikimin browser-server, ato të dhëna prapë kthehen në trajtë tekstuale në momentin e arritjes në server.
Pra, të dhënat e pranuara me cilëndo metodë (GET, POST, databazë, JSON, etj.) dhe pa marrë parasysh a është përdorur a jo SSL, në përfundim të procesit të transferimit - ruhen si tekste të rëndomta.
Kështu, ato të dhëna edhe kur ruhen në databazë, do të jenë të ruajtura si tekste. Nëse ndodh që dikush me SQL injection attack arrin t’i qaset të dhënave në server, ai do të jetë në gjendje t’i lexojë të dhënat e ruajtura aty. Kjo në veçanti është e disfavorshme nëse të dhënat janë sensitive, siç janë: fjalëkalimi, numri i kartelës së kreditit, të dhënat financiare, etj.
Për ta parandaluar rrezikun e leximit të të dhënave sensitive nga databaza, duhet të përdoren metoda të shifrimit të të dhënave në server.
Janë dy metoda kryesore për shifrimin e të dhënave:
- Crypt
- Hash
Në këtë kapitull do të fokusohemi vetëm në cryptographic hash functions.
Hash
Hash është një metodë tjetër e shifrimit të të dhënave, veçori kryesore e së cilës është se teksti i enkriptuar nuk mund të rikthehet në formën e vet origjinale. Për këtë, ky algoritëm gjen zbatim vetëm në raste të caktuara, dhe kryesisht përdoret për enkriptimin e fjalëkalimeve (password), si dhe për verifikimin e integritetit të fajllave (checksum).
Funksionet në PHP për hash janë:
- hash()
- md5()
- sha1()
Për nevojat e aplikacionit tonë, ne do të përqëndrohemi vetëm në sha1(), por parimet janë të njëjta për secilin prej funksioneve të cekura.
E marrim shembull një fjalëkalim të mundshëm: ick2015.
1 <?php
2 $fjalekalimi = "ick2015";
3 echo $fjalelalimi." ".sha1($fjalekalimi);
4 ?>
Rezultati:
1 ick2015 1488c61e22f54e75d43ac3935fea5e85451eb6be
Pra, nga stringu ick2015 fitohet hash-i:
1488c61e22f54e75d43ac3935fea5e85451eb6be
Gjatësia e stringut rezultues te sha1() është 40 karaktere, pa marrë parasysh sa ka qenë gjatësia e stringut origjinal!
Teorikisht, për çdo tekst të ndryshëm do të gjenerohet një hash tjetër. Pra, nuk duhet që dy tekste të ndryshme ta japin hash-in e njëjtë. Kjo vetëm në teori, gjersa në praktikë kemi raste të evidentuara të kolizioneve (collisions), pra të rasteve kur dy tekste që s’kanë kurrfarë ngjashmërie me njëri-tjetrin - kanë dhënë hash të njëjtë. Më shumë kolizione ka te algoritmet që kanë hash më të shkurtër. Funksioni md5() gjeneron hash 32 karakterësh, gjersa sha1(), siç u përmend më sipër - gjeneron hash 40 karakterësh. Kjo është arsyeja që ndërmjet md5() dhe sha1(), gjithmonë zgjedhet ky i dyti. Rekomandim i ekspertëve të sigurisë si dhe të institucioneve të ndryshme është që md5() të nxirret krejtësisht nga përdorimi.
Për thjeshtim të spjegimit, le ta kuptojmë hash-in si një “nënshkrim” të një teksti. Sikur çdo njeri që ka nënshkrim që dallon prej të tjerëve, ashtu edhe çdo tekst, teorikisht, duhet ta ketë hash-in e vetë unik.
Pyetja që vie vetvetiu është: si e fitoj përmbajtjen origjinale të tekstit duke u bazuar vetëm në 40 karaktere? Si e kthej një tekst me 10000 karaktere nga një hash 40 karakterësh? Përgjigja është: hash as nuk përdoret për kthimin e përmbajtjes origjinale! Pra, përmbajtja e hash-it s’ka kurrfarë lidhje përmbajtësore me tekstin origjinal, është vetëm një “nënshkrim” unik dhe në vete nuk përmban asgjë që mund të asocojë në përmbajtjen e tij.
Thënë ndryshe, prej nënshkrimit nuk mund ta rikonstruktojmë njeriun, por dijmë se nënshkrimi i kujt është kur ai njeri nënshkruhet prapë! Kur ai nënshkruhet prapë, i krahasojmë me nënshkrimet e të gjithë të nënshkruarve, dhe kur ta gjejmë nënshkrimin e njëjtë - konstatojmë se është ai njeri ka qenë këtu më parë.
Përdorimi i hash-it në Web aplikacion
T’i kthehemi Web aplikacionit tonë. Të gjithë anëtarët e regjistruar e Web faqes, i kanë të dhënat të regjistruara në një MySQL tabelë, në rastin tonë të quajtur “users”. Disa nga fushat e tabelës janë:
- ID-ja unike e anëtarit, që është një numër rendor, i ndryshëm për secilin anëtar
- Emri dhe mbiemri i anëtarit
- Email adresa e anëtarit
- Fjalëkalimin i anëtarit, etj.
Nëse zgjedhim që fjalëkalimin e anëtarit ta ruajmë si plain text, pra tekst të pashifruar, kjo ka ndikim negativ në sigurinë e të dhënave të anëtarëve sepse ato të dhëna sensitive mund të “nxirren” me një SQL injection attack siç u cek në fillim. Prandaj, fjalëkalimet ruhen në formë të hash, sepse sulmuesi që vie në dispozicion të tabelës së anëtarëve, nuk do të jetë në gjendje t’ua dijë fjalëkalimet duke u bazuar në hash. Pa marrë parasysh çfarë fjalëkalimesh kanë pasur dhe çfarë gjatësie kanë pasur fjalëkalimet, në kolonën e fjalëkalimeve, sulmuesi do të shohë vetëm vargje shkronjash e numrash “të rastësishme” me gjatësi prej 40 karakteresh. Duke ditur se nuk mund të ketë funksion që e bën të kundërtën e sha1(), ne mund të pretendojmë se të dhënat janë të palexueshme për sulmuesin.
Rainbow tables
Por, në praktikë, ka dallim fare të vogël në nivelin e sigurisë ndërmjet plain text dhe hash!
Pse?! Sepse tashmë një kohë të gjatë janë duke u formuar databaza tejet të mëdha në të cilat ruhen tekste të ndryshme të shkurtëra bashkë me hash-in korrespondues. Mjafton që të shënohet hash-i, dhe nga databaza do të nxirret teksti origjinal brenda një kohe të shkurtër! Pra, nuk ka nevojë që ndonjë kompjuter të punojë me orë e ditë të tëra për të bërë “crack”, mjafton vetëm një kërkim në databazë! Këto quhen “rainbow tables” dhe tashmë ka në qarkullim databaza të tilla të madhësisë prej disa Tb.
Shembull: https://crackstation.net
Nëse e marrim psh fjalën “tralala”, me sha1() do ta fitojmë këtë hash:
bb31fbff8fac91c17ee6052d98084e6e63184859
Tash, këtë hash e kopjojmë dhe e vendosim në fushën përkatëse të Web sajtit të cekur dhe shtypim butonin “Crack Hashes”. Si rezultat do të shfaqe fjala “tralala”.
Nëpër “rainbow tables”, gjasat më të mëdha për t’u “thyer” i kanë fjalëkalimet vijuese:
- Fjalëkalimet e shkurtëra: sesame (084a3501edef6845f2f1e4198ec3a2b81cf5c6bc)
- Fjalëkalimet që përmbajnë vetëm shkronja, qoftë uppercase, qoftë lowercase: prishtinacity (16c952e331eb893de13258bafe24e0d126d3e7c1)
- Fjalëkalimet që përmbajnë vetëm numra, psh datëlindjet: 27031990 (9aa1c46a255d9b308bda1197a67757a63b05cee8)
- Fjalëkalimet të çfarëdo gjatësie por që përmbajnë fraza të njohura: hastalavistababy (5541202440b69214af1f24fe6ba385da6709a2bea)
Hash-at e dhënë si shembull mund të testohen në Crack Station, dhe do të shohim se të gjithave u gjindet teksti origjinal!
Meqë në “rainbow tables” tashmë ruhet një numër enorm i hash-ave dhe ky numër është gjithnjë në rritje, ruajtja e fjalëkalimeve në formë hash-i ka fare pak vlerë në aspektin e sigurisë, nëse kemi parasysh fjalëkalimet e shkurtëra dhe ato tipike. Fjalëkalimet e gjata dhe komplekse (që përbëhen nga kombinimi i shkronjave të vogla e të mëdha, numrave dhe shenjave speciale, psh. k!uhFRn9ds54&^!2) nuk janë të rrezikuara nga “thyerja” në “rainbow tables”. Thënë më saktë, ende jo. Me rritjen e fuqisë procesorike të kompjuterëve, në veçanti kur ato lidhen në distributed computing, si dhe të rënies së çmimit të disqeve me kapacitet të madh, nuk është larg dita se këto tabela do të përmbajnë edhe fjalëkalime komplekse, e me këtë t’i diskualifikojnë fjalëkalimet që sot mund të konsiderohen të sigurta.
Fjalëkalimet komplekse, ndonëse më të sigurta, ato janë të vështira për t’u mbajtur në mend nga anëtarët. Për këtë arsye, dikush i ruan fjalëkalimet komplekse psh në email, dhe kështu i hapin dyert atyre që arrijnë t’ua “thejnë” llogarinë e emailit, me ç’rast do të jenë në gjendje t’i marrin këto fjalëkalime! Nëse ruani fjalëkalime të tilla në inbox, atëherë së paku bëjeni fjalëkalimin e emailit sa më kompleks e megjithatë të mbajtshëm në mend. Apo edhe më mirë: fjalëkalimet komplekse i printoni dhe i ruani si hard copy në ndonjë vend të sigurtë.
Ju mund të formoni fjalëkalime komplekse edhe nga tekstet që mbahen në mend lehtë. Psh. e marrim fjalën “Prishina”. Atë mund ta shënojmë si “pR!$hT!nA”, ku njëra shkronjë është e vogël e ajo vijuesja e madhe e kështu me radhë (p-R-h-T-n-A), shkronja “i” është zëvendësuar me “!”, ndërsa shkronja “S” është zëvendësuar me “$”. Ky është fjalëkalim kompleks por që bazohet në një fjalë e njohur.
Salt
Një zgjidhje që mund ta përmirësojë nivelin e sigurisë së fjalëkalimeve është ajo me përdorimin e salt. Çka është salt? Është një numër apo varg shkronjash e numrash, psh: 521457826, a9d5re2.
Salti i bashkangjitet fjalëkalimit në fillim apo në fund, dhe pastaj nxirret hash-i, që në këtë rast do të jetë krejt tjetërfare.
Shkojmë me shembull: Fjala “prishtinacity” e ka si hash stringun “16c952e331eb893de13258bafe24e0d126d3e7c1”.
E marrim një salt, psh. 521457826 dhe ia bashkangjesim në fillim dhe e nxjerrim hashin:
sha1(“521457826prishtinacity”); me ç’rast fitohet hashi i ri: c720ec040d745b38e97df275130c801d7af344aa
Ky tash është hashi i një fjalëkalimi kompleks i përbërë nga 22 karaktere, dhe si i tillë, ka pak gjasa të gjendet në ndonjë “rainbow table”. Edhe po të dekriptohej nga “rainbow tables”, do të rezultonte si “521457826prishtinacity”, ndërkohë që fjalëkalimi real është “prishtinacity”, kështu që prapë do të ishte i papërdorshëm. Natyrisht, me shikim të kujdesshëm, këtë fjalëkalim megjithatë do ta gjenim sepse i paraprin një varg i numrave, të cilat kur i heqim e fitojmë fjalëkalimin “e pastër”. Për këtë, për siguri sa më të lartë, në vend të numrave duhet të përdoren vargje alfanumerike, pra shkronja e numra, asisoj që sulmuesi ta ketë sa më të vështirë të kuptojë se ku fillon e ku mbaron fjalëkalimi, e cila pjesë është salt, pra fragment i shtuar.
Vazhdojmë me hashin që e fituam më sipër. Ai hash, pra c720ec040d745b38e97df275130c801d7af344aa ruhet në tabelën e përdoruesve, në rreshtin e dedikuar për përdoruesin konkret. Duke qenë se është hash i dy teksteve të kombinuara, ne duhet ta ruajmë edhe salt-in në të njëjtin rresht (record), në një fushë të veçantë. Kjo do të na nevojitet gjatë procesit të login, ku fjalëkalimit të shënuar në formularin e login duhet t’ia shtojmë salt-in nga databaza, për t’i bërë hash dhe për ta krahasuar me hash-in e ruajtur në databazë për atë anëtar.
Spjeguar step-by-step:
- Anëtari regjistrohet, duke i dhënë këto informata: emri dhe mbiemri, email adresa, dhe dy herë fjalëkalimi. Dhënie e fjalëkalimit dy herë s’ka lidhje me hash, por bëhet për t’u siguruar se mos ka gabuar anëtari gjatë shkrimit të fjalëkalimit në formular, ku shkronjat e shkruara nuk shihen. Nëse e shkruan dy herë njëjtë, kjo do të thotë se ai di saktë çka ka shkruar dhe se do ta mbajë në mend. Por, nëse ai e shkruan dy herë fjalëkalimin e njëjtë por ai fjalëkalim nuk është fjalëkalimi që ka menduar se është duke e shkruar, ai nuk do të ketë qasje më vonë me atë fjalëkalim që mendon se e ka shkruar. Kjo ndodh psh kur përdoruesi mendon se është duke e përdorur tastierën amerikane, e në fakt e ka pasur të aktivizuar atë shqipe. Në këtë rast, fjalëkalimi i supozuar “prizreni” do të dërgohet në fakt si “priyreni”, meqë Y dhe Z i kanë vendet e ndërruara në tastierë, kur e përdorim tastierën shqipe! Përdoruesi nuk do ta dijë e ka gabuar, sepse të dy herat e ka shënuar të njëjtë dhe sistemi do të raportojë se gjithçka është në rregull.
- Lexohen “POST data” nga skripta për regjistrim dhe gjersa të gjitha fushat tjera do të regjistrohen në databazë të pandryshuara, fusha e fjalëkalimit së pari duhet të përgatitet.
- Me këtë “përgatitje” nënkuptojmë gjenerimin e një numri të madh (apo vargu tekstual) të rastësishëm, psh. një numër ndërmjet 1000000 deri 9999999.
- Numri i rastësishëm bashkangjitet me fjalëkalimin e dërguar nga formulari i regjistrimit dhe prej kësaj nxirret hash-i. Pra,
$pass1 = trim($_POST[‘password1’]);
$random = rand(1000000, 9999999); $passhash = sha1($random.$pass1);
1$pass1=trim($_POST['password1']); - Ky hash tash ruhet në tabelën e anëtarëve, bashkë me të dhënat e tjera.
- Pra, në databazë nuk u ruajt hash-i origjinal, por hashi i ri i kombinimit salt + fjalëkalimi.
Ky ishte spjegimi i procesit të regjistrimit. Hashi dhe salt-i tash e tutje do të na hyjnë në punë gjatë procesit të identifikimit, gjegjësisht gjatë login. - Anëtari klikon linkun për login, e plotëson fushën për email e pastaj atë për fjalëkalim, pastaj butonin për dërgimin e formularit. - Skripta për verifikim (në rastin tonë: check.php), i lexon të dhënat e dërguara me POST. - Merret email adresa dhe bëhet një SQL query për të verifikuar a ekziston ajo email adresë në tabelën e përdoruesve. - Nëse nuk ekziston jepet raporti se nuk ekziston dhe ofrohet mundësia që vizitori të shkojë prapë te skripta për login. - Nëse ekziston ai email, atëherë lexohen të gjitha të dhënat nga record-i i atij anëtari: ID-ja, emri dhe mbiemri, hashi i ruajtur gjatë regjistrimit, si dhe salt-i. - Merret fjalëkalimi i lexuar nga POST dhe i bashkangjitet salt-it të lexuar nga databaza, dhe në atë formë të bashkuar iu nxirret hash-i. - Krahasohet hashi i përfituar kështu me hashin që më parë u lexua nga databaza. - Nëse hashat nuk janë të njëjtë, dmth fjalëkalimi i shënuar në formular nuk ka qenë i saktë. Raportohet gabimi, jepet opsioni për të bërë login sërish. - Nëse hashat janë të njëjtë, atëherë konkludohet se fjalëkalimi i shënuar në formular është i njëjtë me atë që është ruajtur në databazë. Me këtë rast, në variablin e sesionit $_SESSION[‘auth’] vendoset vlera TRUE. - Gjithkund nëpër skripta ku lejohet aksesi vetëm anëtarëve bëhet verifikimi me:
1 if (isset($_SESSION['auth']) && $_SESSION['auth'])
Nëse plotësohet kushti, anëtari ka akses në atë faqe. Nëse jo, atij i raportohet se nuk ka privilegje për ta vizituar atë faqe, apo redirektohet në “Page Not Found”.
Në rastet kur një anëtar e harron fjalëkalimin e vet, nuk do të jetë e mundur që atij t’i dërgohet fjalëkalimi sepse, siç u cek më sipër, nga hash-i nuk mund të përfitohet teksti origjinal. Në këto situata, duhet të krijohet një fjalëkalim i ri që anëtarit i dërgohet në email në formë jo të kriptuar, ndërsa në databazë regjistrohet e kriptuar.
Dictionary attack
Hashi i kombinuar me salt është tërësisht joefektiv karshi dictionary attack!
Çka është “dictionary attack”?
Siç e pamë, salt i bën komplekse edhe fjalëkalimet e thjeshta, duke ia shtuar një varg numrash përpara apo prapa. Mirëpo, fjalëkalimet e enkriptuara me hash dhe salt janë të efektshme për mbrojtje nga ata që e kanë në dorë tabelën e anëtarëve nga databaza.
Ndërsa, dictionary attack bëhet asisoj që një skriptë e thirr direkt faqen ky lexohen ‘POST data’ (në aplikacionin tonë “check.php”), duke i dërguar fjalëkalime nga një tabelë e parapërgatitur e mijëra fjalëkalimeve të mundshme. Kjo mund të realizohet psh me funksionet e caktuara të PHP apo të ekstensionit cURL.
Për këtë është me rëndësi që të bllokohet qasja në skriptën që i lexon “POST data”, duke verifikuar nëse atë skriptë e ka thirrur ndonjë skriptë tjetër e aplikacionit tonë, apo kërkesa ka ardhur nga jashtë. Për këtë mund të përdoret superglobali $_SERVER[‘PHP_REFERER’]. Por, kjo variabël e lexon vlerën nga “HTTP requests”, e këto edhe mund të falsifikohen nga sulmuesi me eksperiencë.
Konkluzioni: Vetëm fjalëkalimet komplekse ofrojnë një nivel sigurie, por megjithatë - në Web kurrë nuk ka mjaft siguri.
Testimi i algoritmeve të ndryshme për hash, mund të bëhet duke klikuar në linkun vijues: