Toon posts:

Challenge / Response login met Salt

Pagina: 1
Acties:

  • monnick
  • Registratie: december 2005
  • Niet online
Ik ben bezig met het maken van een - hopelijk veilig - login systeem dat gebruik maakt van challenge / response. Ik doe dit met PHP en Javascript. Ik heb nog nooit eerder gewerkt met challenge / response technieken dus vergeef me als ik domme dingen zeg.

Wat ik eerst doe is een random string van 50 characters genereren die dient als de challenge. Deze challenge staat in het inlogformulier in een hidden field. Dit is bijvoorbeeld: lvylgM87Un2f68bTyiQc8eifexJObjmkerT6xftjM5zp0x0dZv.
De challenge is opgeslagen in een database en is gekoppeld aan het session_id van de gebruiker. Verder is de challenge voor 5 minuten geldig, daarna komt hij te vervallen.

Als de gebruiker zijn of haar gebruikersnaam en wachtwoord heeft ingevoerd en op submit klikt wordt er een Javascript functie aangeroepen. Deze functie maakt de response voor de server. De functie ziet er als volgt uit:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
function doChallengeResponse() 
{
    var username =      document.getElementById('username').value;
    var password =      sha1(document.getElementById('password').value);
    var challenge =     document.getElementById('challenge').value;
    
    // Empty the password field to prevent it from being send
    document.getElementById('password').value =     '';
    document.getElementById('challenge').value =    '';
    
    // Create the response string
    document.getElementById('response').value = sha1(username + ':' + password + ':' + challenge);
}


De respons voor de server is dus in de vorm: sha1(username:sha1(password):challenge), bijvoorbeeld: monnick:42e3d96910f922bdd7dd1805fe3f8aff634f391d:lvylgM87Un2f68bTyiQc8eifexJObjmkerT6xftj... maar dan met sha1 gehashed.

Nu zou ik nog graag een salt implementeren, ik weet alleen niet goed hoe ik dit veilig kan doen. Als ik de Javascript functie het wachtwoord met de salt wil laten hashen, dan moet de salt dus ook opgeslagen worden in bijvoorbeeld een hidden field. Alleen in dit geval is de salt dus openbaar, en dat lijkt me niet echt veilig. Misschien dat Ajax hier nog wat voor me kan doen.
Hebben jullie ideeën, zie ik iets over het hoofd of is het gewoon een domme vraag? :P

Als jullie commentaar hebben op de bovenstaande challenge / response methode hoor ik dat ook graag. Ik hoop het echt een beetje veilig te maken namelijk :P

  • RobIII
  • Registratie: december 2001
  • Laatst online: 11:21

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij


  • Woy
  • Registratie: april 2000
  • Niet online

Woy

Moderator Devschuur®
Je moet je vooral afvragen waarom je een salt wil hebben. Waarschijnlijk gewoon omdat dan het gebruik van een rainbow table uitgesloten word, en het ook niet mogelijk is om te zien of er 2 mensen hetzelfde wachtwoord hebben.

Dan is het dus eigenlijk helemaal niet interessant dat de salt gewoon publiekelijk bekend is ( Mits de salt complex genoeg is, en per user verschillend ).

Het belangrijkste commentaar is dat je op de server het wachtwoord niet meer plain text moet hebben, maar alleen in hash( password + salt ) vorm, en dat zul je dus inderdaad op de client ook uit moeten voeren.

[Voor 21% gewijzigd door Woy op 12-10-2010 22:23]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • joppybt
  • Registratie: december 2002
  • Laatst online: 14:28
Hoe ga je op de server controleren of het wachtwoord ook goed is?
Ga je op de server ook sha1(username + ':' + password + ':' + challenge) uitrekenen met de waarden uit de database/ sessie challenge?

Dan gok ik namelijk dat jij het wachtwoord plaintext in de database hebt staan en dat is een onveiligheid an sich. De links van RobIIl zijn daartoe zeer leerzaam.
edit:
Oops, dat zegt Woy ook al grotendeels eigenlijk

--
sha1(username + ':' + sha1(password) + ':' + challenge) is volgens mij niet veiliger dan sha1(username + ':' + password + ':' + challenge). Probeer niet het veiliger te maken door obscuurder te doen.
--
Tenslotte: als je in Internet Explorer zorgt dat het password veld en challenge veld in het formulier geen 'name' attribuut hebben (maar alleen een id) dan worden ze automatisch niet gesubmit en hoef je ze dus niet leeg te maken. Ik weet echter niet of dat volgens de officiële specificatie is of een IE-ding.

[Voor 3% gewijzigd door joppybt op 12-10-2010 23:23]


  • RobIII
  • Registratie: december 2001
  • Laatst online: 11:21

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

joppybt schreef op dinsdag 12 oktober 2010 @ 23:22:
Tenslotte: als je in Internet Explorer zorgt dat het password veld en challenge veld in het formulier geen 'name' attribuut hebben (maar alleen een id) dan worden ze automatisch niet gesubmit en hoef je ze dus niet leeg te maken. Ik weet echter niet of dat volgens de officiële specificatie is of een IE-ding.
Beter maak je gewoon een tweede form met enkel hidden inputs (input type="hidden" dus) die je met JS submit op de onsubmit van de UI form ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij


  • monnick
  • Registratie: december 2005
  • Niet online
Bedankt voor je tip wat betreft het schaduw formulier. Ik heb hem momenteel gewoon gedaan door Javascript verschillende velden te laten invullen. Misschien dat ik het binnenkort nog eens verander naar zo'n tweede formulier.

De authenticatie werkt nu, maar nu moeten de sessies en cookies nog veilig gezet worden. Het idee is natuurlijk dat je standaard met sessies wordt ingelogd en dat wanneer je "remember me" aanvinkt er ook cookies worden gezet. Bovendien had ik het idee om alle gezette cookies ook nog bij te houden in de database als extra veiligheid.

Ik heb eigenlijk nooit eerder met cookies gewerkt, maar dit is wat ik had bedacht. Ik zet twee cookies: uid en token. In uid staat gewoon het unieke gebruikers ID. In token zet ik:

PHP:
1
$token = sha1($userIpAddress.':'.$userBrowser.':'.$dbSalt);


Ik vraag me alleen af of dit een geschikte keus is. Zelf heb ik er wel over nagedacht en ook een beetje gekeken hoe andere mensen op GoT dit doen. Wat vinden jullie van deze token keuze? De variable $dbSalt komt uit de database en is de unieke salt van de gebruiker. Deze wordt ook gebruikt bij het hashen van het wachtwoord.
In een MySQL tabel wordt ook voor elke cookie die gezet wordt bijgehouden:

code:
1
uid - token - ip adres - user agent - expire time


Op pagina's waarvoor een gebruiker ingelogd moet zijn kan van de gebruiker weer de token bepaald worden door met de $_COOKIE['uid'] de salt van de gebruiker op te halen uit de database.

Tips / feedback anyone? ;)

  • joppybt
  • Registratie: december 2002
  • Laatst online: 14:28
Je mechanisme moet in ieder geval niet afhankelijk zijn van ip-adres. Veel mensen hebben geen vast ip-adres.
Stel dat je via een laptop vanaf thuis bent ingelogd en een cookie hebt gekregen. De volgende dag ga je met je laptop naar je school/werk en gaat vanaf daar naar jou website. Dan moet je opnieuw inloggen?
In jou 'sessies' tabel in MySQL kun je best ip adres en user agent bijhouden maar dat is puur voor de sier/ statistieken.

Daarnaast: waarom leid je het token af van ip, browser en salt? Volgens mij kun je voor het token gewoon een geheel random getal/string nemen.
--- Later ---
Je moet ook inbouwen dat als iemand zijn wachtwoord wijzigt dat dan al zijn cookies niet meer geldig zijn.

[Voor 9% gewijzigd door joppybt op 17-10-2010 12:27. Reden: aanvulling]


  • monnick
  • Registratie: december 2005
  • Niet online
Bedankt voor je reactie. Ik had er inderdaad nog niet aan gedacht dat mensen hun laptop meenemen naar verschillende locaties of dat ip-adressen gewoon veranderen.

Ik ben inmiddels weer wat verder met de authenticatie methode. Als iemand inlogt worden er sowieso sessies aangemaakt. Een sessie $_SESSION[uid] met daarin het gebruikers ID en een sessie $_SESSION[token]. Ik had bedacht dat de session-token wel gegenereerd mag zijn aan de hand van de browser + ip-adres van de gebruiker aangezien de sessies toch weg is wanneer een gebruiker de browser sluit. De token is gegeven door:

PHP:
1
2
3
4
$userIpAddress =   $_SERVER['REMOTE_ADDR'];
$userBrowser =  $_SERVER['HTTP_USER_AGENT'];
$token =           sha1($userIpAddres.$userBrowser.$dbSalt); 
// $dbSalt is de unieke salt van de gebruiker


Desgewenst worden er ook cookies aangemaakt als de "remember me" optie is aangevinkt. Ook hier geldt weer dat er een cookie met het gebruikers ID en een cookie met een unieke token wordt aangemaakt. In dit geval is de token een random string van 40 tekens die wordt opgeslagen in de database met daarbij de expire time (timestamp).

Om te kijken of een gebruiker is ingelogd controleer ik eerst of er sessies gezet zijn. Ik genereer gewoon weer de sha1-hash van $userIpAddres.$userBrowser.$dbSalt en kijk of dat overeenkomt met de waarde van $_SESSION[token], zo ja: de gebruiker is ingelogd.
Als de sessies niet gezet zijn wordt er gecontroleerd of er cookies zijn gezet. Als de token uit de database overeenkomt met de token in het cookie is de gebruiker ook ingelogd.
Als een gebruiker is ingelogd omdat de cookies zijn gezet, is het dan handig / veiliger om sessies te zetten en die verder te gebruiken voor authorisatie totdat de browser wordt gesloten of kan ik steeds de cookies verifiëren?

Hier de functie die checkt of de gebruiker is ingelogd (doorlezen is niet echt relevant voor de vraag denk ik, maar feedback + tips zijn welkom):

PHP:
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
public function isLoggedIn ()
{
    if (isset($_SESSION['uid']) && isset($_SESSION['token'])) {
    
        $sesUid =           (int)$_SESSION['uid'];
        $sesToken =         $_SESSION['token'];
            
        $dbSalt =           $this->retrieveSaltById($sesUid);
        $validToken =       sha1($_SERVER['HTTP_USER_AGENT'];.$_SERVER['REMOTE_ADDR'].$dbSalt);
            
        if ($sesToken !== $validToken) {
            return false;
        } else {
            echo 'logged in using <strong>sessions</strong>.<br/><br/>';
            return true;
        }   
                    
    } else if (isset($_COOKIE['uid']) && isset($_COOKIE['token'])) {
        
        $cookieUid =    (int)$_COOKIE['uid'];
        $cookieToken =  $_COOKIE['token'];
            
        $userBrowser =  mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']);
            
        $selectQuery =  " SELECT `token`".
                        " FROM `cookie_data`".
                        " WHERE `user_id` = " . $cookieUid .
                        " AND `user_agent` = '" . $userBrowser . "'".
                        " AND `expire_time` > ". time().
                        " LIMIT 1";

        // paar regels weggelaten, hier wordt de query gewoon uitgevoerd.
            
        $dbToken = mysql_result($execQuery, 0);
            
        if ($cookieToken !== $dbToken) {
            return false;
        } else {
            echo 'logged in using <strong>cookies</strong>.<br/><br/>';
            return true;
        }
    }
}

  • joppybt
  • Registratie: december 2002
  • Laatst online: 14:28
Als de cookie verlopen is levert je query geen records op. Moet je daar niet op testen of mag je in PHP dan toch $dbToken = mysql_result($execQuery, 0); doen?
(in ASP/ADODB mag het niet weet ik)

Daarnaast kun je dan de query ook wel uitbreiden tot:
code:
1
2
3
4
5
6
7
$selectQuery =  " SELECT 1".
                " FROM `cookie_data`".
                " WHERE `user_id` = " . $cookieUid .
                " AND `user_agent` = '" . $userBrowser . "'".
                " AND token = '" . mysql_real_escape_string($cookieToken) . "'".
                " AND `expire_time` > ". time().
                " LIMIT 1";

zodat de tussen variabele dbToken niet nodig hebt.

