Tutorials

Loginscripts revisited

Méér dan alleen een loginscript

Pagina 1

Alweer een login script?

Er staan nogal veel login scripts op phphulp. Ik wil niet zomaar nog een nieuw loginscript eraan toevoegen, dus vandaar deze tut. Voordat we beginnen, eerst een paar belangrijke dingen.

Gebruik geen cookies
Zeker, cookies zijn handig. Een gebruiker hoeft niet steeds opnieuw in te loggen. Je kunt bijvoorbeeld een cookie zetten die drie dagen duurt. Dat betekent dus dat iedereen die achter die computer zit drie dagen niet in hoeft te loggen en dus overal bij kan. Bovendien kunnen cookies gestolen worden. Niet doen, dus.

Zet geen username en/of password in de sessie
Je hoeft maar één keer te controleren of een combinatie username/password correct is; als iemand actief inlogt. Daarna heb je die info niet meer nodig, en is het zelfs onveilig om ze in een sessie te zetten. Het enige dat we in een sessie gaan zetten, is het user_id (gewoon een INT) en een boolean (logged_in) die true of false is.
Pagina 2

De loginpagina

De user kan op drie manieren op de loginpagina terecht komen:
1) Als hij wil gaan inloggen
2) Als een loginpoging mislukt is
3) Als hij uitlogt

Wat we voor de zekerheid doen op de loginpagina is eventueel bestaande sessievariabelen opschonen, voor het geval de user uitgelogd heeft. Verder tonen we natuurlijk het inlogformulier. De inlogpagina heet bij mij index.php
De code:

<?php
	session_start ();
	$_SESSION = array ();
	session_destroy ();
?>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">


<html>
<head>
	<title>Login Pagina</title>
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	<style type="text/css">
		@import 'login.css';
	</style>
</head>

<body>
	<form method="post" action="secure_page.php" class="login">
		<h1>Inlogscherm</h1>
		<p>
			<label for="username">username:</label>
			<input id="username" name="username" type="text">
		</p>
		<p>
			<label for="password">password:</label>
			<input id="password" name="password" type="password">
			<input type="submit" value="inloggen">
		</p>
	</form>
</body>
</html>

In deze tut zit wat minimale CSS verwerkt, login.css. Hieronder de code:

form.login {
	margin: 32px auto;
	width: 500px;
	border: 1px solid #69c;
	padding: 16px;
}

h1 {
	font: bold 18px Georgia;
	color: #69c;
	background: #f9f9f9;
	border-top: 1px solid #ccc;
	border-bottom: 1px solid #ccc;
	padding: 5px;
}

p, input, label, pre {
	font: 11px verdana;
}

form.login label {
	float: left;
	width: 200px;
	text-align: right;
	padding-right: 5px;
	padding-top: 3px;
}

form.login input {
	padding: 3px;
}
Pagina 3

Verwerken van de formuliergegevens

In dit voorbeeld gebeurt dat op secure_page.php
De strategie van dit inlogscript is zodanig, dat je alleen maar een php bestand hoeft te includen op elke pagina die je wilt beveiligen. 'secure_page.php' ziet er zo uit:

<?php
	require 'authenticatie.php';
?>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
	<title>Secure Page 1</title>
	<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
	<style type="text/css">
		@import 'login.css';
	</style>
</head>

<body>
	<h1>Secure page 1</h1>
	<p>
		Als u deze pagina ziet, bent u succesvol ingelogd.
	</p>
	<p>
		<a href="secure_page2.php" title="Ga naar pagina 2">Ga naar pagina 2</a>
	</p>
</body>
</html>

Zoals je ziet een doodnormale php pagina. Bovenin wordt 'authenticatie.php' geinclude (of eigenlijk gerequired). De eigenlijke code van de pagina's die je wilt beveilingen maakt niet uit; zolang je maar 'authenticatie.php' aan de bovenkant include. Laten we gaan kijken naar authenticatie.php, want daar gebeurt het eigenlijke werk!
Pagina 4

Authenticatie

