inlog systeem veilig met $_SESSIONS?

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Davey Mat

Davey Mat

25/04/2014 11:03:37
Quote Anchor link
Hallo allemaal,

Ik ben een systeem aan het maken voor een bedrijf waarbij ik zoveel mogelijk wil voorkomen dat hackers het systeem in kunnen komen. Ik weet dat dit onmogelijk is als iemand het echt op je gemunt heeft, maar alle beetjes helpen denk ik dan maar.

Ik ben van plan om op de volgende manieren te werk te gaan:
1. gebruiker logt in waarbij bcrypt hashes van het ingevoerde wachtwoord en de hash die in de db staat worden vergeleken.
2. als de hashes kloppen worden er session variabelen aangemaakt met gegevens van de gebruiker. Zie:

[script]
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
//controleer wachtwoord
        $password = $database->prepare('SELECT wachtwoord, gebruikers_id, email_adres FROM gebruikers WHERE gebruikers_naam = :gebruikers_naam');
        $password->execute(array('gebruikers_naam' => $_POST['username']));
        $hash = $password->fetch();

        if (password_verify($_POST['password'], $hash['wachtwoord'])) {
            $_SESSION['gebruikers_naam'] = $_POST['username'];
            $_SESSION['gebruikers_id'] = $hash['gebruikers_id'];
            $_SESSION['email_adres'] = $hash['email_adres'];
            $_SESSION['rememberMe'] = 1;
            setcookie('Remember',$_SESSION['rememberMe']);
        }
else {
            $err[]='Verkeerde gebruikersnaam en/of wachtwoord!';
        }

?>

[/script]

3. om te controleren of een gebruiker is ingelogd voordat de pagina wordt getoond gebruik ik:

