Toon posts:

[PHP]hoe veilig is mijn login script?

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

Verwijderd

Topicstarter
Ik heb een login script geschreven dat werkt met sessies, maar vroeg me af of dit schript wel veilig is.
Ik hoop dus dat iemand van hier die over de nodige kennis beschikt er even naar zou willen kijken, en eventueel wat commentaar zou kunnen leveren.

Hieronder het login script:
code:
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
<?php 
  session_start(); 
?> 

<html>
<head>
<title></title>
</head>
<body>

<?php
if ($action=="login") {
  $login=addslashes($login); 
  $password=addslashes($password); 
  $password=md5($password);
  $sql="select id from users where login='$login' and password='$password'";
  $res=mysql_query($sql);
  $count=mysql_num_rows($res); 
  if ($count=='1') { 
    if ($rec=mysql_fetch_object($res)) {
      $login='1';
      $user_id=$rec->id;
      $user_ip=$REMOTE_ADDR;
      session_register("login");
      session_register("user_id");
      session_register("user_ip");
      $session_id=session_id(); 
      echo "Je bent ingelogd";
    }     
  } else {
    echo "Wrong login or password";
  }
}
?>

<form action="login.php" method="post">
<table>
<tr>
  <td>Login</td>
  <td><input type="text" name="login" size="15" maxlength="15"></td>
</tr>
<tr>
  <td>Password</td>
  <td><input type="password" name="password" size="15" maxlength="15"></td>
</tr>
<tr>
  <td><input type="hidden" name="action" value="login"></td>
  <td><input type="submit" name="submit" value="login"></td>
</tr>
</table>
</form>

</body>
<html>


En dit staat bovenin elke pagina om te controleren of iemand ingelogd is:
code:
1
2
3
4
5
6
7
8
<?php 
  session_id($session_id); 
  session_start(); 
?> 

if (($HTTP_SESSION_VARS["login"]!='1')&&($HTTP_SESSION_VARS["user_ip"]!=$REMOTE_ADDR)) { 
  echo "You need to login.";
}


Ik wil dat het login script ook werkt bij gebruikers die geen cookies ondersteunen. Pagina's roep ik dus aan met bijvoorbeeld:
code:
1
<a href="pagina.php?session_id=<?php echo "$session_id"; ?>">pagina</a>


Iig vast bedankt.

Greetz,

Bram

Verwijderd

om hen nog veiliger te maken is dat als hij de eerste keer inlogt het ip adres opslaat zodat hij alleen via dat ip kan werken op die tijd, wanneer hij uitlogt moet je hem wel vrij geven, en zo kan nietmand tegelijke tijd op dat account inloggen. dat is een optie, ik kan het niet naar php vertalen want doe alleen met asp, maar hier werkt het perfect mee,


try it:)

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op 23 augustus 2002 @ 16:27:
om hen nog veiliger te maken is dat als hij de eerste keer inlogt het ip adres opslaat zodat hij alleen via dat ip kan werken op die tijd, wanneer hij uitlogt moet je hem wel vrij geven, en zo kan nietmand tegelijke tijd op dat account inloggen. dat is een optie, ik kan het niet naar php vertalen want doe alleen met asp, maar hier werkt het perfect mee,


try it:)

Hij houdt al bij welk ip bij een sessie hoort ;)

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • 2
  • Registratie: November 2000
  • Laatst online: 31-03 13:52

2

Wat misschien een goed idee is is het opslaan van een IP adres bij het invullen van een verkeerde username/password combinatie. Als de gebruiker het dan meer dan bijvoorbeeld 10 keer fout doet ban je hem voor een minuutje ofzo. Zo kan een scriptje bijvoorbeeld niet oneindig veel combinaties gaan proberen.

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Als je geen SSL gebruikt gaat je wachtwoord plaintext over het net. Mensen vinden dit vaak geen probleem, maar als je je bedenkt dat op ethernet alle packets worden gebroadcast...

Oh en, je kijkt niet of $HTTP_SESSION_VARS["user_ip"] gezet is boven elke pagina. Weet niet of het echt een probleem is, maar geeft in ieder geval een warning als dat de eerste pagina is die ik aanroep. Dus !(isSet($HTTP_SESSION_VARS["user_ip"]) && $HTTP_SESSION_VARS["user_ip"]=$REMOTE_ADDR) is beter dan.