Op authenticatie.php hebben we de volgende logische structuur:

1) we includen de gegevens die nodig zijn voor connecten met de database (db_config.php)

2) we declareren twee functies:
to_login (), die de user terugstuurt naar de loginpagina als er iets fout gaat, en
check_login ($username, $password), die lijkt mij voor zichzelf spreekt. 'check_login' zet een sessievariabele met het user_id, wat handig is als je gegevens uit je DB wilt halen over degene die ingelogd is, en een sessievariabele die 'logged_in' heet met de waarde true als het inloggen goed is gegaan.

3) Het eerste dat we hierna controleren, is of $_SESSION['logged_in'] gezet is. Als dat wel zo is, is er al eerder succesvol ingelogd en mag de pagina getoond worden. Als dat niet zo is, zijn er twee mogelijkheden:
OF iemand vraagt de pagina rechtstreeks op in zijn browser zonder eerst netjes in te loggen (is dus niet toegestaan), OF iemand probeert keurig in te loggen vanaf de inlogpagina. In het laatste geval moeten we dus checken of $_POST['username'] en $_POST['password'] gezet zijn en, zo ja, met check_login () controleren of ze juist zijn.

Hieronder de hele code van authenticatie.php:

<?php
	session_start ();

	// de gegevens voor de DB connectie
	require '../../db_config.php';
	
	// terug naar de loginpagina. Met sleep bouwen we een pauze in tegen brute-forcen
	function to_login () {
		sleep (3);
		header ('Location: http://www.jouwsite.nl/login/');  // NIET VERGETEN AAN TE PASSEN!!!
	}
	
	function check_login ($username, $password) {
		// query opstellen
		// je tabel kan er heel simpel uitzien:
		// id INT(11) auto_increment
		// username VARCHAR 64
		// password VARCHAR 64. Het password sla je op in je DB met een SHA1 hash
		// daarom staat SHA1 dus ook in de query
		$sql = "
			SELECT id
			FROM members
			WHERE username = '" . mysql_real_escape_string ($username) . "'
			AND password = SHA1('" . mysql_real_escape_string ($password) . "')
			";
		if ($res = mysql_query ($sql)) {
			if (mysql_num_rows ($res) == 1) {
				// de query is gelukt en we hebben 1 resultaat
				$row = mysql_fetch_assoc ($res);
				$_SESSION['id'] = $row['id'];
				$_SESSION['logged_in'] = true;
			}
			else {
				to_login ();
			}
		}
		else {
			to_login ();
		}
	}
	
	// eerst maar eens kijken of $_SESSION['logged_in'] bestaat
	if (!isset ($_SESSION['logged_in'])) {
		// nog niet eerder ingelogd, maar misschien heeft iemand net het loginformulier ingevuld?
		if (isset ($_POST['username'], $_POST['password'])) {
			check_login ($_POST['username'], $_POST['password']);
		}
		else {
			to_login ();
		}
	}
?>
Pagina 5

Tot slot

Dit script is een opzetje. Je kunt het zelf uitbreiden, maar de minimale ingrediënten zijn in ieder geval aanwezig. Wat je bijvoorbeeld zou kunnen doen, is de combinate van username en password in je DB UNIQUE maken zodat je geen verdubbelingen in je tabel kunt krijgen.

Zoals altijd zijn opmerkingen en suggesties welkom.
Happy PHP-ing

Jan Koehoorn
www.jankoehoorn.nl

------------------------------------------------------------
TODO list n.a.v. de tips en opmerkingen
(rood gekleurde onderdelen zijn nog niet gedaan)
  • [li][color=red]anti session hijacking code invoegen[/color][/li]
    [li][color=red]sleep () vervangen door betere anti brute-force maatregel[/color][/li]
    [li]bescherming tegen mysql injection toevoegen[/li]

------------------------------------------------------------
ChangeLog:
2006-08-22: Anti MySQL-injection code toegevoegd
2006-08-23: Pagina 2: uitlogmogelijkheid beter uitgelegd

Reacties

0
Nog geen reacties.