secure PeerTube API against SSRF attacks
This commit is contained in:
+112
-3
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
// Charger les fonctions de sécurité
|
||||
require_once __DIR__ . '/security.php';
|
||||
|
||||
// Charger d'abord la configuration locale si elle existe
|
||||
$config_local_file = __DIR__ . '/config.local.php';
|
||||
if (file_exists($config_local_file)) {
|
||||
@@ -81,6 +84,19 @@ function initCategories() {
|
||||
* @return array Données retournées par l'API
|
||||
*/
|
||||
function callPeerTubeApi($endpoint, $params = []) {
|
||||
// Validation de l'URL de base PeerTube pour prévenir SSRF
|
||||
if (!isValidPeerTubeUrl(PEERTUBE_URL)) {
|
||||
error_log('SECURITY: Invalid PeerTube URL detected: ' . PEERTUBE_URL);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Nettoyer et valider l'endpoint
|
||||
$endpoint = ltrim($endpoint, '/');
|
||||
if (!isValidApiEndpoint($endpoint)) {
|
||||
error_log('SECURITY: Invalid API endpoint detected: ' . $endpoint);
|
||||
return [];
|
||||
}
|
||||
|
||||
$url = PEERTUBE_URL . '/api/v1/' . $endpoint;
|
||||
|
||||
// Ajouter les paramètres à l'URL
|
||||
@@ -88,10 +104,17 @@ function callPeerTubeApi($endpoint, $params = []) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
// Initialiser cURL
|
||||
// Initialiser cURL avec options de sécurité
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
||||
curl_setopt($ch, CURLOPT_MAXREDIRS, 0);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'KaubuntuRe/1.0');
|
||||
|
||||
// Ajouter la clé API si définie
|
||||
if (defined('API_KEY') && !empty(API_KEY)) {
|
||||
@@ -102,11 +125,18 @@ function callPeerTubeApi($endpoint, $params = []) {
|
||||
|
||||
// Exécuter la requête
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// Traiter la réponse
|
||||
if ($response === false) {
|
||||
// En cas d'erreur, retourner un tableau vide
|
||||
if ($response === false || !empty($error)) {
|
||||
error_log('PeerTube API error: ' . $error);
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
error_log('PeerTube API HTTP error: ' . $httpCode);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -116,6 +146,85 @@ function callPeerTubeApi($endpoint, $params = []) {
|
||||
return $data ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide l'URL PeerTube pour prévenir les attaques SSRF
|
||||
*
|
||||
* @param string $url URL à valider
|
||||
* @return bool True si l'URL est valide et sûre
|
||||
*/
|
||||
function isValidPeerTubeUrl($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide l'endpoint API pour prévenir l'injection de chemins
|
||||
*
|
||||
* @param string $endpoint Endpoint à valider
|
||||
* @return bool True si l'endpoint est valide
|
||||
*/
|
||||
function isValidApiEndpoint($endpoint) {
|
||||
// Bloquer les tentatives de path traversal
|
||||
if (strpos($endpoint, '..') !== false || strpos($endpoint, '//') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Autoriser uniquement les caractères alphanumériques, tirets, underscores et slashes
|
||||
if (!preg_match('/^[a-zA-Z0-9\/_-]+$/', $endpoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Liste blanche des endpoints autorisés
|
||||
$allowedEndpoints = [
|
||||
'videos',
|
||||
'videos/categories',
|
||||
'search/videos',
|
||||
'videos/.*', // Pour les endpoints dynamiques comme videos/{id}
|
||||
'videos/.*/comment-threads', // Pour les commentaires
|
||||
'accounts',
|
||||
'accounts/.*/videos' // Pour les vidéos d'un compte spécifique
|
||||
];
|
||||
|
||||
foreach ($allowedEndpoints as $pattern) {
|
||||
// Remplacer les .* par des marqueurs temporaires
|
||||
$tempPattern = str_replace('.*', '__WILDCARD__', $pattern);
|
||||
// Échapper les caractères spéciaux regex
|
||||
$escapedPattern = preg_quote($tempPattern, '/');
|
||||
// Remettre les wildcards en place
|
||||
$regexPattern = str_replace('__WILDCARD__', '.*', $escapedPattern);
|
||||
|
||||
if (preg_match('/^' . $regexPattern . '$/', $endpoint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les catégories depuis l'API PeerTube
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user