En als je via dezelfde proxy gaat kan je nog steeds sessies kapen. Je kan beter ook nog ff kijken of HTTP_X_FORWARDED_FOR is gezet, als dat zo is heb je meer kans op het echte IP te krijgen ipv het proxy ip.

Ik sla vaak ook nog wat andere (constante) client info op, niet alleen IP. Bv exacte browser/OS versie. Soort extra weak check tegen ip spoofen/proxies.

  • Ehv_BramBo
  • Registratie: November 2000
  • Laatst online: 13-04 22:30
Oke dan mensen, bedankt voor jullie reacties.

het inbouwen van een controle op foutief inloggen is idd een goed idee. Ik zelf wil ook nog een ban list bij gaan houden (email en eventueel ip adres), en bij het inloggen/registreren moet dan gekeken worden of iemand op die lijst staat. En de beveiliging voor mensen achter dezelfe proxy is ook wel een goed idee.

Verder kan ik helaas geen SSL gebruiken. Want dat is toch iets wat op de server geregeld is, en niet iets wat je met je code kan regelen?

Wat ik me zelf nog afvraag is of het slim is om het sessie id aan de URL mee te geven. Gaat dit wel goed met het bookmarken van pagina's en kan een gebruiker hier verder nog kwaad mee doen? (dat laatste lijkt mij niet het geval, maar weet het dus niet zeker). Ik hoop iig dat het geen probeem is. Want ik zou niet weten, hoe ik het geheel anders zonder cookies kan laten werken.


p.s. ik post nu ff onder een andere acount omdat ik de mail van mijn andere acount hier niet kan lezen en mijn paswoord zo niet weet :D

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

[rml]Zoijar in "[ PHP & MYSQL] md5() inloggen"[/rml]

edit:
daar staat een werkwijze zodat je ww niet plaintext over het net gaat

  • Ehv_BramBo
  • Registratie: November 2000
  • Laatst online: 13-04 22:30
Zoijar schreef op 23 augustus 2002 @ 21:10:
[rml]Zoijar in "[ PHP & MYSQL] md5() inloggen"[/rml]

edit:
daar staat een werkwijze zodat je ww niet plaintext over het net gaat
mmmm ik geloof niet dat ik het helemaal begrijp.

Ik snap de methode, maar snap het nut ervan niet zo. Wanneer de waarden ingevuld zijn in het formulier en de post plaats vind, worden de gegevens toch al verstuurd over het web? En dan is het toch al te laat om deze extra beveiliging toe te passen?

En weet je mischien of het meegeven van het sessie id aan de URL (die dus gewoon zichtbaar is) veiligheids problemen op kan leveren?

Verwijderd

2 schreef op 23 augustus 2002 @ 17:17:
Wat misschien een goed idee is is het opslaan van een IP adres bij het invullen van een verkeerde username/password combinatie. Als de gebruiker het dan meer dan bijvoorbeeld 10 keer fout doet ban je hem voor een minuutje ofzo. Zo kan een scriptje bijvoorbeeld niet oneindig veel combinaties gaan proberen.
Een sleep(3) voordat je laat zien dat z'n username/password verkeerd was is veel gemakkelijker. :P

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Ehv_BramBo schreef op 23 augustus 2002 @ 23:03:
[...]


mmmm ik geloof niet dat ik het helemaal begrijp.

Ik snap de methode, maar snap het nut ervan niet zo. Wanneer de waarden ingevuld zijn in het formulier en de post plaats vind, worden de gegevens toch al verstuurd over het web? En dan is het toch al te laat om deze extra beveiliging toe te passen?

En weet je mischien of het meegeven van het sessie id aan de URL (die dus gewoon zichtbaar is) veiligheids problemen op kan leveren?
Nee niet te laat, want die eerste md5 is client side, mbv javascript. Dus je enige ww wat op het net komt is een md5 hash. Dat klinkt veilig, maar nu gaat iemand langs je script en stuurt rechtstreeks die md5. Dan gaat het nog steeds fout, dat is replay attack. Dus voeg je er een waarde aan toe ("nonce"-number used once), en 32bit random is wel "goed genoeg" aangezien dat een kans van 1 op 2^16 geeft voor een vergebruikt nummer.
Hmmm ik ben behoorlijk aangeschoten nu, leg het anders morgen wel uit... :Z :)

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 20:22

