Author: Progman, zuletzt bearbeitet von sica @ 2005-06-03 14:29:23
Bitte beachten Sie, dass die Tutorialkapitel zusammenhängen. Wenn sie direkt auf ein Kapitel verlinkt wurden müssen Sie gegebenenfalls die vorherigen Kapitel auch lesen. Achten Sie beim lesen darauf, dass Sie kein Kapitel überspringen.
Eigenes Loginsystem
- Aufbau des Login-Scripts
- Layout der MySQL Tabellen
- Das eigentliche Script
- ID und IP in die Session speichern
- Sicherheit
- Testlink hinzufügen
- MD5-Checksumme
1. Aufbau des Login-Scripts
Wir schreiben nun ein einfaches Login-Script, basierent auf MySQL mit Verwendung von Sessions. Dieses Login-Script können wir dann in unserem 3 Spalten Layout includen. Die andere Möglichkeit ist es, alle Adminscripte in einem extra Verzeichnis zu packen und dieses mit einer .htaccess Datei nur für Admins zugänglich machen. Wie das geht kann man auf SelfHTML unter .htaccess - Server-Reaktionen kontrollieren nachlesen.
2. Layout der MySQL Tabellen
Wir müssen uns erstmal überlegen, was wir alles über einen Admin speichern wollen. Da gehört auf jedenfall der Name des Admins. Dann vielleicht noch die Emailadresse. Damit der Admin sich auch anmelden kann, braucht er ein Password. Dies sind dann 4 Spalten für die MySQL Tabelle. Die vierte Spalte, die hier nicht aufgeführt ist, ist die ID, die für jeden User angelegt wird. Der MySQL für die Tabelle sieht dann so aus.
CREATE TABLE users (
ID TINYINT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(20),
Password CHAR(32),
Email VARCHAR(60)
);
Die Spalte Password wurde auf den Spaltentyp CHAR(32) gesetzt. Dies ist bewusst, denn dort wird ein genau 32 Zeichen langer String gespeichert. Dieser 32 Zeichen langer String ist eine Einweg-Verschlüsselung des Passwords. Dieser String wird mit dem md5 Algorithmus erstellt. PHP und MySQL haben beide eine Funktion md5 bzw. MD5(). Der Rückgabewert der Funktion wird dann in diese Zelle gespeichert. Aus dieser sogenannten Prüfsumme kann das Ursprungspassword nicht wieder hergestellt werden. Wie wir uns aber trotzdem einloggen wird später erklärt.
3. Das eigentliche Script
Da wir dieses Script Session basiert sein soll müssen wie auch eine Session starten. Da diese nur am Anfang gemacht werden kann müssen wir die index.php Datei etwas anpassen. Wir planen, dass wir den Adminbereich mit index.php?section=admin öffnen. Da wir nur da eine Session brauchen, sollten wir auch die Session nur dann starten wenn der entsprechende GET-Parameter übergeben wurde.
<?php
error_reporting(E_ALL);
include "inc/config.php"; // die Konfigurationsdateien lesen.
// Session starten wenn ?section=admin geöffnet wurde
if(isset($_GET['section']) AND ("admin" == $_GET['section'])) {
session_start();
}
if(get_magic_quotes_gpc()) {
array_stripslashes($_GET);
array_stripslashes($_POST);
array_stripslashes($_COOKIE);
}
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n";
echo " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
echo "<html>\n";
echo " <head>\n";
echo " <title>Meine Seite</title>\n";
echo " <link rel=\"stylesheet\" type=\"text/css\" href=\"page.css\" />\n";
echo " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\" />\n";
echo " </head>\n";
echo " <body>\n";
echo " <div id=\"root\">\n"; // ganz oberer Div-Holder
echo " <div id=\"banner\">\n"; // banner
include "banner.php";
echo " </div>\n";
echo " <div id=\"links\">\n"; // linkes Menu
include "menu.php";
echo " </div>\n";
echo " <div id=\"mitte\">\n"; // In der Mitte der Inhalt
include "inhalt.php";
echo " </div>\n";
echo " <br style=\"clear:both;\" />\n"; // css-float beenden
echo " </div>\n";
echo " </body>\n";
echo "</html>\n";
?>
Achtung: Ich kriege oft Feedback via Email und IRC das PHP die Funktion array_stripslashes nicht findet. Diese Funktion ist keine interne Funktion von PHP. Doch leider öffnen viele User dieses Kapitel direkt, ohne die Kapitel davor zu lesen. Selbst der Hinweis auf jeder Seite das diese Tutorial von anfang an gelesen wird, wird ignoriert. Wenn sie also die Fehlermeldung Undefined function: array_stripslashes oder ähnlich bekommen, haben sie das Tutorial nicht von Anfang an gelesen. Es ist sehr wichtig, dass sie das Tutorial von Anfang an lesen. Siehe dazu den Infotext auf der Index-Seite und die Kopfzeile über jeden Kapitel.
Nun kommt die admin.php Datei. Als Vorüberlegung sagen wir, dass wir jeden Bereich, den wir bearbeiten wollen, mit dem GET-Parameter site erreichen. Die Übersicht erreichen wir mit index.php?section=admin. Ausloggen werden wir uns dann mit index.php?section=admin&action=logout Die grobe admin.php Datei sieht so aus.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
// Sachen zum ausloggen erzeugen
} else {
// der Adminbereich und ggf.
// Das login Formular
}
?>
In der If-Abfrage kommt ein Programmteil der die aktuelle Session beendet.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
// der Adminbereich und ggf.
// Das login Formular
}
?>
Wir schreiben nun unser Formular. Der Benutzer soll dann seinen Namen über eine Dropdown Liste auswählen. Die Dropdown-Liste erstellen wir dynamisch, je nachdem welche User in der Datenbank gespeichert sind.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
// der Adminbereich und ggf.
// Das login Formular
echo "<form action=\"index.php?section=admin\" method=\"post\" class=\"formular\">\n";
echo " <p>\n";
echo " Adminbereich\n";
echo " </p>\n";
echo " <ol>\n";
echo " <li>\n";
echo " <label for=\"name\">Name</label>\n";
$sql = "SELECT
ID,
Name
FROM
users
ORDER BY
Name ASC;";
$result = mysql_query($sql) OR die(mysql_error());
echo " <select size=\"1\" name=\"UserID\" id=\"name\">\n";
echo " <option value=\"0\" selected=\"selected\">Bitte wählen</option>\n";
while($row = mysql_fetch_assoc($result)) {
echo "<option value=\"".$row['ID']."\">".$row['Name']."</option>\n";
}
echo " </select>\n";
echo " </li>\n";
echo " <li>\n";
echo " <label for=\"password\">Password</label>\n";
echo " <input type=\"password\" name=\"Password\" id=\"password\" />\n";
echo " </li>\n";
echo " <li>\n";
echo " <input type=\"submit\" name=\"submit\" value=\"Speichern\" />\n";
echo " <input type=\"reset\" name=\"submit\" value=\"Zurücksetzen\" />\n";
echo " </li>\n";
echo " </ol>\n";
echo "</form>\n";
}
?>
Der User kann dann sein Namen auswählen und gibt dann sein Password ein. Da das Formular an die Seite index.php?section=admin geschickt wird, dort aber unser Login-Formular ist, müssen wir eine If-Abfrage schreiben die das Formular nur dann anzeigt, wenn man nicht eingeloggt ist oder das eingegeben Password falsch ist. Dafür schreiben wir eine extra Funktion die uns sagt ob die übergebenen Logindaten stimmen.
<?php
function login_right($id, $pass)
{
$sql = "SELECT
COUNT(*) as Anzahl
FROM
users
WHERE
ID = '".$id."' AND
Password = MD5('".$pass."');";
$result = mysql_query($sql) OR die(mysql_error());
$row = mysql_fetch_assoc($result);
mysql_free_result($result);
return $row['Anzahl'];
}
?>
Diese Funktion fügen wir dann in unsere functions.php Datei hinzu. In der admin.php können wir dann diese Funktion benutzen.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID']) AND '0' == $_POST['UserID']) {
echo "<p class=\"error\">\n";
echo " Bitte wählen sie einen Benutzernamen aus.\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID'], $_POST['Password']) AND
login_right(addslashes($_POST['UserID']),
addslashes($_POST['Password']))) {
echo "<p>\n";
echo "Willkommen im Adminbereich\n";
echo "</p>\n";
} else {
echo "<form action=\"index.php?section=admin\" method=\"post\" class=\"formular\">\n";
echo " <p>\n";
echo " Adminbereich\n";
echo " </p>\n";
echo " <ol>\n";
echo " <li>\n";
echo " <label for=\"name\">Name</label>\n";
$sql = "SELECT
ID,
Name
FROM
users
ORDER BY
Name ASC;";
$result = mysql_query($sql) OR die(mysql_error());
echo " <select size=\"1\" name=\"UserID\" id=\"name\">\n";
echo " <option value=\"0\" selected=\"selected\">Bitte wählen</option>\n";
while($row = mysql_fetch_assoc($result)) {
echo "<option value=\"".$row['ID']."\">".$row['Name']."</option>\n";
}
echo " </select>\n";
echo " </li>\n";
echo " <li>\n";
echo " <label for=\"password\">Password</label>\n";
echo " <input type=\"password\" name=\"Password\" id=\"password\" />\n";
echo " </li>\n";
echo " <li>\n";
echo " <input type=\"submit\" name=\"submit\" value=\"Speichern\" />\n";
echo " <input type=\"reset\" name=\"submit\" value=\"Zurücksetzen\" />\n";
echo " </li>\n";
echo " </ol>\n";
echo "</form>\n";
}
}
}
?>
So weit so gut. Doch da ist ja noch nix Session-mäßiges vorhanden. Wenn wir nun im Adminbereich einen Link wie index.php?section=admin&site=news&action=add hätten, werden wir ein Problem haben. Die Seite wird ja dann neu aufgerufen, doch die beiden POST-Variablen UserID und Password fehlen. Das hat zu folge, dass wir sofort beim nächsten Klick ausgeloggt sind und wir wieder das Formular sehen. Und jetzt kommt der Trick mit der Session.
4. ID und IP in die Session speichern
Nach dem einloggen speichern wir die ID des Users in die Session. Dies geht ganz einfacher mit einer Zuweisung.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID']) AND '0' == $_POST['UserID']) {
echo "<p class=\"error\">\n";
echo " Bitte wählen sie einen Benutzernamen aus.\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID'], $_POST['Password']) AND
login_right(addslashes($_POST['UserID']),
addslashes($_POST['Password']))) {
if(!isset($_SESSION['ID'])) {
// wenn die ID noch nicht gespeichert wurde,
// jetzt speichern
$_SESSION['ID'] = $_POST['UserID'];
}
echo "<p>\n";
echo "Willkommen im Adminbereich\n";
echo "</p>\n";
} else {
echo "<form action=\"index.php?section=admin\" method=\"post\" class=\"formular\">\n";
echo " <p>\n";
echo " Adminbereich\n";
echo " </p>\n";
echo " <ol>\n";
echo " <li>\n";
echo " <label for=\"name\">Name</label>\n";
$sql = "SELECT
ID,
Name
FROM
users
ORDER BY
Name ASC;";
$result = mysql_query($sql) OR die(mysql_error());
echo " <select size=\"1\" name=\"UserID\" id=\"name\">\n";
echo " <option value=\"0\" selected=\"selected\">Bitte wählen</option>\n";
while($row = mysql_fetch_assoc($result)) {
echo "<option value=\"".$row['ID']."\">".$row['Name']."</option>\n";
}
echo " </select>\n";
echo " </li>\n";
echo " <li>\n";
echo " <label for=\"password\">Password</label>\n";
echo " <input type=\"password\" name=\"Password\" id=\"password\" />\n";
echo " </li>\n";
echo " <li>\n";
echo " <input type=\"submit\" name=\"submit\" value=\"Speichern\" />\n";
echo " <input type=\"reset\" name=\"submit\" value=\"Zurücksetzen\" />\n";
echo " </li>\n";
echo " </ol>\n";
echo "</form>\n";
}
}
}
?>
Um Missverständnisse zu vermeiden: Das Arrayelement $_SESSION['ID'] enthält die User-ID des Benutzers, der sich eingeloggt hat, nicht die Session-ID der Session, die man mit session_id abfragen kann. Die Session-ID und $_SESSION['ID'] sind zwei verschiedene Dinge.
In der If-Abfrage brauchen wir dann nicht mehr die POST-Parameter überprüfen, sondern gucken nur ob es ein Arrayelement $_SESSION['ID'] gibt.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID']) AND '0' == $_POST['UserID']) {
echo "<p class=\"error\">\n";
echo " Bitte wählen sie einen Benutzernamen aus.\n";
echo "</p>\n";
} else {
if(isset($_SESSION['ID']) OR
(isset($_POST['UserID'], $_POST['Password']) AND
login_right(addslashes($_POST['UserID']),
addslashes($_POST['Password'])))) {
if(!isset($_SESSION['ID'])) {
// wenn die ID noch nicht gespeichert wurde,
// jetzt speichern
$_SESSION['ID'] = $_POST['UserID'];
}
echo "<p>\n";
echo "Willkommen im Adminbereich\n";
echo "</p>\n";
} else {
echo "<form action=\"index.php?section=admin\" method=\"post\" class=\"formular\">\n";
echo " <p>\n";
echo " Adminbereich\n";
echo " </p>\n";
echo " <ol>\n";
echo " <li>\n";
echo " <label for=\"name\">Name</label>\n";
$sql = "SELECT
ID,
Name
FROM
users
ORDER BY
Name ASC;";
$result = mysql_query($sql) OR die(mysql_error());
echo " <select size=\"1\" name=\"UserID\" id=\"name\">\n";
echo " <option value=\"0\" selected=\"selected\">Bitte wählen</option>\n";
while($row = mysql_fetch_assoc($result)) {
echo "<option value=\"".$row['ID']."\">".$row['Name']."</option>\n";
}
echo " </select>\n";
echo " </li>\n";
echo " <li>\n";
echo " <label for=\"password\">Password</label>\n";
echo " <input type=\"password\" name=\"Password\" id=\"password\" />\n";
echo " </li>\n";
echo " <li>\n";
echo " <input type=\"submit\" name=\"submit\" value=\"Speichern\" />\n";
echo " <input type=\"reset\" name=\"submit\" value=\"Zurücksetzen\" />\n";
echo " </li>\n";
echo " </ol>\n";
echo "</form>\n";
}
}
}
?>
Man ist eingeloggt, wenn entweder die ID schon in der Session gespeicher wurde oder die Logindaten aus dem Formular stimmen. Wir können dieses Script etwas umschreiben. Wir gucken erst nach ob die Formulardaten stimmen und speichern dann die ID in die Session. In einer separaten If-Abfrage gucken wir dann ob eine ID in der Session gespeichert wurde.
<?php
// die admin.php
if(isset($_GET['action']) AND ("logout" == $_GET['action'])) {
session_destroy();
echo "<p>\n";
echo " Sie haben sich ausgeloggt. Um wieder in den Adminbereich\n";
echo " zu kommen müssen sie sich wieder Einloggen\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID']) AND '0' == $_POST['UserID']) {
echo "<p>\n";
echo " Bitte wählen sie einen Benutzernamen aus.\n";
echo "</p>\n";
} else {
if(isset($_POST['UserID'], $_POST['Password']) AND
login_right(addslashes($_POST['UserID']),
addslashes($_POST['Password']))) {
$_SESSION['ID'] = $_POST['UserID'];
}
if(isset($_SESSION['ID'])) {
echo "<p>\n";
echo "Willkommen im Adminbereich\n";
echo "</p>\n";
} else {
if(isset($_POST['submit'])) {
// Der Submit-Button wurde gedrückt
// aber der Login ist falsch. Deshalb
// erstellen wir eine Fehlermeldung
echo "<p class=\"error\">\n";
echo " Ungültiges Password.\n";
echo "</p>\n";
}
echo "<form action=\"index.php?section=admin\" method=\"post\" class=\"formular\">\n";
echo " <p>\n";
echo " Adminbereich\n";
echo " </p>\n";
echo " <ol>\n";
echo " <li>\n";
echo " <label for=\"name\">Name</label>\n";
$sql = "SELECT
ID,
Name
FROM
users
ORDER BY
Name ASC;";
$result = mysql_query($sql) OR die(mysql_error());
echo " <select size=\"1\" name=\"UserID\" id=\"name\">\n";
echo " <option value=\"0\" selected=\"selected\">Bitte wählen</option>\n";
while($row = mysql_fetch_assoc($result)) {
echo "<option value=\"".$row['ID']."\">".$row['Name']."</option>\n";
}
echo " </select>\n";
echo " </li>\n";
echo " <li>\n";
echo " <label for=\"password\">Password</label>\n";
echo " <input type=\"password\" name=\"Password\" id=\"password\" />\n";
echo " </li>\n";
echo " <li>\n";
echo " <input type=\"submit\" name=\"submit\" value=\"Speichern\" />\n";
echo " <input type=\"reset\" name=\"submit\" value=\"Zurücksetzen\" />\n";
echo " </li>\n";
echo " </ol>\n";
echo "</form>\n";
}
}
}
?>
Wenn nun die Logindaten stimmen, wird die ID in der Session gespeichert. Wenn man die falschen Daten angegeben hat, wird die If-Abfrage, ob es ein Arrayelement ID in der Session gibt, den else-Teil ausführen. Wenn nun im Adminbereich ein Link angeklickt wird, wird dann die alte Session geladen, vorrausgesetzt die Session-ID wurde mit GET, POST oder mit Cookies übertragen. Dann ist das Arrayelement ID in der Session vorhanden und man ist weiterhin eingeloggt.
5. Sicherheit
Das PHP-Script erkennt nun den User nur noch daran ob in der Session das Arrayelement ID vorhanden ist, in der die ID des Users steht. Wenn nun aber jemand anders irgendwie die Session-ID bekommt, kann er mit dieser Session-ID in den Adminbereich. Deswegen müssen wir eine Sicherheitssperre einbauen. Ich nehme jetzt als Sicherheitsmerkmal die IP des Users. Diese wird einmalig in die Session gespeichert und zwar unter dem session_start in der index.php.
<?php
error_reporting(E_ALL);
include "inc/config.php"; // die Konfigurationsdateien lesen.
@mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS) OR die(mysql_error());
mysql_select_db(MYSQL_DATABASE) OR die(mysql_error());
// Session starten wenn ?section=admin geöffnet wurde
if(isset($_GET['section']) AND ("admin" == $_GET['section'])) {
session_start();
if(!isset($_SESSION['IP'])) {
$_SESSION['IP'] = $_SERVER['REMOTE_ADDR'];
}
if($_SESSION['IP'] != $_SERVER['REMOTE_ADDR']) {
echo "<p class=\"error\">\n";
echo " Sie dürfen nicht die Session von einem\n";
echo " anderen user Benutzten. Bitte benutzen sie\n";
echo " folgenden Link um zur Homepage zu gelangen.\n"