[script]
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
if(!isset($_SESSION['gebruikers_id''])){
echo "<p>U heeft geen toegang</p>";
exit;
}

?>

[/script]

Naast deze 3 punten wil ik de user agent opslaan in een sessie variabele als de gebruiker inlogt. Op elke volgende pagina controleer ik of de user agent hetzelfde is gebleven anders vernietig ik de sessie.

Denken jullie dat ik zo goed op weg ben, of ben ik hier verkeerd bezig en/of hebben jullie nog tips?

Met vriendelijke groet,

Davey Mat
 
PHP hulp

PHP hulp

04/08/2021 12:29:52
 
Michael -

Michael -

25/04/2014 11:11:52
Quote Anchor link
Waarom zou je zoveel willen opslaan in SESSIONS? Onthoud dat SESSION eigenlijk gewoon COOKIES zijn (maar dan server side) maar evengoed uit te lezen door de gebruiker.
Dankzij jouw zou je op een slimme manier dus al een naam, id en emailadres in handen kunnen krijgen.
Haal deze gewoon uit de database wanneer je ze nodig hebt!

Je zou een hash kunnen maken van de useragent en deze controleren. De useragent is vrij uniek, maar geef deze dan niet gewoon weg, maar gebruik hiervoor bijvoorbeeld ook bcrypt.
Sla deze useragent op in een aparte tabel en ruim deze op na een bepaalde tijd inactief.

Daarnaast zou je nog bij elke pagina die je opent een unieke string kunnen opslaan in je database en session en deze met elkaar vergelijken. Als iemand de string in handen krijgt is deze maar beperkt geldig.
 
Ozzie PHP

Ozzie PHP

25/04/2014 12:53:48
Quote Anchor link
@Michael:

>> Onthoud dat SESSION eigenlijk gewoon COOKIES zijn (maar dan server side) maar evengoed uit te lezen door de gebruiker

Wat bedoel je hiermee? Het lijkt nu alsof je zegt dat een gebruiker het sessiebestand kan inzien en dat is natuurlijk niet zo.
 
Ward van der Put
Moderator

Ward van der Put

25/04/2014 13:10:26
Quote Anchor link
Michael - op 25/04/2014 11:11:52:
Waarom zou je zoveel willen opslaan in SESSIONS? Onthoud dat SESSION eigenlijk gewoon COOKIES zijn (maar dan server side) maar evengoed uit te lezen door de gebruiker.
Dankzij jouw zou je op een slimme manier dus al een naam, id en emailadres in handen kunnen krijgen.
Haal deze gewoon uit de database wanneer je ze nodig hebt!

Een $_SESSION['email_adres'] wordt gewoon op de server opgeslagen hoor. Die ziet de client dus niet. Het enige dat de client ziet, is de hash van de sessie-ID.

Wat wél mogelijk is, is bijvoorbeeld session hijacking: wie de cookie met de sessie-ID in handen krijgt, kan de sessie kapen.

Wat je daarom om te beginnen moet doen, is HTTPS met SSL gebruiken. Dat maakt het aftappen van het HTTP-verkeer bijna onmogelijk, waardoor ook de cookie met de sessie-ID niet zo makkelijk in verkeerde handen kan vallen.

Wat je daarnaast kunt doen, is het raden van een geldige sessie-ID moeilijker maken. Standaard is het namelijk slechts een MD5-hash. Daarvan kun je om te beginnen een langere en dus sterkere SHA-1-hash maken:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
// MD5 (128 bits) vervangen door SHA-1 (160 bits)
ini_set('session.hash_function', '1');
// PHP-sessie starten of hervatten
session_start();
?>


Wil je er voor eens en altijd van af zijn, dan doe je dat in php.ini:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
; Select a hash function for use in generating session ids.
; Possible Values
; 0 (MD5 128 bits)
; 1 (SHA-1 160 bits)
; This option may also be set to the name of any hash function supported
; by the hash extension. A list of available hashes is returned by the
; hash_algos() function.
; http://php.net/session.hash-function
session.hash_function = 1


In PHP >= 5.3.0 kun je er nog een schepje bovenop gooien door MD5 of SHA-1 te vervangen door een variant van SHA-2, bijvoorbeeld met een 512-bits hash:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
// MD5 (128 bits) vervangen door SHA-2 (512 bits)
ini_set('session.hash_function', 'sha512');
// PHP-sessie starten of hervatten
session_start();
?>
 
Michael -

Michael -

25/04/2014 13:23:32
Quote Anchor link
@Ozzie, Ik bedoelde session haijacking. Je moet er niet vanuit gaan dat als je je gegevens in een sessie opslaat, dat deze dan veilig zijn. Mijn mening is dat je niet alles uit een database moet lezen en dan in een session opslaan. Haal het gewoon uit de database als je het nodig hebt.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
<?php
$_SESSION
['gebruikersnaam'] = 'Ozzie';
$_SESSION['password'] = '12345';
?>


Mooie uitleg weer Ward :)
Gewijzigd op 25/04/2014 13:23:55 door Michael -
 
Davey Mat

Davey Mat

25/04/2014 13:24:45
Quote Anchor link
Bedankt voor de reacties!
Ik heb even snel iets gemaakt:

functions.php:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
//sla user agent encrypted op in db
//plaats deze functie nadat $_SESSION['gebruikers_id'] is aangemaakt

function save_ua() {
    $encrypted_ua = password_hash($_SERVER['HTTP_USER_AGENT'], PASSWORD_BCRYPT);
    $save_ua = $database->prepare('UPDATE gebruikers SET user_agent=:user_agent WHERE gebruikers_id = :gebruikers_id');
        $save_ua->execute(array('user_agent' => $encrypted_ua, 'gebruikers_id' => $_SESSION['gebruikers_id']));
    
}

//controleer of user agent zelfde is als in db, anders sessie destroyen
function check_ua() {
    $encrypted_ua = password_hash($_SERVER['HTTP_USER_AGENT'], PASSWORD_BCRYPT);
    $check_ua = $database->prepare('SELECT user_agent FROM gebruikers WHERE gebruikers_id = :gebruikers_id');
        $check_ua->execute(array('gebruikers_id' => $_SESSION['gebruikers_id']));    
    $ua = $check_ua->fetch();

        if (!(password_verify($encrypted_ua, $ua['user_agent']))) {
        $_SESSION = array();
        destroy_session();
            header('Location: index.php');
        exit;
        }
}

//genereer sessie id en plaats deze in sessie variabele + database
//plaats deze functie nadat $_SESSION['gebruikers_id'] is aangemaakt

function set_session_id() {
    $session_id = bin2hex(openssl_random_pseudo_bytes(16));
    $set_session_id = $database->prepare('UPDATE gebruikers SET session_id=:session_id WHERE gebruikers_id = :gebruikers_id');
        $set_session_id->execute(array('session_id' => $session_id, 'gebruikers_id' => $_SESSION['gebruikers_id']));
    $_SESSION['session_id']=$session_id;
}

//controleer of sessie id variabele en databse sessie id overeenkomen en maak daarna een nieuwe aan
function check_session_id() {
    $session_id=$_SESSION['session_id'];
    $check_session_id = $database->prepare('SELECT session_id FROM gebruikers WHERE gebruikers_id = :gebruikers_id');
        $check_session_id->execute(array('gebruikers_id' => $_SESSION['gebruikers_id']));    
    $session_id_db = $check_session_id->fetch();

        if ($session_id!=$session_id_db['session_id']) {
        $_SESSION = array();
        destroy_session();
            header('Location: index.php');
        exit;
        }
else {
        $session_id = bin2hex(openssl_random_pseudo_bytes(16));
        $set_session_id = $database->prepare('UPDATE gebruikers SET session_id=:session_id WHERE gebruikers_id = :gebruikers_id');
            $set_session_id->execute(array('session_id' => $session_id, 'gebruikers_id' => $_SESSION['gebruikers_id']));
        $_SESSION['session_id']=$session_id;
    }
}

?>


De set functies roep ik aan nadat de gebruiker succesvol inlogt. De check functies zet ik boven iedere pagina waar een gebruiker daarna naar toe kan.
Daarnaast heb ik als de gebruiker uitlogt:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['logout'])) {
    $save_ua = $database->prepare('UPDATE gebruikers SET user_agent=NULL, session_id=null WHERE gebruikers_id = :gebruikers_id');
    $save_ua->execute(array('gebruikers_id' => $_SESSION['gebruikers_id']));
    $_SESSION = array();
    session_destroy();
    header("Location: index.php");
    exit;
}

?>


Denken jullie dat ik deze code zo goed heb gemaakt en dat dit op deze manier zinvol is?

---TOEVOEGING:
Ik heb overigens de tip van Ward gebruikt door dit bovenaan de inlog pagina te zetten:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
session_name('Login');
// Cookie voor 2 weken
session_set_cookie_params(2*7*24*60*60);
// MD5 (128 bits) vervangen door SHA-2 (512 bits)
ini_set('session.hash_function', 'sha512');
session_start();
Gewijzigd op 25/04/2014 13:35:02 door Davey Mat
 
Ozzie PHP

Ozzie PHP

25/04/2014 13:28:13
Quote Anchor link
@Michael

Tja... ik weet niet hoor, maar als je iemand password in een sessie gaat zetten, ben je niet helemaal slim bezig lijkt mij. Een naam e.d. kun je gewoon in sessie zetten. Waarom zou je die iedere keer opnieuw gaan ophalen?

Maareh... hoe weet jij eigenlijk mijn wachtwoord?! ;)
 
Davey Mat

Davey Mat

25/04/2014 13:29:06
Quote Anchor link
Toevoeging 2:
Daarnaast begrijp ik Micheal- dat sessie variabelen niet als veilig moeten worden beschouwd, dus zal ik enkel de gebruikers_id uit de database in een sessie variabele opslaan. De rest vraag ik iedere keer met een query op.
 
Michael -

Michael -

25/04/2014 13:52:23
Quote Anchor link
@Ozzie, Een wachtwoord opslaan in een sessie is niet slim, dat weet jij en ik. Een naam zou nog kunnen, een gebruikersnaam/emailadres vind ik weer van niet. Als deze zijn te achterhalen hoef je alleen nog het wachtwoord te raden (die lang niet allemaal even moeilijk zijn).
Ik durf ook te wedden dat wel eens het gebruikers id wordt gebruikt als inlog controle (Davey?).
Ik vind dat je met sessies het zelfde om moet gaan als cookies (betreft veiligheid).
Sorry dat ik je wachtwoord hier poste :P
 
Ozzie PHP

Ozzie PHP

25/04/2014 13:56:56
Quote Anchor link
>> Sorry dat ik je wachtwoord hier poste :P

Ja, niet meer doen hoor! Nu moet ik overal al m'n wachtwoorden aanpassen. Ik heb besloten om nu meer tekens te gebruiken. Eerst waren het er 6, nu doe ik er voortaan 10. Het zijn allemaal 4'en, maar uiteraard ben ik niet zo dom om te vertellen in welke volgorde ze staan!
 
Michael -

Michael -

25/04/2014 13:59:39
Quote Anchor link
@Ozzie ha ha :) Flauw hoor (Nee ik hoef geen SALT).
 
Ward van der Put
Moderator

Ward van der Put

25/04/2014 14:03:57
Quote Anchor link
Afgaan op $_SERVER['HTTP_USER_AGENT'] helpt je niet bij phishing en dergelijke. De hacker/cracker kan de gebruiker naar een fake site lokken (bijvoorbeeld in een onzichtbaar frame) of de gebruiker een mailtje laten lezen via webmail en heeft dan direct deze HTTP-header te pakken.

Verder heeft encryptie van de user agent in de database niet veel zin, want het is slechts een extern gegeven waarvan je alleen maar wilt weten of het gewijzigd is. Daarvoor voldoet een asymmetrische hash.