Reptile209

- gers -

Verwijderd schreef op 23 augustus 2002 @ 16:24:
code:
1
2
3
4
5
6
[...]
      echo "Je bent ingelogd";
    }     
  } else {
    echo "Wrong login or password";
[...]
[oftopic]
Natuurlijk nog wel even kiezen tussen Engels en Nederlands :D

Zo scherp als een voetbal!


Verwijderd

Wat misschien ook nog wel even handig is is register globals op off zetten in php.ini. Als jij rechtstreeks de variabele $password en $login kan raadplegen ipv het moeten gebruiken van $_POST['password'] of $_GET['password'] en $_POST['login'] of $_GET['login'] staat dat nl op on.

en dan het volgende:
PHP:
1
($HTTP_SESSION_VARS["login"]!=1)&&($HTTP_SESSION_VARS["user_ip"]!=$REMOTE_ADDR)

deze expressie levert true op als zowel login als user_ip niet kloppen. Maar wat je eigenlijk wil is dat mensen al niet ingelogd zijn als 1 van de 2 niet kloppen. AND moet dus OR zijn oftewel ||
Op de manier zoals jij het deed blijven mensen ingelogd als alleen login op 1 staat, of erger nog: als alleen het ip adres klopt.

Stukje wiskunde:
p ^ false <-> false
p ^ true <-> p
p v false <-> p
p v true <-> true

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Had je trouwens hier al es naar gekeken: Welkom in P&W (FAQ-19/02/2003)

Stukje had ik niet voor niets geschreven en dan kan je zien of je iig aan die puntjes voldoet :)

  • Ehv_BramBo
  • Registratie: November 2000
  • Laatst online: 13-04 22:30
2 Zoijar: a haa, thanx :) mijn java script is alleen zwaar onder de maat. heb je daar mischien een voorbeeldje van? Zou wel cool zijn.

2 daemian: AU!!! hehehe stom....stom.....stom. had de conditie eerst anders om staan (controleerde of ze beide true waren). Maar heb er not's van gemaakt zodat ik daarachter bijvoorbeeld een die() functie kon gebruiken en de code steeds boven in elk script kon zetten (zadat ik alle beveiligde pagina's tussen een if conditie hoef te gooien). Ben dus vergeten om "&&" in "||" te veranderen. Das stom :(

2 ACM: heb het nu gelezen, staan idd wat interessante punten in (ook m.b.t. andere zaken) :)

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Vooruit dan maar :) Als PHP gebruik ik dit:

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
<?php
require_once('class/database.class.php');

define('C_SQL_LOGIN_USER', 'SELECT id, passw FROM books WHERE login = \'%s\'');

class Login {
    var $challenge;
    var $user;

    var $allows;
    var $CDatabase;

    function Login(&$CDatabase, &$Session) {
        $this->challenge = &$Session['login_challenge'];
        $this->user = &$Session['login_user'];
        if (!isSet($Session['login'])) {
            $Session['login'] = true;
            $this->challenge = 0;
            $this->user = '';
            $this->seed();
        }

        $this->CDatabase = &$CDatabase;
        $this->allows = array();
    }

    function allowFrom($user, $password) {
        $this->allows[$user] = $password;
    }

    function makeChallenge() {
        $this->challenge = $this->random();
    }

    function getChallenge() {
        return $this->challenge;
    }

    function doLogin($username, $passw) {
        if (!$info = $this->findUser($username)) return false;
        if ($this->challenge != 0 && md5($info[1] . $this->challenge) == $passw) {
            $this->challenge = 0;
            $this->user = $info[0];
            return true;
        } else return false;
    }

    function logout() {
        $this->user = '';
        $this->challenge = 0;
    }

    function isLoggedOn() {
        return ($this->user != '');
    }

    function getUser() {
        return $this->user;
    }



// PRIVATE

    function seed() {
        mt_srand ((double) microtime() * 1000000); 
    }

