Yawa Skrevet 29. august 2014 Del Skrevet 29. august 2014 (endret) Jeg har forsøkt å få til en løsning hvor man kan logge inn på et passordbeskyttet område for å administrere nettstedet. Type backend område for webmaster/administrator o.l. Dette er så langt jeg har kommet. Og hadde likt om noen kunne tatt en titt og gitt litt tilbakemeldinger på det hele.. Nå i testperioden har jeg kun benyttet meg av definerte konstanter for brukernavn og passord, men dette kan naturligvis korrigeres til å foreta et oppslag mot en database for å finne match basert på brukerens input. Ved å sjekke om en bruker er logget inn, kan jeg nå lett avgjøre om spesifikt innhold skal være synlig eller ikke. Også sperre av hele sider etc... Jeg starter det hele med å opprette og starte en SESSION, samt tilknytte en klasse med login-data for dette backendet: <?php if(!isset($_SESSION)){ session_name('__KD'); session_set_cookie_params(0,'/','','',true); session_start(); } if(!isset($_SESSION['backend'])){ $_SESSION['backend'] = new KD_backend(); } ?> Denne klassen er foreløpig skremmende enkel, og er på grensen til at jeg ikke selv fatter hvorfor jeg har laget den. Men regner med at den kommer til nytte, på sikt, når ting blir mer kompleks på ett eller annet sett og vis: <?php class KD_backend { private $accessible = false; private $username = ''; # access public function accessible(){ return $this->accessible; } // # user public function getUsername(){ return $this->username; } // # login public function login($username){ $this->accessible=true; // set access $this->username=$username; // set username } // # logout public function logout(){ $this->accessible=false; $this->username=''; } // } ?> Landingssiden for backendet, under testing, ser foreløpig slik ut - ferdig strippa: <?php if($_SESSION['backend']->accessible()===false): ?> <form name="BackendLogin" method="post" action="<?=request_uri?>"> <input type="hidden" name="load-script" value="Backend" readonly> <label><input type="text" name="username" placeholder="Brukernavn" autofocus></label> <label><input type="password" name="password" placeholder="Passord"></label> <label><input type="submit" name="submit__BackendLogin" value="Logg inn"></label> </form> <?php else: ?> <form name="BackendLogout" method="post" action="<?=request_uri?>"> <input type="hidden" name="load-script" value="Backend" readonly> <input type="submit" name="submit__BackendLogout" value="Logg ut"> </form> <?php endif; ?> Som dere ser, så kjører jeg en rask status sjekk mot $_SESSION['backend']->accessible() for å se om brukeren er logget inn, eller ikke. "False" betyr at brukeren ikke er logget inn - så et login skjema vises. (Tilsvarende metode benyttes for å avgjøre tilgang til innhold etc.) En ekstern skript-fil hentes inn og håndterer forespørselen til brukeren: <?php extract($_POST,EXTR_PREFIX_ALL,'p'); # login details define('user','admin'); define('password',password_hash('0112358',PASSWORD_DEFAULT)); // passordet er begynnelsen på tallrekken "Fibonacci". Sjekk den ut // # notification messages $success_LoggedIn = 'Du ble vellykket logget inn som <b>'.user.'</b>!'; $success_LoggedOut = 'Du ble vellykket logget ut'; $error_EmptyFields = 'OBS! Ett eller flere påkrevde felter er tomme...'; $error_NoMatch = 'Innloggingen misslykkes.<br>Venligst sjekk at brukernavnet - <b>'.$p_username.'</b> - er riktig, og at du har benyttet riktig passord...'; // # execute login script if (isset($p_submit__BackendLogin)){ if(empty($p_username)||empty($p_password)){ // check form fields KD::addNotice('error',$error_EmptyFields); }else{ if($p_username==user){ // check username if (password_verify($p_password, password)){ // verify password KD::addNotice('success',$success_LoggedIn); session_regenerate_id(); $_SESSION['backend']->login(user); session_write_close(); } else { KD::addNotice('error',$error_NoMatch); // (username was correct, but the password didn't match) } } else { KD::addNotice('error',$error_NoMatch); // (username was incorrect) } } KD::redirect(); } // # execute logout script if (isset($p_submit__BackendLogout)){ KD::addNotice('success',$success_LoggedOut); session_regenerate_id(); $_SESSION['backend']->logout(); session_write_close(); KD::redirect(); } // ?> Det er to skript i denne filen: logg inn, og logg ut. Hvilket skript som kjøres blir avgjort etter hvilken "submit"-knapp/skjema som benyttes. ("KD" er en klasse jeg har opprettet selv. - addNotice() og redirect() er vel ganske selvforklarende - håper jeg..) Jeg er i utgangspunktet fornøyd med løsningen min så langt. Det fungerer veldig tilfredstillende. Men det er svært enkelt/simpelt, så jeg hadde håpet på å få litt tilbakemeldinger på ting som eks. sikkerhet og sånt. Om det er noen guruer på å oppdage svakheter etc. så tar jeg imot alle tilbakemeldinger i håp om å kunne forbedre. Tillegg: Selve utloggingsbiten har jeg også vert gjennom denne varianten: # execute logout script if (isset($p_submit__BackendLogout)){ KD::addNotice('success',$success_LoggedOut); $params = session_get_cookie_params(); setcookie(session_name(),'',time()-3600, $params['path'], $params['domain'], $params['secure'], isset($params['httponly'])); KD::redirect(); } // Men denne løsningen kom i konflikt med tilbakemeldingen fra KD::addNotice(); da denne samler opp meldinger i ett array, og plasserer de i en SESSION kalt "_SITE_notices". Meldingen ble stoppet, eller falt bort, før de ble vist til brukeren. Og det kan jeg ikke ha noe av. Endret 29. august 2014 av Yawa88 Lenke til kommentar
Lanes Skrevet 30. august 2014 Del Skrevet 30. august 2014 (endret) Hadde skrevet en lengre post her, men så krasjet nettleseren Her kommer en veldig forkortet versjon. Kan ikke kalle meg en guru, men det er noen åpenbare ting her. 1. Ikke bruk extract() på usikre data 2. Du mangler all form for validering 3. Session burde sikres. Digert tema, men jeg ville startet på http://php.net/manual/en/session.security.php, og så ville jeg lagt til en hash av $_SERVER['USER_AGENT'] som sjekkes mot samme verdi ved innlogging (ikke sikkert, men reduserer eventuell skade) 4. Din første isset() er vel ikke nødvendig. Session_start() må uansett kalles før du får tilgang til $_session. Ønsker du å se om session likevel har startet, så sjekk her: http://stackoverflow.com/a/18542272 5. Ser ikke redirect funksjonen din, men ønsker du å lagre meldinger mellom sider bruker du jo session. En 3xx redirect blir jo en ny kobling til nettsiden din, så da starter jo scriptet helt på nytt (og tidligere beskjeder i $notice forsvinner fra minnet) 6. Du bruker en mix av camelCase og underscore_case... Ikke akkurat krise, men det ser bedre ut om du følger en definert stil. F.eks. Zend sin 7. Logoutfunksjonen din burde inkludere noe ala session_destroy(); session_unset(); 8. Du burde kun bruke cookies i session. Endre php.ini eller ved runtime: ini_set('session.use_only_cookies',true) 9. Den lille og enkle klassen din er bra. Følger 'separation of concerns' http://en.wikipedia.org/wiki/Separation_of_concerns Endret 30. august 2014 av Lanes Lenke til kommentar
Yawa Skrevet 30. august 2014 Forfatter Del Skrevet 30. august 2014 (endret) 1. Hva er forskjellen på å skrive "$_POST['navn']", og heller la dette gå gjennom "extract();" slik at man bare trenger å skrive "$p_navn"? Utfallet er vel det samme? 2. Type validering tenker du på? Med tanke på at jeg benytter PDO og prepared statements, så er det ikke behov for ytterligere validering av inputsa - etter min forståelse.. EDIT: Jeg kom til å tenke på at jeg muligens missforstod hva du mente med validering. For en god tid tilbake lagde jeg denne funksjonen for å sjekke om brukernavnet hadde riktig format: function val_name ($n) { $n = trim($n); if (empty($n)) { $err = 'OBS!<br />Navne-feltet er tomt...'; return false; } elseif (strlen($n) < 3 || strlen($n) > 18) { $err = 'OBS<br />Navn må bestå av minst 3 bokstaver, og kan ikke overstige 18'; return false; } elseif (!preg_match('/^[A-Åa-å0-9 ]+$/', $n)) { $err = 'OBS!<br />Navnet ditt kan kun bestå av bokstaver og tall...'; return false; } else { return true; } } Denne kan jeg nok modifisere, og bygge inn i en klasse for enklere bruk. Noe i den dur du mente? Kan ikke huske/finne igjen noen passord sjekker i farta. 3. Tenker du på if(!isset($_SESSION)){...}? 4. Sjekker dette ut nærmere 5. redirect funksjonen fungerer helt utmerket: public static function redirect($to=http_referer){ // http_referer er en forhåndsdefinert konstant basert på $_SERVER['HTTP_REFERER'] if (!headers_sent()){ return exit(header('Location:'.$to)); } else { return exit(print_r('<script>window.location.replace("'.$to.'");</script>')); } } ... og dette er meldings funksjonen: # notice private static $notice = array(); public static function addNotice($type,$note,$code=''){ self::$notice[($code!=''?$code:uniqid())]=array('type'=>$type,'note'=>$note); $_SESSION['_SITE_notices'] = self::$notice; } // Meldingene lagres til session slik at de kan vises på siden man blir sendt til. Meldingene blir fjernet fra session slik - etter at de har blitt presentert: <?php unset($_SESSION['_SITE_notices']); ?> 6. Noe å tenke på 7. Noe i denne retningen?: # execute logout script if (isset($p_submit__BackendLogout)){ KD::addNotice('success',$success_LoggedOut); $_SESSION['backend']->logout(); session_destroy(); session_unset(); KD::redirect(); } // 8. Kan du utdype litt hva du mener her? 9. Jupp. Det enkle er ofte det beste. Og den gjør det svært enkelt å jobbe med Mange takk for informativ tilbakemelding. Setter pris på det Endret 30. august 2014 av Yawa88 Lenke til kommentar
Lanes Skrevet 30. august 2014 Del Skrevet 30. august 2014 Kjapt svar på #1: det er 'bad practice'. Trenger egentlig ikke å være så farlig, men det øyeblikket scriptet ditt vokser, eller noen andre tar over kan det åpne opp for feil. Du skal heller aldri bruke $_post variabler direkte, først kjøre de gjennom et saniteringsfilter/validering, deretter assigne til nye variable som et sikre. Dette er sikrere, mer oversiktelig og lettere å utvide senere. For eksempel om du skulle ta imot variabler fra noe annet en $_post så slipper du da å gå gjennom hele kodebasen din. Jeg benytter meg av en egen REQUEST klasse i all koden min som er uavhengig av hvor inputen kommer fra. Mer info her: http://stackoverflow.com/questions/829407/what-is-so-wrong-with-extract Lenke til kommentar
Lanes Skrevet 30. august 2014 Del Skrevet 30. august 2014 Blir litt etter litt dette her. #8: for en bruker som har skrudd av cookies vil session id legges til som en $_get variabel og kan sådan sees med det blotte øye + eksponeres av brukere som ikke vet hva de gjør når de skal lime inn en link til siden din. Lenke til kommentar
Yawa Skrevet 30. august 2014 Forfatter Del Skrevet 30. august 2014 Kjapt svar på #1: det er 'bad practice'. Trenger egentlig ikke å være så farlig, men det øyeblikket scriptet ditt vokser, eller noen andre tar over kan det åpne opp for feil. Du skal heller aldri bruke $_post variabler direkte, først kjøre de gjennom et saniteringsfilter/validering, deretter assigne til nye variable som et sikre. Dette er sikrere, mer oversiktelig og lettere å utvide senere. For eksempel om du skulle ta imot variabler fra noe annet en $_post så slipper du da å gå gjennom hele kodebasen din. Jeg benytter meg av en egen REQUEST klasse i all koden min som er uavhengig av hvor inputen kommer fra. Mer info her: http://stackoverflow.com/questions/829407/what-is-so-wrong-with-extract Jeg gjorde det før, når jeg brukte mysql, og ikke PDO. Da gjorde jeg noe slikt som dette - før jeg sendte dataen til databasen: $var1 = mysql_real_escape_string(); $var2 = mysql_real_escape_string(); ... Men etter jeg gikk over til PDO har jeg fått intrykk av at dette ikke lenger var nødvendig.. Men skal se nærmere på det der. Det er naturligvis viktig å sikre da man aldri vet hva som legges inn i slike situasjoner... Lenke til kommentar
Lanes Skrevet 30. august 2014 Del Skrevet 30. august 2014 PDO beskytter mot 1st order sql injection, men ikke noe mer. Uten verifisering at data faktisk er korrekt er det fremdeles fullt mulig å få tak i brukerdat, gjøre xss angrep og whatnot. mysql_real_escape_string() er for så vidt ikke i nærheten nok til å beskytte seg (og er ikke lenger en del av PHP >5.5). Det finnes mengder av informasjon på dette emnet på nettet, foreslår du søker litt. Lenke til kommentar
Anbefalte innlegg
Opprett en konto eller logg inn for å kommentere
Du må være et medlem for å kunne skrive en kommentar
Opprett konto
Det er enkelt å melde seg inn for å starte en ny konto!
Start en kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå