[PHP] Probleem met inlogsysteem

Pagina: 1
Acties:
  • 230 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Ik heb een aantal dagen terug een inlogsysteem gebruikt voor een nieuw project van mij. Het script heb ik al voor meerdere sites gebruikt en ik snap ook precies hoe het werkt een waar vaak problemen in kunnen zitten. Nu zit ik echter al een aantal dagen met een hardnekkig probleem, en dat is dat het script de mist in gaat bij het vergelijken van de gegenereerde response met het wachtwoord uit de database en de post response.

Hier volgt het script (alles in een document) (sorry voor de lengte :$ ):
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<?php session_start();

$hostname = "localhost";
$database = "got";
$username = "gotuser";
$password = "gotpass";
@mysql_pconnect($hostname, $username, $password) or trigger_error(mysql_error(),E_USER_ERROR); 
@mysql_select_db($database);

if( isset( $_SESSION['currentip'] ) && $_SESSION['currentip'] != $_SERVER['REMOTE_ADDR'] )
{
  session_unset();
  session_destroy();
  die();
} 

function parsestring( $lengte )
{
  srand( ( (double) microtime() ) * 1000000 );
  $string = '';

  // De parser doet irri met zoveel tekens zonder spaties achter elkaar, dus verdeel ik dit ff over meerdere regels
  $tekens = 'abcdefghijklmnopqrstuvwxyz';
  $tekens .= 'ABCDEFGHIJKLMNOPQRSTUWXYZ';
  $tekens .= '01234567890123456789';

  for( $i = 0; $i < $lengte; $i++ )
  {
    $string .= $tekens{ rand( 0, ( strlen( $tekens ) - 1 ) ) };
  }

  return $string;
} 

if( isset( $_POST['username'], $_POST['response'], $_SESSION['challenge'] ) )
{
  $query = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' LIMIT 0,1";
  $result = mysql_query( $query ) or trigger_error( mysql_error() );
  if( mysql_num_rows( $result ) == 0 )
  {
    echo 'Username bestaat niet';
    $login_Failed_Error = true;
  }
  else
  {
    $fetch = mysql_fetch_assoc( $result );

    // ----> HIER gaat het fout <------------------------------------------------------------------------------------
    if( sha1( $fetch['password'] . ':' . $_SESSION['challenge'] ) != $_POST['response'] )
    {
      // script eindigt dus altijd hier.
      echo 'Wachtwoord is niet ok..';
    $login_Failed_Error = true;
    }
    else
    {
      echo 'Ingelogd';
      // etc..
    }
  }
}


if( isset( $_SESSION['currentip'] ) == false )
{
  $_SESSION['currentip'] = $_SERVER['REMOTE_ADDR'];
} 


$challenge = parsestring( 255 );
$_SESSION['challenge'] = $challenge; 

 ?>
<script type="text/javascript" src="./js/hash.js"></script>
<script type="text/javascript">
<!--
function createResponse()
{
  document.getElementById( 'response' ).value = hex_sha1( hex_sha1( document.getElementById( 'password' ).value ) + ":" + document.getElementById( 'challenge' ).value );
  document.getElementById( 'password' ).value = "";
  document.getElementById( 'challenge' ).value = "";
  return true;
}
//-->
</script> 
          <?php
          if (($_SESSION['loggedin'] != TRUE) AND (!isset($_GET['mode']))) {
          echo 'In order to use your account, you need to log in.<br />Enter your private username and password and submit this form.<hr />';
          if ($login_Failed_Error == TRUE) {
          echo '<div class="error"><img src="./img/embedded/icons/error.gif" alt="Error" align="top" /> It appears you could not be logged in. Please try again.<br />'.$login_Failed_Reason.'</div>';
          }
          ?>
          <form method="post" action="<?=$_SERVER['PHP_SELF'];?>" onsubmit="return createResponse();">
            <table width="100%" border="0" cellspacing="0" cellpadding="0">
            <tr>
              <td width="125" valign="top"><strong>Username</strong></td>
              <td><input name="username" type="text" class="logintxt" size="40" /></td>
            </tr>
            <tr>
              <td width="125" valign="top"><strong>Password</strong></td>
              <td><input type="password" class="logintxt" id="password" size="40" />
                  <input type="hidden" id="challenge" value="<?php echo $challenge; ?>">
    <input type="hidden" name="response" id="response" value=""></td>
            </tr>
            <tr>
              <td width="125" valign="top"><strong>Other</strong></td>
              <td><label>
                <input type="checkbox" name="usr_Public" value="true" />
                I am using a public computer</label>
                <br />
                <label>
                <input type="checkbox" name="usr_SetCookie" value="true" />
                Keep me logged in</label>
                <br /></td>
            </tr>
            <tr>
              <td valign="top">&nbsp;</td>
              <td><br />
                <input type="submit" value="Access" style="font-weight:bold;" /> <input type="button" name="button2" id="button2" value="Register account" onclick="location.href='<?php echo $_SERVER['PHP_SELF']; ?>?mode=register'" /></td>
            </tr>
          </table>
          </form>
          <?php } ?>


Daarnaast maakt het script, zoals je uit de code kan opmaken, gebruik van het Javascript "sha1.js" dat je hier kan vinden:

http://pajhome.org.uk/crypt/md5/sha1src.html

Even een uitleg van het script:

Bij het laden van de pagina wordt telkens een nieuwe, random string van 255 tekens gegenereerd, door de functie parsestring. Deze wordt in het hidden veld gezet en bij het submitten van het inlog formulier wordt het wachtwoord uit zijn veld gehaald, samen met deze random string (challenge) gehashed en als response in de postback gezet. Daarnaast wordt de challenge telkens opgeslagen in een sessie.
Zoals je ziet is dus het enige dat via $_POST verstuurd wordt, de response en gebruikersnaam.
Wanneer de postback gedaan is, wordt er eerst gecontrolleerd of de gebruiker nog steeds hetzelfde IP-adres gebruikt. Als dat zo is, vervolgt het script en maakt het verbinding met de database server en kiest hij de database. Daarna controlleert hij of de gebruikersnaam die is opgegeven wel bestaat dmv mysql_num_rows. Tot zo ver gaat alles goed.
Hierna moet er worden gecontrolleerd of de wachtwoorden uit de database en het opgegeven wachtwoord, overeenkomen.
Het wacthwoord wordt uit de database gehaald en samen met de eerder in een sessie opgeslagen challenge sha1 gehashed. Het resultaat is een response, en als het goed is, is dat precies hetzelfde als de response die je met de postback hebt verkregen en die is aangemaakt door de javascript functie. Hier gaat het dus fout en op een of andere manier, ondanks tientalle controles, actie per actie, gaat het hier fout bij mij en komen de responses telkens maar niet overeen. Ik snap echter totaal niet wáár....

Database structuur
In de database staat gewoon het wachtwoord sha1 gehashed in de column "password" en de gebruikersnaam onder "username".

Kan iemand mij hierbij helpen? Ik kom er even niet meer uit :| ...
Alvast bedankt!

[ Voor 1% gewijzigd door VR46 op 28-05-2007 19:48 . Reden: database feitjes vergeten ]


Acties:
  • 0 Henk 'm!

  • pietje63
  • Registratie: Juli 2001
  • Laatst online: 16:14

pietje63

RTFM

Het wacthwoord wordt uit de database gehaald en samen met de eerder in een sessie opgeslagen challenge sha1 gehashed. Het resultaat is een response, en als het goed is, is dat precies hetzelfde als de response die je met de postback hebt verkregen en die is aangemaakt door de javascript functie. Hier gaat het dus fout en op een of andere manier, ondanks tientalle controles, actie per actie, gaat het hier fout bij mij en komen de responses telkens maar niet overeen. Ik
Ik denk je dat uit moet gaan zoeken welke van de 2 fout is.

De grootste Nederlandstalige database met informatie over computers met zoekfunctie!!


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
pietje63 schreef op maandag 28 mei 2007 @ 18:22:
[...]

Ik denk je dat uit moet gaan zoeken welke van de 2 fout is.
Dat heb ik ook stapsgewijs gedaan:
- De door javascript en door php gehashede passwords zijn het zelfde (juist dus)
- De post en sessie-challenge zijn het zelfde (juist dus)
- Het enige dat fout gaat is het aanmaken van één van de twee responses, maar ik weet niet welke en waar de fout zit :?, vandaar dit topic dus. Als het zo simpel zat als jij het zegt had ik niks gepost 8)7

[ Voor 8% gewijzigd door VR46 op 28-05-2007 18:50 ]


Acties:
  • 0 Henk 'm!

  • dusty
  • Registratie: Mei 2000
  • Laatst online: 15-09 18:24

dusty

Celebrate Life!

Het wacthwoord wordt uit de database gehaald
:X

Ik zou persoonlijk zeggen, herschrijf dit (crappy) script zelf, zorg dat de database de wachtwoorden vergelijkt en jij NIET het wachtwoord uit de database haalt. een wachtwoord uit de database halen om te vergelijken hoort een rode vlag op te werpen, iets wat je gewoon weg niet hoort te doen.

Voor het debuggen kan je het best een paar echo's toevoegen voor de if's om te kijken of de if voldoet, en een echo binnen de if-end statement zodat je weet dat dat ook uitgevoerd wordt.

Back In Black!
"Je moet haar alleen aan de ketting leggen" - MueR


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
dusty schreef op maandag 28 mei 2007 @ 19:14:
[...]
:X

Ik zou persoonlijk zeggen, herschrijf dit (crappy) script zelf, zorg dat de database de wachtwoorden vergelijkt en jij NIET het wachtwoord uit de database haalt. een wachtwoord uit de database halen om te vergelijken hoort een rode vlag op te werpen, iets wat je gewoon weg niet hoort te doen.

Voor het debuggen kan je het best een paar echo's toevoegen voor de if's om te kijken of de if voldoet, en een echo binnen de if-end statement zodat je weet dat dat ook uitgevoerd wordt.
Dus als ik het goed begrijp bedoel je dat ik beter de wachtwoorden kan vergelijken doormiddel van een query? Is het dan nog echt noodzakellijk dat ik die challenge en response gebruik met gebruik van Javascript? Ik heb namelijk het gevoel dat het javascript-deel van het script de mist in gaat...
De if-statements werken allemaal naar behoren, ik heb al echo's toegevoegd om dat te kunnen checken. Het enige is dus het vergelijken van de wachtwoorden.
Kan je me misschien een voorbeeld laten zien van zo'n vergelijk-query?

Acties:
  • 0 Henk 'm!

  • dusty
  • Registratie: Mei 2000
  • Laatst online: 15-09 18:24

dusty

Celebrate Life!

Je zou een challenge/response kunnen gebruiken, je zou ook gewoon kunnen controleren of er een sessie is gezet voordat je post waardes accepteert. Persoonlijk hou ik een lijstje van IP's bij met mislukte inlogs, is dat IP meer dan 4 keer voorbij gekomen in de laatste xx-uur, dan kan dat IP gewoon niet voor een bepaalde tijd inloggen.

Als je op IP waardes controleert of het dezelfde IP is, kunnen gebruikers van sommige providers gewoon niet inloggen ( bijvoorbeeld AOL met speedbooster )

voorbeeldje van een query zou kunnen zijn:
code:
1
select userid from users where username=$username and wachtwood=password($password)

Geen resultaten betekent verkeerde username/wachtwoord.

Back In Black!
"Je moet haar alleen aan de ketting leggen" - MueR


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Er is alleen een probleem met dat vergelijken van de wachtwoorden in de database zelf: hoe kan ik dan nog gebruik maken van de challenge en response? En als ik daar niet gebruik van kan maken, kan ik slechts het wachtwoord vóór het verzenden van het formulier laten hashen met dat stukje javascript, waarvan ik betwijfel of het veilig genoeg is...

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
dusty schreef op maandag 28 mei 2007 @ 20:18:
Je zou een challenge/response kunnen gebruiken, je zou ook gewoon kunnen controleren of er een sessie is gezet voordat je post waardes accepteert. Persoonlijk hou ik een lijstje van IP's bij met mislukte inlogs, is dat IP meer dan 4 keer voorbij gekomen in de laatste xx-uur, dan kan dat IP gewoon niet voor een bepaalde tijd inloggen.

Als je op IP waardes controleert of het dezelfde IP is, kunnen gebruikers van sommige providers gewoon niet inloggen ( bijvoorbeeld AOL met speedbooster )

voorbeeldje van een query zou kunnen zijn:
code:
1
select userid from users where username=$username and wachtwood=password($password)

Geen resultaten betekent verkeerde username/wachtwoord.
Hoe wil je dat dan doen met een challenge response systeem waarbij de vergelijkstring altijd anders is? Zelfs gebruik ik een soortgelijk systeem maar daarbij ook een salt en SHA256 ipv SHA1. Het idee erachter is juist dat het wachtwoord dat je post altijd anders zal zijn. Alle data wordt serverside vergeleken, lijkt me niets mis mee. Kun je me es uitleggen waarom dit zo verkeerd is?

Overigens is het teruggeven van een 'gebruiker bestaat niet' nooit goed. Altijd alleen de melding gebruikernaam/wachtwoord combinatie komt niet overeen. Dan weet iemand ook niet of de gebruiker wel bestaat, je geeft anders teveel info weg.

Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Cartman! schreef op maandag 28 mei 2007 @ 20:44:
Overigens is het teruggeven van een 'gebruiker bestaat niet' nooit goed. Altijd alleen de melding gebruikernaam/wachtwoord combinatie komt niet overeen. Dan weet iemand ook niet of de gebruiker wel bestaat, je geeft anders teveel info weg.
Snap ik, dat heb ik ook puur voor dit topic gedaan ;)
Dat vergelijken in de database ziet er naar uit niet veel veiliger te zijn trouwens, aangezien je dat slechts het wachtwoord kan hashen, en volgens mij geen gebruik meer kan maken van challenge en response e.d...
Ik zal dat script gaan herschrijven met sha256 en kijken of ik de response op een andere manier kan aanmaken ofzo, maar suggesties e.d. zijn nog altijd meeer dan welkom :P Mn hoofd begint een beetje te roken inmiddels }:|

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
cbernardini schreef op maandag 28 mei 2007 @ 18:49:
[...]

Dat heb ik ook stapsgewijs gedaan:
- De door javascript en door php gehashede passwords zijn het zelfde (juist dus)
- De post en sessie-challenge zijn het zelfde (juist dus)
Toon eens wat je als parameter aan sha1 voert (dus de waarde van
hex_sha1( document.getElementById( 'password' ).value ) + ":" + document.getElementById( 'challenge' ).value
in javascript en de waarde van $fetch['password'] . ':' . $_SESSION['challenge'] in PHP)

{signature}


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
@Cartman!: Na even googlen merk ik dat sha256 implementatie nog niet echt optimaal is voor PHP, en dat er nog geen officiële functie voor bestaat.. Daar ga ik vooralsnog op dit moment niet aan beginnen.

Maarr... Goed nieuws:

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
44
45
if( isset( $_SESSION['huidig_ip'] ) && $_SESSION['huidig_ip'] != $_SERVER['REMOTE_ADDR'] )
{
  session_unset();
  session_destroy();
  die();
}

function parserandom($length)
{
    $pattern = "1234567890abcdefghijklmnopqrstuvwxyz";
    $key  = $pattern{rand(0,35)};
    for($i=1;$i<$length;$i++)
    {
        $key .= $pattern{rand(0,35)};
    }
    return $key;
}

if( isset( $_POST['username'], $_POST['response'], $_SESSION['challenge'] ) )
{
    $query = "SELECT * FROM users WHERE username = '".htmlentities($_POST['username'],ENT_QUOTES)."' LIMIT 0,1";
    $result = @mysql_query($query);
    $fetch = @mysql_fetch_assoc($result);
    if (@mysql_num_rows($result) == 1) {
        if ( sha1($fetch['password'].$_SESSION['challenge']) == $_POST['response']) {
        $_SESSION['loggedin'] = true;
        $_SESSION['userdata'] = array($fetch['id'],$fetch['username']);
        } else {
        $login_Failed_Error = true;
        $login_Failed_Reason = "password";
        }
    } else {
        $login_Failed_Error = true;
        $login_Failed_Reason = "username";
    }
if ($result) { @mysql_free_result($result); }
}

if( !isset( $_SESSION['huidig_ip'] ) )
{
    $_SESSION['huidig_ip'] = $_SERVER['REMOTE_ADDR'];   
}

$challenge = @parserandom(255);
$_SESSION['challenge'] = $challenge;


Ik heb het script herschreven en hier en daar niet echt ontzettend significante aanpassingen toegepast, maar wonder boven wonder blijkt het nu ineens te werken...
Lijkt het jullie zo, zoals ik het nu heb, een degelijk en veilig inlogscript?

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
cbernardini schreef op maandag 28 mei 2007 @ 20:54:
[...]

Snap ik, dat heb ik ook puur voor dit topic gedaan ;)
Dat vergelijken in de database ziet er naar uit niet veel veiliger te zijn trouwens, aangezien je dat slechts het wachtwoord kan hashen, en volgens mij geen gebruik meer kan maken van challenge en response e.d...
Ik zal dat script gaan herschrijven met sha256 en kijken of ik de response op een andere manier kan aanmaken ofzo, maar suggesties e.d. zijn nog altijd meeer dan welkom :P Mn hoofd begint een beetje te roken inmiddels }:|
het SHA1 of SHA256 gebeuren zou ik niet teveel waarde aan hechten. Je string wordt gewoon een stuk langer. Groot voordeel voor rainbowtables ieder geval. En je hebt alleen wat aan een rainbowtable als je de database hebt weten te verkrijgen en je wilt inloggen op die specifieke site. Het originele wachtwoord zul je er (hoogstwaarschijnlijk) nooit mee terug krijgen.

Ik sla mn wachtwoorden op in de database samen met een salt ( sha256(ww+salt) ) en op de login pagina zet ik de challenge (een sha256 hash van timestamp + random getal) en de salt in een hidden fields. Onsubmit hash ik : sha256((sha256(ww+salt) + challenge) en dat verstuur ik samen met de username + evt. overige opties (ip lock, time lock). Serverside controleer ik de username, als deze gevonden is vergelijk ik sha256(database ww+challenge) met de post var. Als dit overeenkomt ben je ingelogd. Eenmaal ingelogd wordt er een cookie gezet met een zelf gegenereerd session id. Deze wordt in de database opgeslagen en daarmee wordt gecheckt of je ingelogd bent etc. (net zoals op GoT/Tnet zegmaar).

edit:
cbernardini:
ik heb een eigen hash class gemaakt die gebruik maakt van de hash funtie in PHP of de mhash extensie voor PHP. Als ze beiden niet beschikbaar zijn schakelt t systeem zelf terug naar SHA1. Het geinige eraan is dat je ook, zou je t willen, gebruik kunt maken van andere hash technieken zoals RIPEMD of WHIRLPOOL.

[ Voor 9% gewijzigd door Cartman! op 28-05-2007 21:24 ]


Acties:
  • 0 Henk 'm!

  • pietje63
  • Registratie: Juli 2001
  • Laatst online: 16:14

pietje63

RTFM

dusty schreef op maandag 28 mei 2007 @ 19:14:
[...]
:X

Ik zou persoonlijk zeggen, herschrijf dit (crappy) script zelf, zorg dat de database de wachtwoorden vergelijkt en jij NIET het wachtwoord uit de database haalt. een wachtwoord uit de database halen om te vergelijken hoort een rode vlag op te werpen, iets wat je gewoon weg niet hoort te doen.

Voor het debuggen kan je het best een paar echo's toevoegen voor de if's om te kijken of de if voldoet, en een echo binnen de if-end statement zodat je weet dat dat ook uitgevoerd wordt.
Beetje offtopic maar ik haal juist altijd bewust wel de wachtwoorden uit de database. Eerst een select voor de username, en dan de password checken. De reden hiervoor is dat een standaard select van MySQL niet case sensitive is.

De grootste Nederlandstalige database met informatie over computers met zoekfunctie!!


Acties:
  • 0 Henk 'm!

Verwijderd

pietje63 schreef op dinsdag 29 mei 2007 @ 00:58:
[...]

Beetje offtopic maar ik haal juist altijd bewust wel de wachtwoorden uit de database. Eerst een select voor de username, en dan de password checken. De reden hiervoor is dat een standaard select van MySQL niet case sensitive is.
Je wil toch niet zeggen dat je het wachtwoord in plaintext in de database aan het vergelijken bent met een POST waarde? :X

Veel makkelijk en nog steeds betrekkelijk veilig (het systeem is toch maar zo veilig als de gebruiker, dus afhankelijk van je context kan het al heel snel veilig genoeg zijn) is om een vaste random salt per gebruiker te gebruiken en deze ook op te slaan. Eventueel kun je regelmatig deze salt verversen, bijvoorbeeld wanneer de gebruiker z'n wachtwoord wijzigt, dit zou je dan ook weer af kunnen dwingen om eens in de zoveel tijd te moeten doen.

Je kan dan ook de response al opslaan in de database welke je ook gewoon zonder security problemen eruit kunt halen en in PHP oid kunt vergelijken.

Maar enig puntje is dat je een vrij krachtige encryptiemethode moet gebruiken want met rainbowtables en een bekende salt is het wachtwoord relatief snel te achterhalen.

[ Voor 14% gewijzigd door Verwijderd op 29-05-2007 01:22 ]


Acties:
  • 0 Henk 'm!

  • pietje63
  • Registratie: Juli 2001
  • Laatst online: 16:14

pietje63

RTFM

offtopic:
Nee, een MD5 hash. Is ook niet alles, I know

De grootste Nederlandstalige database met informatie over computers met zoekfunctie!!


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
cbernardini schreef op maandag 28 mei 2007 @ 21:17:
PHP:
1
2
    $query = "SELECT * FROM users WHERE username = '".
htmlentities($_POST['username'],ENT_QUOTES)."' LIMIT 0,1";
htmlentities() is een functie die je pas nodig hebt tijdens het presenteren. mysql_real_escape_straing() is de functie die je nodig hebt om sql injection te voorkomen.
Ik heb het script herschreven en hier en daar niet echt ontzettend significante aanpassingen toegepast, maar wonder boven wonder blijkt het nu ineens te werken...
Ik kan het nooit hebben als ik niet weet wat de fout was. :P Weet je echt niet waar het hem in zat?
pietje63 schreef op dinsdag 29 mei 2007 @ 00:58:
[...]
Beetje offtopic maar ik haal juist altijd bewust wel de wachtwoorden uit de database. Eerst een select voor de username, en dan de password checken. De reden hiervoor is dat een standaard select van MySQL niet case sensitive is.
Dan zorg je er dmv een keyword voor dat het wel goed gaat?
Bijvoorbeeld SELECT BINARY 'a' = 'A'; geeft 0 terug.
Overigens maakt het bij heel veel implementaties ook niet uit, puur omdat je toch al weet dat bijvoorbeeldede gebruikte encryptiefunctie het resultaat in hex teruggeeft met altijd dezelfde case voor A-F, en dat de input voor de hash/encryptiefunctie al case sensitive is.
Verwijderd schreef op dinsdag 29 mei 2007 @ 01:20:
...bijvoorbeeld wanneer de gebruiker z'n wachtwoord wijzigt, dit zou je dan ook weer af kunnen dwingen om eens in de zoveel tijd te moeten doen.
Dat is nog wel een discussie waard of dat veiliger is, genoeg mensen zeggen van niet, omdat bv. maandelijks wachtwoord veranderen wachtwoorden als 'pass'.$maandnr uitlokt. :P

[ Voor 13% gewijzigd door Voutloos op 29-05-2007 10:25 ]

{signature}


Acties:
  • 0 Henk 'm!

  • Equator
  • Registratie: April 2001
  • Laatst online: 09-09 15:29

Equator

Crew Council

#whisky #barista

offtopic:
@Voutloos: Het is mysql_real_escape_string() ;)

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
offtopic:
Je hebt gelijk, maar als je door een dergelijke typo de functie niet zou kunnen vinden, ben je te dom om te poepen. :+

