Dienstag, 17. März 2015

PHP Loginsystem

Anmerkung: Die hier vorgestellte Methode ist unsicher und veraltet. Ich habe sie als leichten Einstieg gewählt, für richtige Anwendungen sollte die Klasse mysqli verwendet werden. Dieses Loginsystem in der sicheren Variante findet ihr hier.

Im heutigen Post möchte ich das erlernte der vorigen Posts verbinden und eine etwas größere Anwendung, nämlich ein Loginsysten in PHP, vorstellen. Die Benutzer werden mit Benutzername und Password in einer Tabelle einer MySQL Datenbank gespeichert.
Es gibt 2 PHP Funktionen, eine registriert einen neuen Benutzer. Hierfür fragt sie die Datenbank ab, ob es bereits einen Nutzer mit dem gleichen Namen gibt. Falls nicht, wird ein neuer Eintrag in der Tabelle angelegt. Die andere loggt einen Benutzer ein und startet dann eine PHP Session.
Wichtig ist es, dass Passwort nicht im Klartext in der Datenbank zu speichern, sondern dieses zu hashen. Aus dem Passwort wird also mittels einer Einweghashfunktion eine Zeichenkette generiert, welche nicht wieder zurück in das Passwort überführt werden kann. Beim Loginversuch wird das eingegebene Passwort wieder gehasht und die beiden Hashes verglichen. Dies hat mehrere Gründe: Zum einen wären bei der Klartextspeicherung sofort alle Passwörter für jeden sichtbar, falls dieser sich unbefugt Zugriff zur Datenbank verschafft. Zum anderen kann der Admin bei der Klartextspeicherung jederzeit alle Passwörter lesen, was nicht gut ist. Denn Nutzer benutzen erfahrungsgemäß Standardpasswörter, sodass sich der Admin dann z.B. u.U. in den Emailaccount des Nutzers einloggen könnte.
Zum Hashen des Passworts benutzen wir einfach die PHP Funktion md5() (welche einen MD5 Hash erzeugt). Dies ist nicht mehr die empfohlene Variante, wer also sicher gehen möchte sollte sich über die aktuellen Standards informieren, hier soll md5() aber zum Verständnis reichen.
Wir benutzen wieder die db4free Datenbank aus einem vorigen Post, ich habe dort die Tabelle Users hinzugefügt. Diese hat 2 Spalten, eine ist der String Benutzername, der andere der String Passwort. Zu beachten ist die Länge dieses, md5() erzeugt aus jeder Eingabe einen 32 Zeichen langen String.
Die Funktion register.php (http://bloggeroliver.bplaced.net/PHPExamples/Login/register.php) sieht folgendermaßen aus, und sollte mit den Erklärungen aus den vorigen Posts leicht verständlich sein:

<?php
mysql_connect ("db4free.net", "csharptricks", "12345678");
mysql_select_db("csharptricks");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$checkuser = mysql_query("SELECT username FROM Users WHERE username = '$username'");
$num_rows = mysql_num_rows($checkuser);

if ($num_rows > 0) {
     echo "Existing";
}
else {
     mysql_query("INSERT INTO Users (username, password) VALUES ('$username', '$hashedpw');");
     echo "Success";
}
?>

Gleiches gilt für die Datei login.php (http://bloggeroliver.bplaced.net/PHPExamples/Login/login.php):

<?php
session_start();

mysql_connect ("db4free.net", "csharptricks", "12345678");
mysql_select_db("csharptricks");

$username = $_POST["username"];
$password = $_POST["password"];
$hashedpw = md5($password);

$result = mysql_query("SELECT username, password FROM Users WHERE username = '$username' LIMIT 1");
$row = mysql_fetch_object($result);
    
if($row->password == $hashedpw) {
    $_SESSION["username"] = $username;
    echo "LoginGood";
}
else {
    echo "LoginBad";
}
?>

Wie man sieht, wird bei Übereinstimmung der Hashes der Benutzer quasi eingeloggt, in dem eine Session gestartet wird und die Variable username auf den Benutzernamen festgelegt wird.
In einer Funktion, welche nur nach dem Login verfügbar sein soll, könnte dann zum Beispiel die folgende Abfrage dieses garantieren, ist der Benutzer nicht eingeloggt wird das Skript beendet:

session_start();

if(!isset($_SESSION['username'])) { 
echo "Login first.";
exit; 
}

Eine C# Anwendung, welche das Loginsystem benutzt, könnte wie folgt aussehen:
Es gibt 2 Textboxen, die erste enthält den Benutzernamen, die zweite das Passwort. In diesem sollte die Eigenschaft PasswordChar gesetzt sein, z.B. auf *, so wird das Passwort nicht sichtbar.
Es gibt weiter 2 Buttons, ein erster zur Registrierung, ein zweiter zum Login. Der Code ist der folgende, auch er setzt Kenntnis der vorigen Posts voraus:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Net;
using System.IO;

namespace LoginSystem
{
    public partial class Form1 : Form
    {
        CookieContainer Cookie = null;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Cookie = new CookieContainer(); // important
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(PerformRequest("http://bloggeroliver.bplaced.net/PHPExamples/Login/register.php", textBox1.Text, textBox2.Text));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(PerformRequest("http://bloggeroliver.bplaced.net/PHPExamples/Login/login.php", textBox1.Text, textBox2.Text));
        }

        private string PerformRequest(string url, string username, string password)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.CookieContainer = Cookie; // use the global cookie variable
            string postData = "username=" + username + "&password=" + password;
            byte[] data = Encoding.UTF8.GetBytes(postData);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
            request.ContentLength = data.Length;

            using (Stream stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            WebResponse response = (HttpWebResponse)request.GetResponse();
            return new StreamReader(response.GetResponseStream()).ReadToEnd();
        }
    }
}

Keine Kommentare:

Kommentar veröffentlichen