Security: verwijder hardcoded wachtwoord, voeg random-wachtwoord-generator toe bij eerste installatie

This commit is contained in:
2026-08-27 08:57:05 +00:00
parent 6485f693dc
commit 74612aefbb
55 changed files with 6019 additions and 876 deletions
+115 -6
View File
@@ -1,14 +1,57 @@
<?php
/**
* Logging en analyse van inkomende requests.
*
* Biedt functionaliteit om inkomende requests naar een logbestand te
* schrijven, IP-adressen te anonimiseren, IP's tegen lijsten/CIDR-ranges
* te matchen, het client-IP te bepalen en bezoekerstype (bot/mens/AI)
* te detecteren.
*
* @since 2.6.4
*/
class RequestLogger
{
/**
* Pad naar het logbestand.
*
* @since 2.6.4
* @var string
*/
private string $logFile;
/**
* Maak een nieuwe RequestLogger aan.
*
* @since 2.6.4
*
* @param string $logFile Pad naar het te gebruiken logbestand.
*/
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
/**
* Schrijf een request-regel naar het logbestand.
*
* De gebruikersagent en referrer worden ontdaan van controle-tekens en
* afgekapt tot 500 tekens. De landcode wordt alleen opgenomen wanneer
* deze exact twee tekens lang is. De mapstructuur wordt automatisch
* aangemaakt indien deze nog niet bestaat.
*
* @since 2.6.4
*
* @param string $page Opgevraagde pagina of route.
* @param string $ip Client IP-adres.
* @param string $userAgent HTTP User-Agent header.
* @param string $referrer HTTP Referer header.
* @param string $host Hostnaam van het request.
* @param string $acceptLanguage HTTP Accept-Language header.
* @param string $status Statusaanduiding van het request. Default 'ok'.
* @param string|null $country Tweeletterige landcode of null. Default null.
* @return void
*/
public function log(string $page, string $ip, string $userAgent, string $referrer, string $host, string $acceptLanguage, string $status = 'ok', ?string $country = null): void
{
$dir = dirname($this->logFile);
@@ -25,7 +68,16 @@ class RequestLogger
}
/**
* Mask the last octet (IPv4) or last block (IPv6) of an IP address
* Anonimiseer het laatste deel van een IP-adres.
*
* Voor IPv4 wordt het laatste octet vervangen door 'x'. Voor IPv6 worden
* alleen de eerste vier blokken behouden en de rest vervangen door '::x'.
* Ongeldige adressen worden ongewijzigd teruggegeven.
*
* @since 2.6.4
*
* @param string $ip Het te anonimiseren IP-adres.
* @return string Het geanonimiseerde IP-adres of het origineel bij een ongeldig adres.
*/
public static function anonymizeIp(string $ip): string
{
@@ -45,13 +97,17 @@ class RequestLogger
}
/**
* Check whether an IP matches any entry in a list of IPs/CIDR ranges.
* Controleer of een IP overeenkomt met een entry uit een lijst van IP's of CIDR-ranges.
*
* Supports exact IPv4/IPv6 addresses and CIDR notation (e.g. 192.168.0.0/16).
* Ondersteunt exacte IPv4/IPv6-adressen en CIDR-notatie (bijv. 192.168.0.0/16).
* Bij een prefix-lengte van 0 komt ieder IP van dezelfde adresfamilie overeen.
* Adresfamilies (IPv4 vs IPv6) worden niet onderling gemengd.
*
* @param string $ip The client IP to test
* @param array $list List of IPs and/or CIDR ranges
* @return bool True if the IP matches any entry
* @since 2.6.4
*
* @param string $ip Het te testen client IP-adres.
* @param string[] $list Lijst van IP-adressen en/of CIDR-ranges.
* @return bool True wanneer het IP met een entry overeenkomt.
*/
public static function ipMatchesList(string $ip, array $list): bool
{
@@ -117,6 +173,18 @@ class RequestLogger
return false;
}
/**
* Bepaal het client IP-adres uit de beschikbare request-headers.
*
* Doorloopt in twee passes een vaste set headers (zoals CF-Connecting-IP,
* X-Real-IP, X-Forwarded-For en REMOTE_ADDR). De eerste pass geeft
* voorrang aan geldige publieke IP's en slaat interne/reserved ranges
* over; de tweede pass is een fallback voor lokale ontwikkelomgevingen.
*
* @since 2.6.4
*
* @return string Het gedetecteerde client IP-adres, of '127.0.0.1' als fallback.
*/
public static function getClientIp(): string
{
$headerKeys = [
@@ -165,6 +233,21 @@ class RequestLogger
return $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}
/**
* Detecteer het type bezoeker aan de hand van de User-Agent.
*
* Wanneer de BotGuard-class beschikbaar is wordt de UA geïdentificeerd
* en een passend label, badge-kleur en Bootstrap-icon teruggegeven
* (ai, search, scraper, generieke bot, lege UA of mens). Bij een
* ingelogde gebruiker wordt de naam aan het label toegevoegd. Zonder
* BotGuard wordt een algemene bezoeker teruggegeven.
*
* @since 2.6.4
*
* @param string $ua De User-Agent string.
* @param string $user Optionele gebruikersnaam; bij 'Gast' of leeg wordt geen naam toegevoegd. Default ''.
* @return array<string,string> Associatieve array met keys: type, label, badge, icon.
*/
public static function detectVisitorInfo(string $ua, string $user = ''): array
{
if (class_exists('BotGuard')) {
@@ -202,6 +285,18 @@ class RequestLogger
return ['type' => 'unknown', 'label' => 'Bezoeker', 'badge' => 'secondary', 'icon' => 'bi-person'];
}
/**
* Detecteer of het huidige request afkomstig is van een bekende bot.
*
* Leest de User-Agent uit de request-headers en identificeert deze via
* BotGuard wanneer beschikbaar. Alleen de categorieën 'ai', 'search'
* en 'scraper' worden als bot beschouwd en als uppercase string
* teruggegeven; anders null.
*
* @since 2.6.4
*
* @return string|null Boticategorie ('AI', 'SEARCH' of 'SCRAPER') of null bij geen bot.
*/
public static function detectBot(): ?string
{
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
@@ -214,6 +309,20 @@ class RequestLogger
return null;
}
/**
* Lees de laatste regels uit het logbestand en parse deze.
*
* De regels worden geparseerd volgens het vaste logformaat en omgezet
* naar associatieve arrays met tijd, IP, gebruiker, bezoeker-info, taal,
* pagina, UA, referrer, status en landcode. Regels waarvan de host leeg
* is of een IP/cli-waarde bevat worden als gebruiker 'Gast' gemarkeerd.
* Het resultaat is nieuwste-regel-eerst.
*
* @since 2.6.4
*
* @param int $lines Aantal te lezen regels vanaf het einde. Default 100.
* @return array<int,array<string,mixed>> Geparseerde logregels, nieuwste eerst. Lege array bij ontbrekend bestand.
*/
public function getLogs(int $lines = 100): array
{
if (!file_exists($this->logFile)) {