security

object

%audit

/phlo/resources/security/audit.phlo
version 1.1
creator q-ai.nl
summary Audit log for model mutations (opt-in via static idColumn/objAudit). Schema: resources/security/audit.sql
package security
frontend false
backend true
requires @MySQL
tags audit log compliance traceability
method

%audit -> log ($model, $action, $before = [], $after = [], $exclude = [])

line 10
Logt wijzigingen die aan een model zijn aangebracht in het auditlogboek, waarbij het type actie, de voor- en na-staten worden vastgelegd en opgegeven velden worden uitgesloten.
$class = is_object($model) ? get_class($model) : (string)$model
$pk = (is_string($class) && class_exists($class) && property_exists($class, 'idColumn')) ? $class::$idColumn : 'id'
$id = is_object($model) ? ($model->$pk ?? $model->id ?? null) : null
if ($id === null) return
$before = (array)$before
$after = (array)$after
foreach ($exclude AS $col) unset($before[$col], $after[$col])
$changes = $action === 'update' ? $this->diff($before, $after) : ($action === 'create' ? $after : $before)
%MySQL->query(
	'INSERT INTO audit_log (ts, user, model, record_id, action, changes, ip) VALUES (?, ?, ?, ?, ?, ?, ?)',
	time(),
	isset(%session->user) ? (int)%session->user : null,
	$class,
	(string)$id,
	$action,
	json_encode($changes, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
	(string)($_SERVER['REMOTE_ADDR'] ?? null),
)
method

%audit -> diff ($before, $after)

line 31
Vergelijkt twee arrays, $before en $after, en retourneert een array van wijzigingen waar waarden verschillen, met de oude en nieuwe waarden voor elke gewijzigde eigenschap.
$changed = []
foreach ($after AS $col => $newVal){
	$oldVal = $before[$col] ?? null
	if ($oldVal !== $newVal) $changed[$col] = ['from' => $oldVal, 'to' => $newVal]
}
return $changed
method

%audit -> history ($model, $recordId, $limit = 50)

line 40
Haal de auditgeschiedenis op voor een specifiek model en record-ID, en retourneer een beperkt aantal vermeldingen gesorteerd op tijdstempel in aflopende volgorde.
$class = is_object($model) ? get_class($model) : (string)$model
return %MySQL->query(
	'SELECT * FROM audit_log WHERE model=? AND record_id=? ORDER BY ts DESC LIMIT ?',
	$class, (string)$recordId, (int)$limit,
)->fetchAll(\PDO::FETCH_OBJ)
method

%audit -> byUser ($userId, $fromTs = 0, $limit = 100)

line 48
Haal auditlogboekvermeldingen op voor een specifieke gebruiker, beginnend vanaf een gegeven tijdstempel, met een limiet op het aantal teruggegeven vermeldingen.
return %MySQL->query(
	'SELECT * FROM audit_log WHERE user=? AND ts >= ? ORDER BY ts DESC LIMIT ?',
	(int)$userId, (int)$fromTs, (int)$limit,
)->fetchAll(\PDO::FETCH_OBJ)
method

%audit -> purge ($olderThanSeconds = 31536000)

line 55
Verwijdert vermeldingen uit de audit_log-tabel die ouder zijn dan het opgegeven aantal seconden.
%MySQL->query('DELETE FROM audit_log WHERE ts < ?', time() - $olderThanSeconds)
object

%creds

/phlo/resources/security/creds.phlo
version 1.0
creator q-ai.nl
summary Credentials resolver from env and ini sources
package security
frontend false
backend true
tags credentials env ini secrets configuration
method

%creds -> __construct (?array $values = null)

line 9
Initialiseert het creds-object, waarbij waarden worden opgelost als deze niet zijn opgegeven, en wijst elke waarde toe aan de bijbehorende eigenschap, waarbij indien nodig een nieuwe instantie van static of SensitiveParameterValue wordt gemaakt.
$values ??= $this->resolve()
foreach ($values AS $key => $value){
	$this->$key = is_array($value) ? new static($value) : new \SensitiveParameterValue((string)$value)
}
method

%creds -> resolve

line 16
Deze functie lost en voegt inloggegevens samen vanuit een INI-bestand en omgevingsvariabelen, en retourneert de gecombineerde gegevensarray.
$data = []
$this->merge($data, $this->loadINI(data.'creds.ini'))
$this->merge($data, $this->envValues(false))
$this->merge($data, $this->envValues(true))
return $data
method

%creds -> loadINI (string $file):array

line 24
Laadt configuratie-instellingen uit een INI-bestand dat is opgegeven door het gegeven bestandspad en retourneert deze als een associatieve array. Als het bestand niet bestaat of niet kan worden geparsed, retourneert het een lege array.
if (!is_file($file)) return []
$ini = parse_ini_file($file, true, INI_SCANNER_RAW)
return is_array($ini) ? $ini : []
method

%creds -> envValues (bool $hostScoped = false):array

line 30
Haalt omgevingsvariabele waarden op die beginnen met een opgegeven voorvoegsel, optioneel beperkt tot de host, en retourneert deze als een associatieve array.
$out = []
$prefix = $hostScoped ? ('PHLO_'.$this->hostKey().'__') : 'PHLO__'
$sources = []
is_array($_ENV ?? null) && $sources[] = $_ENV
is_array($_SERVER ?? null) && $sources[] = $_SERVER
is_array($env = getenv()) && $sources[] = $env
foreach ($sources AS $source){
	foreach ($source AS $key => $value){
		$key = (string)$key
		if (!str_starts_with($key, $prefix)) continue
		$path = substr($key, strlen($prefix))
		if (!$path) continue
		$this->envAssign($out, explode('__', $path), (string)$value)
	}
}
return $out
method

%creds -> hostKey

line 49
Deze functie verwerkt de host van de aanvraag, zet deze om naar hoofdletters, vervangt niet-alfanumerieke tekens door underscores en verwijdert eventuele leidende of volgende underscores.
$host = strtoupper(%req->host)
$host = preg_replace('/[^A-Z0-9]+/', '_', $host)
return trim($host, '_')
method

%creds -> envAssign (array &$target, array $parts, string $value):void

line 55
Wijst een waarde toe aan een geneste arraystructuur op basis van de opgegeven delen, waarbij indien nodig tussenliggende arrays worden aangemaakt.
$parts = array_values(array_filter(loop($parts, fn($part) => trim($part)), 'strlen'))
if (!$parts) return
$node = &$target
$last = count($parts) - 1
foreach ($parts AS $i => $part){
	if ($i === $last){
		$node[$part] = $value
		return
	}
	if (!isset($node[$part]) || !is_array($node[$part])) $node[$part] = []
	$node = &$node[$part]
}
method

%creds -> merge (array &$base, array $add):void

line 70
Voegt de inhoud van de $add-array samen met de $base-array, waarbij waarden recursief worden gecombineerd als beide arrays zijn.
foreach ($add AS $key => $value){
	if (isset($base[$key]) && is_array($base[$key]) && is_array($value)){
		$this->merge($base[$key], $value)
		continue
	}
	$base[$key] = $value
}
method

%creds -> objGet ($key)

line 80
Haal de waarde op die aan de opgegeven sleutel is gekoppeld aan het object, en retourneer gevoelige waarden op een veilige manier.
if ($key === 'toArray') return loop($this->objData, fn($value) => is_a($value, 'SensitiveParameterValue') ? $value->getValue() : $value)
if (isset($this->objData[$key]) && is_a($this->objData[$key], '\SensitiveParameterValue')) return $this->objData[$key]->getValue()
method

%creds -> objInfo

line 85
Deze functie verwerkt elk item in objData, vervangt instanties van SensitiveParameterValue door sterren die overeenkomen met hun lengte, terwijl andere waarden ongewijzigd blijven.
loop($this->objData, fn($value) => is_a($value, '\SensitiveParameterValue') ? str_repeat('*', strlen($value->getValue())) : $value)
object

%CSRF

/phlo/resources/security/CSRF.phlo
version 1.0
creator q-ai.nl
summary Rotating async CSRF protection for Phlo requests
package security
frontend true
backend true
requires @session token payload
provides app.mod.csrf
tags csrf security async forms
view

%CSRF -> view

line 11
Genereert een meta-tag met de CSRF-token voor veilige formulierindieningen.
<meta name=csrf content="$this->token">
prop

%CSRF -> token

line 12
Genereert een CSRF-token van 32 tekens als er nog geen in de sessie bestaat.
%session->csrf ??= token(32)
method

%CSRF -> verify

line 13
Verifieert het CSRF-token door het te vergelijken met het token dat in de HTTP-header is ontvangen.
hash_equals($this->token, (string)($_SERVER['HTTP_X_CSRF_TOKEN'] ?? void))
method

%CSRF -> update

line 14
Werk de CSRF-token in de sessie bij en wijs deze toe aan de csrf-eigenschap.
arr(csrf: $this->token = %session->csrf = token(32))
view

script

line 16
Stelt de CSRF-tokenwaarde in de meta-tag van het document in.
app.mod.csrf = value => obj('meta[name="csrf"]').content = value
object

%JWT

/phlo/resources/security/JWT.phlo
version 1.0
creator q-ai.nl
summary Sign and verify compact HS256 JSON Web Tokens (RFC 7519), secure by default
package security
frontend false
backend true
requires php-ext:hash
tags jwt jws hs256 token auth security
method

%JWT -> __construct (public string $secret, public string $issuer = void, public int $leeway = 30)

line 10
Initialiseert een JWT-instantie met een geheim, een uitgever en een speling voor de vervaltijd. Het zorgt ervoor dat het geheim minimaal 32 bytes lang is en geeft een foutmelding als aan deze voorwaarde niet wordt voldaan.
strlen($this->secret) >= 32 || error('JWT secret must be at least 32 bytes', 500)
method

%JWT -> sign (array $claims, int $ttl = 3600):string

line 14
Genereert een JSON Web Token (JWT) door de opgegeven claims te ondertekenen met een gespecificeerde tijdsduur (TTL). De token bevat tijdstempels voor uitgifte (iat) en vervaldatum (exp), en kan optioneel een uitgever (iss) bevatten.
$now = time()
$claims['iat'] = $now
$claims['exp'] = $now + $ttl
if ($this->issuer !== void) $claims['iss'] = $this->issuer
$body = $this->encode(['alg' => 'HS256', 'typ' => 'JWT']).dot.$this->encode($claims)
return $body.dot.$this->sig($body)
method

%JWT -> verify (string $token):array

line 23
Verifieert een JSON Web Token (JWT) door de structuur, handtekening en claims zoals vervaldatum en uitgever te controleren.
$token = preg_replace('/^Bearer\s+/i', void, trim($token))
$parts = explode(dot, $token)
count($parts) === 3 || error('JWT malformed', 401)
[$h, $p, $s] = $parts
$header = (array)json_decode((string)$this->decode($h), true)
($header['alg'] ?? void) === 'HS256' || error('JWT algorithm not allowed', 401)
hash_equals($this->sig($h.dot.$p), $s) || error('JWT signature invalid', 401)
$claims = (array)json_decode((string)$this->decode($p), true)
$now = time()
isset($claims['nbf']) && $now + $this->leeway < $claims['nbf'] && error('JWT not yet valid', 401)
isset($claims['exp']) && $now - $this->leeway >= $claims['exp'] && error('JWT expired', 401)
$this->issuer === void || ($claims['iss'] ?? void) === $this->issuer || error('JWT issuer mismatch', 401)
return $claims
method

%JWT -> sig (string $body):string

line 39
Genereert een handtekening voor de gegeven body met HMAC met SHA-256 en een geheime sleutel.
$this->encode(hash_hmac('sha256', $body, $this->secret, true))
method

%JWT -> encode ($data):string

line 40
Codeert de gegeven gegevens in een JSON Web Token (JWT) formaat met behulp van base64-codering.
rtrim(strtr(base64_encode(is_string($data) ? $data : (string)json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)), '+/', '-_'), eq)
method