{signature}


Acties:
  • 0 Henk 'm!

  • dusty
  • Registratie: Mei 2000
  • Laatst online: 15-09 18:24

dusty

Celebrate Life!

Cartman! schreef op maandag 28 mei 2007 @ 20:44:
[...]
Kun je me es uitleggen waarom dit zo verkeerd is?
Alles wat je uit de database haalt kan op het net terecht komen ( door wat voor fout dan ook. ) Dus als jij je hash uit je database haalt, kan die hash express/per ongeluk op het scherm getoond worden. (voor wat voor reden dan ook..) Wat je niet uit de database haalt kan ook niet op het scherm getoverd worden.

Mensen die denken dat het coderen van een wachtwoord clientside een stuk veiliger is, moeten gewoon HTTPS gebruiken. Als het wachtwoord dan al zo belangrijk is. zo'n 70% van accounts die worden gehacked op fora is via keyloggers, een groot gedeelte van de rest is gewoon personen die niet uitloggen of hun gegevens aan anderen geven. De percentage dat echt wordt gesniffed is gewoon ontzettend laag.
[...]
Overigens is het teruggeven van een 'gebruiker bestaat niet' nooit goed. Altijd alleen de melding gebruikernaam/wachtwoord combinatie komt niet overeen. Dan weet iemand ook niet of de gebruiker wel bestaat, je geeft anders teveel info weg.
Ik ben zelfs nog gemener bezig dan dat, op sommige sites als je te vaak 'verkeerd' inlogt kun je dus niet meer inloggen vanaf het ip waarop je het probeert de foutmelding die je dan krijgt? "Verkeerde gebruiker/wachtwoord combinatie". Als de trigger dus afgaat kan je zelfs een juiste login en wachtwoord invoeren, krijg je nog steeds dezelfde melding :+

Back In Black!
"Je moet haar alleen aan de ketting leggen" - MueR


Acties:
  • 0 Henk 'm!

  • pietje63
  • Registratie: Juli 2001
  • Laatst online: 16:14

pietje63

RTFM

Ik zou dan denk ik voor de middenweg gaan: wel het formulier nog een keer tonen (voor de bot) maar OOK een melding voor de echte bezoekers.

De grootste Nederlandstalige database met informatie over computers met zoekfunctie!!


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Alles wat je uit de database haalt kan op het net terecht komen ( door wat voor fout dan ook. ) Dus als jij je hash uit je database haalt, kan die hash express/per ongeluk op het scherm getoond worden. (voor wat voor reden dan ook..) Wat je niet uit de database haalt kan ook niet op het scherm getoverd worden.
Dat wachtwoord echo je natuurlijk nergens, ook niet per ongeluk. Dat vind ik echt een non-argument. Als je dat voor elkaar krijgt is er serieus wat mis met je code natuurlijk.
Mensen die denken dat het coderen van een wachtwoord clientside een stuk veiliger is, moeten gewoon HTTPS gebruiken. Als het wachtwoord dan al zo belangrijk is. zo'n 70% van accounts die worden gehacked op fora is via keyloggers, een groot gedeelte van de rest is gewoon personen die niet uitloggen of hun gegevens aan anderen geven. De percentage dat echt wordt gesniffed is gewoon ontzettend laag.
Tuurlijk is https een optie. Het nadeel is dat voor veel klanten een SSL certificaat te duur is of ze zien t nut er niet van in. Dus moet het toch zo veilig mogelijk zijn als je protocol het toelaat. Als bedrijf zijnde wil je je toch indekken tegen dingen als sniffen. Een keylogger is een probleem van de user zelf, niet meer van de technische implementatie.
Ik ben zelfs nog gemener bezig dan dat, op sommige sites als je te vaak 'verkeerd' inlogt kun je dus niet meer inloggen vanaf het ip waarop je het probeert de foutmelding die je dan krijgt? "Verkeerde gebruiker/wachtwoord combinatie". Als de trigger dus afgaat kan je zelfs een juiste login en wachtwoord invoeren, krijg je nog steeds dezelfde melding :+
Goede optie, maar zinloos op het moment dat gebruik wordt gemaakt van anonieme proxies of bijv. Tor.

