Neu: inc/security.inc.php, eingebunden von inc/config.inc.php. CSRF Der Adminbereich hatte keinerlei Schutz gegen fremde POSTs. Jetzt haengt csrf_inject_output() als Ausgabefilter das Token an jedes POST-Formular und csrf_require() weist POSTs ohne gueltiges Token ab. Beides wird nur fuer Skripte unterhalb von admin/ aktiviert, der oeffentliche Bereich bleibt unveraendert. Der Umweg ueber den Ausgabefilter erspart es, die rund 60 bestehenden Formulare einzeln anzufassen; der AJAX-Aufruf auf mailtemplate.php schickt das Token als Feld mit. Session-Cookie-Flags config.inc.php setzt secure, httponly und samesite - aber 25 Dateien in admin/ und intern/ riefen session_start() vor dem Include auf, womit die Parameter wirkungslos waren. Die vorgezogenen Aufrufe sind entfernt, config.inc.php startet die Sitzung nur noch, wenn keine laeuft. admin/logout.php musste umgestellt werden, weil dort session_destroy() vor dem Include stand; die Cookies werden jetzt mit denselben Parametern geloescht, mit denen sie gesetzt wurden. Brute-Force Nach 5 Fehlversuchen je Konto oder 20 je IP ist die Anmeldung 15 Minuten gesperrt, gezaehlt in der neuen Tabelle login_attempts. Fehlt die Tabelle, laeuft der Login wie bisher - gleiche Vorgehensweise wie bei securitytokensHatAblaufspalte(). Eine erfolgreiche Anmeldung raeumt die Fehlversuche des Kontos ab. Passwort vergessen admin/passwortvergessen.php uebergab $mail und $body an SendMailMessage(); beide Variablen gibt es dort nicht, sie heissen $empfaenger und $text. Die Mail ging deshalb nie raus, obwohl der Reset-Code gesetzt wurde. Getestet gegen einen lokalen PHP-Server: Token wird eingesetzt, POST ohne Token liefert 403, mit Token laeuft der Login normal, der sechste Fehlversuch wird gesperrt, das Sitzungscookie traegt secure/HttpOnly/ SameSite. Oeffentliche Seiten sind unveraendert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
97 lines
2.9 KiB
PHP
97 lines
2.9 KiB
PHP
<?php
|
|
require_once(__DIR__ . "/../inc/config.inc.php");
|
|
require_once(__DIR__ . "/../inc/functions.inc.php");
|
|
|
|
|
|
if(!isset($_GET['userid']) || !isset($_GET['code'])) {
|
|
error("Leider wurde beim Aufruf dieser Website kein Code übermittelt");
|
|
}
|
|
|
|
|
|
|
|
$showForm = true;
|
|
$userid = $_GET['userid'];
|
|
$code = $_GET['code'];
|
|
|
|
//Abfrage des Nutzers
|
|
$statement = $pdo->prepare("SELECT *, (passwortcode_time IS NOT NULL AND passwortcode_time > DATE_SUB(NOW(), INTERVAL 24 HOUR)) AS code_noch_gueltig FROM intern_users WHERE id = :userid");
|
|
$result = $statement->execute(array('userid' => $userid));
|
|
$user = $statement->fetch();
|
|
|
|
//Überprüfe dass ein Nutzer gefunden wurde und dieser auch ein Authentifizierungscode angefordert hat
|
|
// fetch() liefert false und nicht null, wenn kein Benutzer gefunden wird -
|
|
// die alte Bedingung griff daher nie und lief mit false in den Array-Zugriff.
|
|
if(!$user || $user['passwortcode'] === null) {
|
|
error("Der Benutzer wurde nicht gefunden oder hat kein ein Authentifizierungscode angefordert.");
|
|
$showForm = false;
|
|
}
|
|
|
|
// Ablauf in SQL pruefen: passwortcode_time wird mit NOW() gesetzt (Uhr des
|
|
// DB-Servers), waehrend PHP auf dem Webserver in einer anderen Zeitzone
|
|
// laeuft. strtotime()/time() lag hier um genau diese Differenz daneben.
|
|
if(!$user['code_noch_gueltig']) {
|
|
error("Dein Code ist leider abgelaufen. Bitte benutze die Authentifizierungsfunktion erneut.");
|
|
$showForm = false;
|
|
}
|
|
|
|
|
|
//Überprüfe den Passwortcode
|
|
if(sha1($code) != $user['passwortcode']) {
|
|
error("Der übergebene Code war ungültig. Stell sicher, dass du den genauen Link in der URL aufgerufen hast. Solltest du mehrmals die Authentifizierungsfunktion genutzt haben, so ruf den Link in der neuesten E-Mail auf.");
|
|
$showForm = false;
|
|
}
|
|
|
|
|
|
include("templates/header.inc.php");
|
|
?>
|
|
|
|
<div class="container small-container-500">
|
|
|
|
<h1>Authentifizierung</h1>
|
|
<?php
|
|
|
|
if(check_mailreg()){
|
|
echo "<p>Ihr Benutzerkonto ist schon authentifiziert.</p>";
|
|
|
|
}elseif($showForm){
|
|
|
|
// (hier stand ein password_hash() auf einer nie gesetzten Variablen, dessen
|
|
// Ergebnis niemand benutzt hat - es warf nur eine Deprecation-Warnung)
|
|
$statement = $pdo->prepare("UPDATE intern_users SET mailreg='1', passwortcode = NULL, passwortcode_time = NULL WHERE id = :userid");
|
|
$result = $statement->execute(array('userid'=> $userid ));
|
|
|
|
if($result) {
|
|
$msg = "<p>Die Authentifizierung Ihre Kontos wurde erfolgreich abgeschlossen.</p>";
|
|
$showForm = false;
|
|
}
|
|
|
|
}else{
|
|
echo "<p>Die Authentifizierungsfunktion steht mit den angegebenen Daten nicht zur Verfügung.</p>";
|
|
|
|
}
|
|
|
|
if(isset($msg)) {
|
|
echo $msg;
|
|
}
|
|
|
|
|
|
echo "<br><br>";
|
|
echo "<br><br>";
|
|
echo "<form action='index.php' method=POST>";
|
|
echo "<input name=aktion type=hidden value=1>";
|
|
echo "<input type=submit class='btn btn-primary' value='Zurück'><br>";
|
|
echo '</form>';
|
|
|
|
echo "<br><br>";
|
|
echo "<br><br>";
|
|
echo "<br><br>";
|
|
echo "<br><br>";
|
|
|
|
?>
|
|
|
|
</div> <!-- /container -->
|
|
|
|
|
|
<?php
|
|
include("templates/footer.inc.php")
|
|
?>
|