%JWT -> decode (string $data):string

line 41
Decodeert een JSON Web Token (JWT) van een base64-gecodeerde string, waarbij URL-veilige tekens worden vervangen door standaard base64-tekens.
(string)base64_decode(strtr($data, '-_', '+/'))
object

%rate

/phlo/resources/security/rate.phlo
version 1.2
creator q-ai.nl
summary Rate-limit (fixed window) op rate_limit-tabel. Schema: resources/security/rate.sql
package security
frontend false
backend true
requires @MySQL
tags rate limit throttle abuse
method

%rate -> check ($key, $limit, $windowSeconds, $storage = 'db')

line 10
Controleert of een snelheidslimiet is overschreden voor een gegeven sleutel binnen een specifieke tijdsperiode, waarbij de gegevens in een database of APCu-cache worden opgeslagen.
if ($storage === 'apcu') return $this->checkApcu($key, $limit, $windowSeconds)
$now = time()
$row = %MySQL->query('SELECT count, window_start FROM rate_limit WHERE rkey=?', $key)->fetchObject('obj') ?: null
if ($row && ($now - (int)$row->window_start) < $windowSeconds){
	if ((int)$row->count >= $limit) return false
	%MySQL->query('UPDATE rate_limit SET count=count+1 WHERE rkey=?', $key)
	return true
}
%MySQL->query('INSERT INTO rate_limit (rkey, count, window_start) VALUES (?,1,?) ON DUPLICATE KEY UPDATE count=1, window_start=VALUES(window_start)', $key, $now)
return true
method