Ben dus niet echt overtuigd eigenlijk, je hebt namelijk ook het wachtwoord voor je database connectie in een config file oid staan. Hoe wil je dat dan oplossen?

Acties:
  • 0 Henk 'm!

Verwijderd

Zoals al vaak gezegd, de security is maar zo goed als de eindgebruiker.

Jij kunt dus jouw high-tech systeem neerzetten, maar als de gebruiker dan z'n wachtwoord op de bekende post-it schrijft en op de monitor plakt kom je nog nergens.

Het is dus belangrijk de boel af te stemmen op de doelgroep en situatie, als je dan een beetje overkill hebt is het al prima.

Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Poeh, na heel wat bijwerken en nadenken heb ik er het volgende van gemaakt (dit moet het uiteindelijke script worden). Kan her en der een klein stukje missen, aangezien ik in het wilde weg heb zitten knippen om het enigzins kort te kunnen houden. Alle css en html opbouw heb ik er ook weg gehaald in dit voorbeeld. Let dus alleen op de code ;)

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<?php session_start(); require_once('./connections/db.php');

if( isset( $_SESSION['huidig_ip'] ) && $_SESSION['huidig_ip'] != $_SERVER['REMOTE_ADDR'] )
{
  session_unset();
  session_destroy();
  die();
}

if ($_GET['mode'] == "logout") {
    $query = "UPDATE database.users SET sessid='' WHERE id='".$_SESSION['userdata'][0]."' and username='".$_SESSION['userdata'][1]."'";
    $result = @mysql_query($query);
    $_SESSION['sessid'] = '';
    $_SESSION['loggedin'] = FALSE;
    $_SESSION['userdata'] = '';
    session_unset();
}

function parserandom($length)
{
    $pattern = "1234567890abcdefghijklmnopqrstuvwxyz";
    $key  = $pattern{rand(0,35)};
    for($i=1;$i<$length;$i++)
    {
        $key .= $pattern{rand(0,35)};
    }
    return $key;
}

function chckLoginStatus()  {
    if (($_SESSION['loggedin'] == true) && isset($_SESSION['userdata']) && ($user_Account_Blocked !== true)) {
        $login_Query = "SELECT * FROM database.users WHERE id='".$_SESSION['userdata'][0]."' and username='".$_SESSION['userdata'][1]."' and sessid='".$_SESSION['sessid']."' LIMIT 0,1";
        $login_Result = @mysql_query($login_Query) or trigger_error(mysql_error());
        if (@mysql_num_rows($login_Result) == 1) {
            if ($_SESSION['sessid'] == sha1(session_id().date("Y-m-d"))) {
                return "positive".sha1(session_id().date("Y-m-d"));
            } else {
                return false;
            }
        } else {
            return false;
        }
        @mysql_free_result($login_Result) or trigger_error(mysql_error());
    } else { 
        return false; 
    }   
} 

function updateFailedAttempts($num,$remaddr,$username)  {
    $query = "SELECT * FROM database.fails WHERE ipaddress='".$_SERVER['REMOTE_ADDR']."' LIMIT 0,1";
    $result = @mysql_query($query);
    if (@mysql_num_rows($result) == 0) {
        $query = "INSERT INTO database.fails (ipaddress,amount,lastdate,lasttime,username) VALUES('".$remaddr."','".$num."','".date("Y-m-d")."','".date("H:i:s")."','".@mysql_real_escape_string($username)."')";
        $result = @mysql_query($query);
    } else {
    $fetch = @mysql_fetch_assoc($result);
    $lastdate = $fetch['lastdate'];
            if ($lastdate == date("Y-m-d")) {
                $amount = "amount+".$num;
            } else {
                $amount = $num;
            }
        $query = "UPDATE database.fails SET amount=(".$amount."), lastdate='".date("Y-m-d")."', lasttime='".date("H:i:s")."', username='".@mysql_real_escape_string($username)."' WHERE ipaddress='".$remaddr."'";
        $result = @mysql_query($query);
    }
}
$query = "SELECT amount,lastdate FROM database.fails WHERE ipaddress='".$_SERVER['REMOTE_ADDR']."' LIMIT 0,1";
$result = @mysql_query($query);
$fetch = @mysql_fetch_assoc($result);
$amount = $fetch['amount'];
$lastdate = $fetch['lastdate'];
if (@mysql_num_rows($result) == 1) {
    if (($amount > 3) && ($lastdate == date("Y-m-d"))) {
        $user_Account_Activationkey = sha1(@parserandom(255));
        $query = "UPDATE database.fails SET activkey='".$user_Account_Activationkey."' WHERE ipaddress='".$_SERVER['REMOTE_ADDR']."'";
        $result = @mysql_query($query);
        if ($result) {
            $user_Account_Blocked = true;
        }
    } elseif ($lastdate != date("Y-m-d")) {
        $query = "UPDATE database.fails SET amount='0',activkey='' WHERE ipaddress='".$_SERVER['REMOTE_ADDR']."'";
        $result = @mysql_query($query);
    }
}

