fix(security): systematic output escaping with e() and video-card partial
This commit is contained in:
+90
-2
@@ -3,6 +3,20 @@
|
||||
* Fonctions de sécurité pour la validation et l'assainissement des entrées
|
||||
*/
|
||||
|
||||
/**
|
||||
* Échappe une valeur pour une sortie HTML (texte ou attribut).
|
||||
*
|
||||
* Raccourci pour htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8') :
|
||||
* les guillemets simples et doubles sont encodés, ce qui rend la sortie
|
||||
* sûre aussi bien dans le contenu que dans les attributs.
|
||||
*
|
||||
* @param mixed $value Valeur à échapper
|
||||
* @return string Valeur échappée
|
||||
*/
|
||||
function e($value) {
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide et assainit un ID de vidéo UUID
|
||||
*
|
||||
@@ -141,6 +155,44 @@ function validateHttpHeaders() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valeur par défaut livrée dans config.default.php : si CSRF_SECRET vaut
|
||||
* encore cette valeur, le secret n'a pas été configuré pour l'instance.
|
||||
*/
|
||||
if (!defined('CSRF_SECRET_PLACEHOLDER')) {
|
||||
define('CSRF_SECRET_PLACEHOLDER', 'change-me-in-config-local-php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le secret CSRF effectif utilisé pour signer les tokens.
|
||||
*
|
||||
* Si CSRF_SECRET est absent, vide ou vaut encore la valeur par défaut, un
|
||||
* avertissement critique est enregistré et un secret éphémère propre au
|
||||
* processus est généré (bin2hex(random_bytes(32))) : les tokens restent
|
||||
* signés, mais sont invalidés à chaque redémarrage du processus PHP.
|
||||
*
|
||||
* @return string Secret CSRF effectif
|
||||
*/
|
||||
function getCsrfSecret() {
|
||||
static $secret = null;
|
||||
|
||||
if ($secret !== null) {
|
||||
return $secret;
|
||||
}
|
||||
|
||||
if (defined('CSRF_SECRET') && CSRF_SECRET !== '' && CSRF_SECRET !== CSRF_SECRET_PLACEHOLDER) {
|
||||
$secret = CSRF_SECRET;
|
||||
return $secret;
|
||||
}
|
||||
|
||||
error_log('SECURITY CRITICAL: CSRF_SECRET is not configured (default value in use). '
|
||||
. 'An ephemeral per-process secret was generated: CSRF tokens will be invalidated '
|
||||
. 'on every process restart. Set CSRF_SECRET in config.local.php (bin2hex(random_bytes(32))).');
|
||||
|
||||
$secret = bin2hex(random_bytes(32));
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un token CSRF stateless (HMAC + timestamp).
|
||||
*
|
||||
@@ -152,7 +204,7 @@ function validateHttpHeaders() {
|
||||
*/
|
||||
function generateCSRFToken() {
|
||||
$timestamp = time();
|
||||
$hash = hash_hmac('sha256', (string) $timestamp, CSRF_SECRET);
|
||||
$hash = hash_hmac('sha256', (string) $timestamp, getCsrfSecret());
|
||||
return $timestamp . ':' . $hash;
|
||||
}
|
||||
|
||||
@@ -183,7 +235,7 @@ function validateCSRFToken($token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expectedHash = hash_hmac('sha256', $timestamp, CSRF_SECRET);
|
||||
$expectedHash = hash_hmac('sha256', $timestamp, getCsrfSecret());
|
||||
return hash_equals($expectedHash, $hash);
|
||||
}
|
||||
|
||||
@@ -347,4 +399,40 @@ function validateAjaxOrigin() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide une URL distante (PeerTube, Castopod, Funkwhale…) pour prévenir
|
||||
* les attaques SSRF avant tout appel sortant.
|
||||
*
|
||||
* @param string $url URL à valider
|
||||
* @return bool True si l'URL est valide et sûre
|
||||
*/
|
||||
function isValidRemoteUrl($url) {
|
||||
// Vérifier que l'URL est bien formée
|
||||
$parsed = parse_url($url);
|
||||
if (!$parsed || !isset($parsed['scheme']) || !isset($parsed['host'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Autoriser uniquement HTTPS (ou HTTP en développement)
|
||||
if (!in_array($parsed['scheme'], ['https', 'http'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bloquer les adresses IP privées et locales
|
||||
$host = $parsed['host'];
|
||||
if (filter_var($host, FILTER_VALIDATE_IP)) {
|
||||
if (!filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bloquer localhost et autres domaines dangereux
|
||||
$blockedHosts = ['localhost', '127.0.0.1', '::1', '0.0.0.0', 'metadata.google.internal'];
|
||||
if (in_array(strtolower($host), $blockedHosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user