%rate -> checkApcu ($key, $limit, $windowSeconds)

line 23
Controleert het aantal voorvallen voor een gegeven sleutel binnen een specifieke tijdsperiode, met gebruik van APCu voor caching. Het verhoogt de telling voor de sleutel en retourneert of de telling binnen de gedefinieerde limiet ligt.
$window = (int)floor(time() / $windowSeconds) * $windowSeconds
$apcuKey = 'phlo.rate.'.$key.':'.$window
$count = apcu_inc($apcuKey, 1, $success, $windowSeconds)
if (!$success) apcu_store($apcuKey, 1, $windowSeconds)
return $count <= $limit
method

%rate -> status ($key, $limit, $windowSeconds)

line 31
Controleert de huidige status van de rate limit voor een gegeven sleutel en retourneert het aantal gebruikte verzoeken, de limiet en de tijd tot de limiet opnieuw wordt ingesteld.
$now = time()
$row = %MySQL->query('SELECT count, window_start FROM rate_limit WHERE rkey=?', $key)->fetchObject('obj') ?: null
if (!$row || ($now - (int)$row->window_start) >= $windowSeconds) return obj(used: 0, limit: $limit, resetIn: 0)
return obj(used: (int)$row->count, limit: $limit, resetIn: max(0, ((int)$row->window_start + $windowSeconds) - $now))
method