    function random() {
        return mt_rand();
    }

    function findUser($user) {
        if (isSet($this->allows[$user])) {
            $info = array();
            $info[0] = $user;
            $info[1] = $this->allows[$user];    
            return $info;
        } else {
            return $this->CDatabase->queryrow(sprintf(C_SQL_LOGIN_USER, addslashes($user)));
        }
    }

}

?>


Dan als javascript md5 dit: (niet van mij)
code:
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Copyright (C) Paul Johnston 1999 - 2000.
 * Updated by Greg Holt 2000 - 2001.
 * See http://pajhome.org.uk/site/legal.html for details.
 */

/*
 * Convert a 32-bit number to a hex string with ls-byte first
 */
var hex_chr = "0123456789abcdef";
function rhex(num)
{
  str = "";
  for(j = 0; j <= 3; j++)
    str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
           hex_chr.charAt((num >> (j * 8)) & 0x0F);
  return str;
}

/*
 * Convert a string to a sequence of 16-word blocks, stored as an array.
 * Append padding bits and the length, as described in the MD5 standard.
 */
function str2blks_MD5(str)
{
  nblk = ((str.length + 8) >> 6) + 1;
  blks = new Array(nblk * 16);
  for(i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(i = 0; i < str.length; i++)
    blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
  blks[i >> 2] |= 0x80 << ((i % 4) * 8);
  blks[nblk * 16 - 2] = str.length * 8;
  return blks;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally 
 * to work around bugs in some JS interpreters.
 */
function add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * These functions implement the basic operation for each round of the
 * algorithm.
 */
function cmn(q, a, b, x, s, t)
{
  return add(rol(add(add(a, q), add(x, t)), s), b);
}
function ff(a, b, c, d, x, s, t)
{
  return cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t)
{
  return cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t)
{
  return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t)
{
  return cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Take a string and return the hex representation of its MD5.
 */
function calcMD5(str)
{
  x = str2blks_MD5(str);
  a =  1732584193;
  b = -271733879;
  c = -1732584194;
  d =  271733878;

  for(i = 0; i < x.length; i += 16)
  {
    olda = a;
    oldb = b;
    oldc = c;
    oldd = d;

    a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = ff(c, d, a, b, x[i+10], 17, -42063);
    b = ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = ff(d, a, b, c, x[i+13], 12, -40341101);
    c = ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = ff(b, c, d, a, x[i+15], 22,  1236535329);    

    a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = gg(c, d, a, b, x[i+11], 14,  643717713);
    b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = gg(c, d, a, b, x[i+15], 14, -660478335);
    b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = gg(b, c, d, a, x[i+12], 20, -1926607734);
    
    a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = hh(b, c, d, a, x[i+14], 23, -35309556);
    a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = hh(d, a, b, c, x[i+12], 11, -421815835);
    c = hh(c, d, a, b, x[i+15], 16,  530742520);
    b = hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = ii(c, d, a, b, x[i+10], 15, -1051523);
    b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = ii(d, a, b, c, x[i+15], 10, -30611744);
    c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = add(a, olda);
    b = add(b, oldb);
    c = add(c, oldc);
    d = add(d, oldd);
  }
  return rhex(a) + rhex(b) + rhex(c) + rhex(d);
}

function MD5Login(f, rndval) {
    f.password.value = calcMD5(calcMD5(f.password.value) + rndval);
    return true;
}


En dan last but not least dit als form: (hoop dat je door de xsl kan lezen...)

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="login">
    <FORM NAME="Login" ACTION="index.php" METHOD="GET">
        <xsl:attribute name="onSubmit">javascript:return MD5Login(this, <xsl:value-of select="random"/>);</xsl:attribute>

        <DIV>Username: </DIV><INPUT TYPE="Text" NAME="username" VALUE="" /><BR />
        <DIV>Password: </DIV><INPUT TYPE="Password" NAME="password" VALUE="" /><BR />
        
        <INPUT TYPE="Submit" NAME="action">
            <xsl:attribute name="value"><xsl:value-of select="action"/></xsl:attribute>
        </INPUT>
    </FORM>
</xsl:template>
</xsl:stylesheet>
Pagina: 1