Daarnaast is er nu nog wel het (toch redelijk hypothetische) probleem dat iemand draadloos werkt, merkt dat zijn ontvangst slecht is en toch maar een kabel inprikt, een ander ip-adres krijgt en plotseling merkt dat zijn sessie verlopen is.

  • Devil
  • Registratie: oktober 2001
  • Niet online

Devil

King of morons

Nu is je token dus altijd hetzelfde voor een gebruiker met een statisch ip.
Iemand die die token steelt kan dus altijd inloggen als die gebruiker. Het is veel veiliger als je token een maximale levensduur heeft en voor elke sessie uniek is.

Daarnaast is het nog veel veiliger als je niet zelf gaat zitten prutsen, maar gewoon een library van internet haalt ;)

After all, we are nothing more or less than what we choose to reveal.


  • monnick
  • Registratie: december 2005
  • Niet online
Ja, inderdaad nu is de session token elke keer dezelfde hash. De reden hiervoor is dat ik dacht dat sessions erg lastig te onderscheppen zijn. Maar oke, je hebt gelijk dat het veiliger is om elke sessie een andere token te gebruiken. Is het dan gebruikelijk om elke session ook op te slaan in de database met een verlooptijd?

En een library van internet plukken kan natuurlijk, het gaat mij er echter om om wat dingen van beveiliging te leren ;)

EDIT: Klopt het wel wat je zegt? Stel iemand steelt de token (geen idee hoe dat werkt) en zet voor zich zelf deze token in de variable $_SESSION[token]. Op de server wordt nu alsnog de hash gemaakt:

PHP:
1
$validToken = sha1($_SERVER['HTTP_USER_AGENT'];.$_SERVER['REMOTE_ADDR'].$dbSalt);


Waardoor de session hijacker alsnog niet is ingelogd omdat het IP-adres van hem anders is dan van degene van wie de token is gestolen. Of zie ik nu iets over het hoofd? :o

[Voor 35% gewijzigd door monnick op 19-10-2010 14:06]


  • 164019
  • Registratie: december 2005
  • Laatst online: 21-07-2014
Ja, bij dat laatste zie je iets over het hoofd ;)

Het is niet perfect veilig om de gebruiker data aan te leveren voor een token. Hoewel het bijna niet te doen is, zou de aanvaller z'n user agent-header dusdanig kunnen kiezen dat de hash weer overeenkomt met de hash van z'n slachtoffer.

Verder is het een minder geslaagd idee om sessies aan IP-adressen te koppelen, aangezien je niet weet in hoeverre het IP-adres van een gebruiker verandert tijdens diens sessie. Tenzij de gebruiker erom vraagt, maar in zo'n geval zou ik de gegevens ook server-side houden en niet in het token opnemen.

[Voor 4% gewijzigd door 164019 op 19-10-2010 14:22]


  • RobIII
  • Registratie: december 2001
  • Laatst online: 11:21

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

Hewoon een random string als token kiezen (en daar een levensduur aan hangen etc. etc.). Waarom moeilijk doen als 't makkelijk kan :?

[Voor 20% gewijzigd door RobIII op 19-10-2010 15:11]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij

Pagina: 1


Nintendo Switch (OLED model) Apple iPhone 13 LG G1 Google Pixel 6 Call of Duty: Vanguard Samsung Galaxy S21 5G Apple iPad Pro (2021) 11" Wi-Fi, 8GB ram Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2021 Hosting door True

Tweakers maakt gebruik van cookies

Bij het bezoeken van het forum plaatst Tweakers alleen functionele en analytische cookies voor optimalisatie en analyse om de website-ervaring te verbeteren. Op het forum worden geen trackingcookies geplaatst. Voor het bekijken van video's en grafieken van derden vragen we je toestemming, we gebruiken daarvoor externe tooling die mogelijk cookies kunnen plaatsen.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Forum cookie-instellingen

Bekijk de onderstaande instellingen en maak je keuze. Meer informatie vind je in ons cookiebeleid.

Functionele en analytische cookies

Deze cookies helpen de website zijn functies uit te voeren en zijn verplicht. Meer details

janee

    Cookies van derden

    Deze cookies kunnen geplaatst worden door derde partijen via ingesloten content en om de gebruikerservaring van de website te verbeteren. Meer details

    janee