if( isset( $_POST['username'], $_POST['response'], $_SESSION['challenge'] ) && ($user_Account_Blocked != true) )
{
    $query = "SELECT * FROM database.users WHERE username = '".@mysql_real_escape_string($_POST['username'])."' LIMIT 0,1";
    $result = @mysql_query($query);
    $fetch = @mysql_fetch_assoc($result);
    if (@mysql_num_rows($result) == 1) {
        if ( sha1($fetch['password'].$_SESSION['challenge']) == $_POST['response']) {
            $_SESSION['loggedin'] = true;
            $_SESSION['userdata'] = array($fetch['id'],$fetch['username']);
            $_SESSION['sessid'] = sha1(session_id().date("Y-m-d"));
            $query = "UPDATE database.users SET sessid='".$_SESSION['sessid']."' WHERE id='".$_SESSION['userdata'][0]."' and username='".$_SESSION['userdata'][1]."'";
            $result = @mysql_query($query) or trigger_error(mysql_error());
            } else {
        $login_Failed_Error = true;
        @updateFailedAttempts(1,$_SERVER['REMOTE_ADDR'],$_POST['username']);
        }
    } else {
        $login_Failed_Error = true;
        @updateFailedAttempts(1,$_SERVER['REMOTE_ADDR'],$_POST['username']);
    }
    if ($result) { @mysql_free_result($result); }
}

if( !isset( $_SESSION['huidig_ip'] ) )
{
    $_SESSION['huidig_ip'] = $_SERVER['REMOTE_ADDR'];   
}

$challenge = @sha1(@date("Ymd").@parserandom(255));
$_SESSION['challenge'] = $challenge;
 ?>
<!-- !knip! (blablabla, niet interessant) -->
<script type="text/javascript" src="./js/hash.sha1.js"></script>
<script type="text/javascript">
<!--
function createResponse()
{
  document.getElementById( 'response' ).value = SHA1( SHA1( document.getElementById( 'password' ).value ) + document.getElementById( 'challenge' ).value );
  document.getElementById( 'password' ).value = "";
  document.getElementById( 'challenge' ).value = "";
  return true;
}
//-->
</script> 
<!-- !knip! (blablabla, niet interessant) -->
          <?php
          if (@chckLoginStatus() == "positive".$_SESSION['sessid']) {
            echo 'ingelogd';
          } else {
          echo 'In order to use your account, you need to log in.<br />Enter your private username and password and submit this form.<hr />';
          if ($login_Failed_Error == TRUE) {
          echo '<div class="error"><img src="./img/embedded/icons/error.gif" alt="Error" align="top" /> It appears you could not be logged in. Please try again.<br /></div>';
          }
          if ($user_Account_Blocked != true) {
          ?>
            <form method="post" action="./inloggen.php" onsubmit="return createResponse();">
            <input name="username" type="text" class="logintxt" size="40" /><input type="password" class="logintxt" id="password" size="40" />
            <input type="hidden" id="challenge" value="<?php echo $_SESSION['challenge']; ?>">
            <input type="hidden" name="response" id="response" value="">
            <label>
            <input type="checkbox" name="usr_Public" value="true" />
            I am using a public computer</label>
            <br />
            <label>
            <input type="checkbox" name="usr_SetCookie" value="true" />
            Keep me logged in</label>
            <br />
            <input type="submit" value="Access" style="font-weight:bold;" /> <input type="button" name="button2" id="button2" value="Register account" onclick="location.href='<?php echo $_SERVER['PHP_SELF']; ?>?mode=register'" />
          </form>
          <?php 
            } else {
                echo '<div class="error"><img src="./img/embedded/icons/error.gif" alt="Error" align="top" /> It appears you have failed logging in for 4 or more times.<br />Therefore, your ip-address ('.$_SERVER['REMOTE_ADDR'].') has been blocked for today. You will be able to login again tomorrow.</div>';
            }
          } ?>
 <!-- !knip! -->
<?php if ($result) { @mysql_free_result($result); } ?>
</body>
</html>


Het systeem registreerd dus elke verkeerde login attempt en bij 4 attempts kan dat ip-adres voor een dag niet meer inloggen. De volgende dag wordt gekeken of er nog geregistreerde attempts zijn en zoja, wordt gecheckt van welke dag die zijn, en als dat niet dezelfde dag is, staat de teller weer op nul.

Daarnaast wordt er een uniek id opgeslagen in de database doormiddel van een sha1 hash van het sessie-id en de datum, en dit uniek id wordt ook in een sessie opgeslagen ($_SESSION['sessid']).

Iedere keer dat moet worden geverifiëerd of een gebruiker is ingelogd, kan de functie chckLoginStatus() worden aangeroepen, die controlleert of de verplichte sessies $_SESSION['loggedin'], $_SESSION['userdata'] (waar de broodnodige gebruikersinfo in is opgeslagen) en $_SESSION['sessid'] bestaan. Daarna doet het een query in de database en als daar een row van matcht, dan worden de sessie $_SESSION['sessid'] met de de sha1 hash van het session_id en de datum gecontrolleerd. Als dat allemaal klopt, geeft de functie de string "positive" met een sha1 hash van het session_id en de datum terug. Dat wordt bij het inloggen vervolgens weer gecontrolleerd met de sessie $_SESSION['sessid'].
:Y .