%rate -> reset ($key)

line 38
Reset de rate limit voor een opgegeven sleutel door de bijbehorende invoer uit de rate_limit-tabel in de MySQL-database te verwijderen.
%MySQL->query('DELETE FROM rate_limit WHERE rkey=?', $key)
method

%rate -> purge ($olderThanSeconds = 604800)

line 39
Verwijdert vermeldingen uit de rate_limit-tabel in MySQL waar de window_start-tijdstempel ouder is dan het opgegeven aantal seconden.
%MySQL->query('DELETE FROM rate_limit WHERE window_start < ?', time() - $olderThanSeconds)
object

%security

/phlo/resources/security/security.phlo
version 1.0
creator q-ai.nl
summary Generic security resource
package security
frontend true
backend true
requires @session token
tags security csp nonce headers
prop

%security -> whitelist

line 10
Definieert een whitelist voor beveiligingsdoeleinden, waardoor alleen gespecificeerde vermeldingen zijn toegestaan.
[]
method

%security -> setNonce

line 12
Stelt een nonce-waarde in voor de applicatie met behulp van een gegenereerde token van de opgegeven lengte.
%app->nonce = token(8)
method

%security -> frameProtect ($mode = 'DENY')

line 14
Stelt de X-Frame-Options-header in om te bepalen of de pagina in een frame kan worden weergegeven, met de standaardmodus 'DENY'.
%res->header('X-Frame-Options', $mode)
method

%security -> frameWhitelist

line 15
Genereert een string van goedgekeurde frame-oorsprongen voor beveiligingsdoeleinden, waardoor alleen opgegeven domeinen de inhoud in een iframe kunnen insluiten.
$this->whitelist ? ' '.implode(space, array_map(fn($d) => "https://*.$d", (array)$this->whitelist)) : void
method

%security -> strict

line 17
Stelt strikte beveiligingsheaders in voor de respons, waaronder Cache-Control en Content-Security-Policy, om de bescherming tegen verschillende webkwetsbaarheden te verbeteren.
$this->base
if (%req->async) return
%res->header('Cache-Control', 'no-store')
$nonce = $this->setNonce
%res->header('Content-Security-Policy', "default-src 'self'; script-src 'nonce-$nonce'; style-src 'self' 'nonce-$nonce'; img-src 'self' data:; font-src 'self'; connect-src 'self'; form-action 'self'; object-src 'none'; frame-src 'self'$this->frameWhitelist; frame-ancestors 'none'; base-uri 'self'")
method