Je kunt hier zowel strengere als sneller code gebruiken, bijvoorbeeld inclusief een IP-adres:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?php
function save_ua()
{

    $hashed_ua = sha1($_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']);
    $stmt = $database->prepare('UPDATE gebruikers SET user_agent=:user_agent WHERE gebruikers_id = :gebruikers_id');
    $stmt->execute(array('user_agent' => $hashed_ua, 'gebruikers_id' => $_SESSION['gebruikers_id']));
}

?>


Davey Mat op 25/04/2014 13:29:06:
Daarnaast begrijp ik Micheal- dat sessie variabelen niet als veilig moeten worden beschouwd, dus zal ik enkel de gebruikers_id uit de database in een sessie variabele opslaan. De rest vraag ik iedere keer met een query op.


Nee, dat is zonde. De sessievariabelen zijn veilig opgeslagen in (meestal) een file cache in een server-directory buiten de root, waar helemaal niemand bij kan. Je verliest een van de voordelen van sessies als je dat vervangt door databaseverkeer. Je hebt dan bijvoorbeeld meer databaseverbindingen nodig en je hebt ze langer nodig.

Wat ik wel nog mis, is het loggen van fouten en het blokkeren van een account of een IP-adres. Je vernietigt nu de sessie, maar na de redirect kan iemand gewoon een nieuwe poging wagen. Miljoenen keren zelfs...
 
Ozzie PHP

Ozzie PHP

25/04/2014 14:38:04
Quote Anchor link
>> Je kunt hier zowel strengere als sneller code gebruiken, bijvoorbeeld inclusief een IP-adres:

Ip-adres kan tijdens een sessie veranderen. Lijkt me geen goed plan.
 
Ward van der Put
Moderator

Ward van der Put

25/04/2014 14:42:57
Quote Anchor link
User agent kan tijdens een sessie ook veranderen...

Bovendien hoef je de sessie bij een gewijzigde user agent of een ander IP-adres natuurlijk niet te vernietigen. Wel kun je constateren dat er ondertussen iets bij de client structureel anders is. Dat kán een reden zijn om ergens extra controles in te bouwen.

Bijvoorbeeld: normaliter mag je je accountgegevens na inloggen wijzigen, maar nu staat er een vlaggetje op rood en moet je ter bevestiging nog even opnieuw je wachtwoord invullen.

Er is meer tussen hemel en aarde dan true/false.
 
Frank Nietbelangrijk

Frank Nietbelangrijk

25/04/2014 15:14:13
Quote Anchor link
Ward van der Put op 25/04/2014 14:03:57:
Wat ik wel nog mis, is het loggen van fouten en het blokkeren van een account of een IP-adres. Je vernietigt nu de sessie, maar na de redirect kan iemand gewoon een nieuwe poging wagen. Miljoenen keren zelfs...


Precies en deze is echt heel belangrijk want dit is de manier op een password te raden.
dus na laten we zeggen een keer of 20 mislukte login direct het ip blokkeren.

Vervolgens denk ik altijd maar aan het afsluiten van je woonhuis:
De achterdeur draai je drie keer in het slot en als je thuis komt is je hele huis overhoop gehaald: blijkt het slaapkamerraam nog open te staan..

Dus:
- goede sterke wachtwoorden gebruiken voor de login maar ook voor de database en de ftp server.
- zo weinig mogelijk informatie prijsgeven bij foutieve inlogpogingen. enkel een melding 'Geen toegang' is wat mij betreft genoeg.
- Zoals al gezegd: gebruik een beveiligde internet-verbinding.
- Sla de wachtwoorden gecodeerd op in de database.
- Desgewenst log je de inlogpogingen. Indien er vele pogingen in korte tijd bijkomen kun je nog een email laten versturen naar de beheerder. Deze kan vervolgens beslissen of de site misschien tijdelijk offline moet.
- Overweeg het gebruik waar filters. Bijvoorbeeld: inloggen enkel vanuit nederland of enkel bepaalde ip-adressen
 
Ozzie PHP

Ozzie PHP

27/04/2014 19:31:21
Quote Anchor link
@Ward:

>> User agent kan tijdens een sessie ook veranderen...

Hoe kan een user agent tijdens een sessie veranderen?
 
Davey Mat

Davey Mat

28/04/2014 00:56:58
Quote Anchor link
Bedankt allemaal voor de reacties! Hier kan ik zeker weer mee aan de slag.
Dit zal ik deze week ook allemaal gaan doen. Op het lijstje:
-user agent + ip adres controle waarna de gebruiker bij verandering nog wel content kan zien, maar niet kan aanpassen, verwijderen etc.
-loggen van fouten en ip adressen die aanmelden en/of brute force proberen waarna ik na bv 10-20x foutief inloggen binnen een uur het ip adres blokkeer(blokkeren is gewoon te vertrouwen als ik in php hiermee werk? Dus een database tabel met geblokkeerde ipadressen en dan elke keer als iemand een pagina opent kijken of dit van een geblokkeerd ip adres komt, dan een fout weergeven. Of is er een betere manier?)
-mail naar systeembeheer bij veel aanmeldingen op 1 gebruiker o.i.d. binnen een tijdsbestek van 1 uur.
-inloggen enkel via de ip reeksen van landen waar gebruikers vandaan komen (Nl/BE)

@Ozzie: Ik denk dat Ward bijvoorbeeld bedoelt dat als iemand de sessie van een gebruiker kan "stelen". De hacker kan daarna met deze sessie verbinding maken met de website, maar als hij/zij een andere user agent heeft dan merken we dat dus op en blokkeren we bepaalde functies. Daarnaast kan iedereen zijn user agent veranderen. Zie bijvoorbeeld: http://www.howtogeek.com/113439/how-to-change-your-browsers-user-agent-without-installing-any-extensions/
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.