Daarnaast is het me nog niet gelukt info over salt te vinden (weet niet precies wat dat inhoud, maar zou het wel graag toe willen passen), maar gebruikt het script dus nog altijd challenge en response.
Alles wat je uit de database haalt kan op het net terecht komen ( door wat voor fout dan ook. ) Dus als jij je hash uit je database haalt, kan die hash express/per ongeluk op het scherm getoond worden. (voor wat voor reden dan ook..) Wat je niet uit de database haalt kan ook niet op het scherm getoverd worden.
Om eerlijk te zijn, plak ik aan Cartman!'s point of view

*O* ?

Acties:
  • 0 Henk 'm!

  • Equator
  • Registratie: April 2001
  • Laatst online: 09-09 15:29

Equator

Crew Council

#whisky #barista

Om eerlijk te zijn, plak ik aan Cartman!'s point of view
Natuurlijk echo je nooit je password hash, maar als er door een foutje in de code dat ding zich toch laat zien ben je zuur :p

En bovendien: Als ik wil weten of de gebruiker pietje bestaat met eht wachtwoord "12345678" dan is dit:
PHP:
1
2
3
4
5
6
7
8
9
10
$query = "SELECT uid, pwd FROM tUsers WHERE uid = '" . mysql_real_escape_string($_POST['uid']) .  "'";
$result = mysql_fetch_assoc(mysql_query($query));
if($result['pwd'] == $_POST['pwd']) 
{
    //user posted the correct wachtwoord
}
else
{
    //user posted the wrong password
}

op zich niet anders dan:
PHP:
1
2
3
4
5
6
7
8
9
10
$query = "SELECT COUNT(*) as count FROM tUsers WHERE uid = '" . mysql_real_escape_string($_POST['uid'] . "' AND pwd = '" . mysq_real_escape_string($_POST['pwd']) ."'";
$result = mysql_fetch_assoc(mysql_query($query));
if($result['count'] > 0)
{
    //user posted correct password
}
else
{
    //user posted incorrect password
}

Maar de 2e optie is misschien nog wel sneller, en je weet iig zeker dat je password hash nooit op straat komt te liggen.

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
cbernardini schreef op dinsdag 29 mei 2007 @ 22:14:
Daarnaast is het me nog niet gelukt info over salt te vinden (weet niet precies wat dat inhoud, maar zou het wel graag toe willen passen), maar gebruikt het script dus nog altijd challenge en response.
Dan google je naar 'encryption salt' of 'hash salt' en vind je wel boeiende info ipv de recepten die verschijnen als je alleen op 'salt' zoekt. ;) Bv. http://en.wikipedia.org/wiki/Salt_(cryptography)

{signature}


Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Equator, het hele punt is meer dat je op die 2e manier (alles in je query) niet je challenge/response kunt afhandelen. In de scripts die ik maak is er geen kans op dat het password ergens ge-echo'ed wordt, foutmelding of niet. Nog steeds geen argument imo.

Het prettige aan het systeem vind ik dat de gebruiker er niks van merkt (geen extra handelingen) en dat het toch n stuk veiliger is. Als jij inlogt bij je online bankieren wens je toch ook dat dit op de meest veilige manier gaat? Ookal heb je misschien je pincode naast je monitor geplakt.

[ Voor 36% gewijzigd door Cartman! op 30-05-2007 11:12 ]


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Nu heb ik nog een vraagje, om niet nog een topic te vervuilen ga ik hier maar verder.

Ik wil nu ook dat de gebruiker ervoor kan kiezen om ingelogd te blijven met een cookie. Ik heb echter nog nooit cookies gebruikt en vraag me dus af hoe ik dit moet doen, en hoe ik de cookie veilig kan houden. Watvoor string kan ik bijvoorbeeld het beste in de cookie opslaan en hoe kan ik de cookie het beste aanmaken etc. etc.

Iemand? :)

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Lees eerst setcookie() in manual en dan zien we wel weer of je nog vragen hebt. :P

{signature}


Acties:
  • 0 Henk 'm!

  • VR46
  • Registratie: Januari 2005
  • Laatst online: 08-09 12:51
Voutloos schreef op donderdag 31 mei 2007 @ 14:53:
Lees eerst setcookie() in manual en dan zien we wel weer of je nog vragen hebt. :P
Ja, dat heb ik al wel doorgelezen en ik weet in principe wel hóe je er mee om moet gaan, maar ik vraag jullie advies over veiligheid en hoe je er het beste mee te werk kunt gaan.

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
In je cookie zet je de info die jij nodig hebt om een login te checken. Basisregel is om nooit het wachtwoord (gehashed of niet) client side op te slaan. Zelf sla ik de session hash op in een cookie. User kan aangeven of er een iplock of tijdlock op moet komen tijdens het inloggen.

Acties:
  • 0 Henk 'm!

  • Mr_gadget
  • Registratie: Juni 2004
  • Laatst online: 20-09 14:54

Mr_gadget

C8H10N4O2 powered

Ik gebruik zelf een alleen een session als de gebruiker is ingelogd, is dit onveilig? Register globals staan uit dus je kan geen session id meegeven in de taakbalk.

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Mr_gadget schreef op donderdag 31 mei 2007 @ 20:01:
Ik gebruik zelf een alleen een session als de gebruiker is ingelogd, is dit onveilig?
Tal van sites werken zo. Wel is het aardig om de optie aan te bieden om te kunnen koppelen aan ip, en een keuze voor max. levensduur. Kijk eens hoe het inloggen bij je favoriete forum werkt. ;)
Register globals staan uit dus je kan geen session id meegeven in de taakbalk.
Que? Via je adresbalk bedoel je, maar dan nog heeft het een weinig met het ander te maken. Als register globals uit staat kan je net zo goed met http request variabelen werken (gelukkig wel :P ), je moet het alleen expliciet doen.

{signature}

Pagina: 1