%security -> basic

line 24
Stelt de Content Security Policy (CSP) headers in voor de respons, waarbij toegestane bronnen voor verschillende inhoudstypen worden gedefinieerd op basis van de async-status van het verzoek en de debugmodus.
$this->base
if (%req->async) return
%res->header('Content-Security-Policy', "default-src 'self'; script-src 'self'".(debug ? " 'unsafe-inline'" : '')."; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; form-action 'self'; object-src 'none'; frame-src 'self'$this->frameWhitelist; frame-ancestors 'none'; base-uri 'self'")
method

%security -> marketing

line 29
Stelt de Content Security Policy (CSP) headers voor de respons in, waarmee toegestane bronnen voor verschillende inhoudstypen worden gedefinieerd om de beveiliging te verbeteren.
$this->base
if (%req->async) return
%res->header('Content-Security-Policy', "default-src 'self'; script-src 'self'".(debug ? " 'unsafe-inline'" : '')."; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; form-action 'self'; object-src 'none'; frame-src 'self'$this->frameWhitelist; frame-ancestors 'none'; base-uri 'self'")
method

%security -> api

line 34
Stelt beveiligingsgerelateerde HTTP-headers in voor de API-respons, waaronder Content-Security-Policy, X-Content-Type-Options en Referrer-Policy om de beveiliging te verbeteren.
%res->header('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'")
%res->header('X-Content-Type-Options', 'nosniff')
%res->header('Referrer-Policy', 'no-referrer')
method

%security -> base

line 40
Stelt verschillende beveiligingsgerelateerde HTTP-headers in voor de respons, waaronder Referrer-Policy, X-Content-Type-Options, Cross-Origin-beleid en X-Frame-Options, om de beveiliging tegen cross-origin aanvallen te verbeteren.
%res->header('Referrer-Policy', 'strict-origin-when-cross-origin')
%res->header('X-Content-Type-Options', 'nosniff')
%req->async || %res->header('Cross-Origin-Opener-Policy', 'same-origin')
%req->async || %res->header('Cross-Origin-Resource-Policy', 'same-origin')
%req->async || %res->header('Access-Control-Allow-Origin', %req->base)
%req->async || %res->header('X-Frame-Options', 'DENY')

Functions

function

decrypt($encrypted, $key):string|false

/phlo/resources/security/encryption.phlo line 10
Decrypt de gegeven base64-gecodeerde versleutelde string met de opgegeven sleutel en retourneer de oorspronkelijke gegevens of false als de decryptie mislukt.
($d = base64_decode($encrypted, true)) !== false && strlen($d) >= SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ? sodium_crypto_secretbox_open(substr($d, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES), substr($d, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES), hash('sha256', $key, true)) : false
function

encrypt($data, $key):string

/phlo/resources/security/encryption.phlo line 8
Versleutelt de gegeven data met een geheime sleutel en retourneert het base64-gecodeerde resultaat.
base64_encode(($nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES)).sodium_crypto_secretbox($data, $nonce, hash('sha256', $key, true)))
function

token(int $length = 8, ?string $input = null)

/phlo/resources/security/token.phlo line 8
Genereert een willekeurig token van de opgegeven lengte met een gedefinieerd alfabet, optioneel gezaaid met een invoerstring voor extra willekeurigheid.
	$length || error('Token must have a minimum length above 0', 500)
	$alphabet = 'abcdefghijklmnopqrstuvwxyz'
	$alphabetLength = strlen($alphabet)
	$limit = intdiv(256, $alphabetLength) * $alphabetLength
	$token = void
	$buffer = void
	$state = is_null($input) ? null : hash('sha256', (string)$input, true)
	while (strlen($token) < $length){
		if ($buffer === void){
			if (is_null($state)) $buffer = random_bytes(32)
			else {
				$state = hash('sha256', $state, true)
				$buffer = $state
			}
		}
		$byte = ord($buffer[0])
		$buffer = substr($buffer, 1)
		if ($byte >= $limit) continue
		$token .= $alphabet[$byte % $alphabetLength]
	}
	return $token

We gebruiken essentiële cookies om deze site te laten werken. Met uw toestemming gebruiken we ook analytics om de site te verbeteren.