1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/spip_ynh.git synced 2024-09-03 20:25:59 +02:00
spip_ynh/conf/ecran_securite.php
Éric Gaspar a8a493d9da fix
2023-06-01 09:51:02 +02:00

758 lines
18 KiB
PHP

<?php
/*
* ecran_securite.php
* ------------------
*/
define('_ECRAN_SECURITE', '1.5.3'); // 2023-05-31
/*
* Documentation : https://www.spip.net/fr_article4200.html
*/
/*
* Test utilisateur
*/
if (isset($_GET['test_ecran_securite'])) {
$ecran_securite_raison = 'test ' . _ECRAN_SECURITE;
}
if (file_exists($f = __DIR__ . DIRECTORY_SEPARATOR . 'ecran_securite_options.php')) {
include ($f);
}
/*
* Monitoring
* var_isbot=0 peut etre utilise par un bot de monitoring pour surveiller la disponibilite d'un site vu par les users
* var_isbot=1 peut etre utilise pour monitorer la disponibilite pour les bots (sujets a 503 de delestage si
* le load depasse ECRAN_SECURITE_LOAD)
*/
if (!defined('_IS_BOT') and isset($_GET['var_isbot'])) {
define('_IS_BOT', $_GET['var_isbot'] ? true : false);
}
/*
* Détecteur de robot d'indexation
*/
if (!defined('_IS_BOT')) {
define(
'_IS_BOT',
isset($_SERVER['HTTP_USER_AGENT'])
and preg_match(
','
. implode('|', array(
// mots generiques
'bot',
'slurp',
'crawler',
'crwlr',
'java',
'monitoring',
'spider',
'webvac',
'yandex',
'MSIE 6\.0', // botnet 99,9% du temps
// UA plus cibles
'200please',
'80legs',
'a6-indexer',
'aboundex',
'accoona',
'acrylicapps',
'addthis',
'adressendeutschland',
'alexa',
'altavista',
'analyticsseo',
'antennapod',
'arachnys',
'archive',
'argclrint',
'aspseek',
'baidu',
'begunadvertising',
'bing',
'bloglines',
'buck',
'browsershots',
'bubing',
'butterfly',
'changedetection',
'charlotte',
'chilkat',
'china',
'coccoc',
'crowsnest',
'dataminr',
'daumoa',
'dlvr\.it',
'dlweb',
'drupal',
'ec2linkfinder',
'eset\.com',
'estyle',
'exalead',
'ezooms',
'facebookexternalhit',
'facebookplatform',
'fairshare',
'feedfetcher',
'feedfetcher-google',
'feedly',
'fetch',
'flipboardproxy',
'genieo',
'google',
'go-http-client',
'grapeshot',
'hatena-useragent',
'head',
'hosttracker',
'hubspot',
'ia_archiver',
'ichiro',
'iltrovatore-setaccio',
'immediatenet',
'ina',
'inoreader',
'infegyatlas',
'infohelfer',
'instapaper',
'jabse',
'james',
'jersey',
'kumkie',
'linkdex',
'linkfluence',
'linkwalker',
'litefinder',
'loadimpactpageanalyzer',
'ltx71',
'luminate',
'lycos',
'lycosa',
'mediapartners-google',
'msai',
'myapp',
'nativehost',
'najdi',
'netcraftsurveyagent',
'netestate',
'netseer',
'netnewswire',
'newspaper',
'newsblur',
'nuhk',
'nuzzel',
'okhttp',
'otmedia',
'owlin',
'owncloud',
'panscient',
'paper\.li',
'parsijoo',
'protopage',
'plukkie',
'proximic',
'pubsub',
'python',
'qirina',
'qoshe',
'qualidator',
'qwantify',
'rambler',
'readability',
'ruby',
'sbsearch',
'scoop\.it',
'scooter',
'scoutjet',
'scrapy',
'scrubby',
'scrubbybloglines',
'shareaholic',
'shopwiki',
'simplepie',
'sistrix',
'sitechecker',
'siteexplorer',
'snapshot',
'sogou',
'special_archiver',
'speedy',
'spinn3r',
'spreadtrum',
'steeler',
'subscriber',
'suma',
'superdownloads',
'svenska-webbsido',
'teoma',
'the knowledge AI',
'thumbshots',
'tineye',
'traackr',
'trendiction',
'trendsmap',
'tweetedtimes',
'tweetmeme',
'universalfeedparser',
'uaslinkchecker',
'undrip',
'unwindfetchor',
'upday',
'vedma',
'vkshare',
'vm',
'wch',
'webalta',
'webcookies',
'webparser',
'webthumbnail',
'wesee',
'wise-guys',
'woko',
'wordpress',
'wotbox',
'y!j-bri',
'y!j-bro',
'y!j-brw',
'y!j-bsc',
'yahoo',
'yahoo!',
'yahooysmcm',
'ymobactus',
'yats',
'yeti',
'zeerch'
)) . ',i',
(string)$_SERVER['HTTP_USER_AGENT']
)
);
}
if (!defined('_IS_BOT_FRIEND')) {
define(
'_IS_BOT_FRIEND',
isset($_SERVER['HTTP_USER_AGENT'])
and preg_match(
',' . implode('|', array(
'facebookexternalhit',
'twitterbot',
'flipboardproxy',
'wordpress'
)) . ',i',
(string)$_SERVER['HTTP_USER_AGENT']
)
);
}
/*
* Interdit de passer une variable id_article (ou id_xxx) qui ne
* soit pas numérique (ce qui bloque l'exploitation de divers trous
* de sécurité, dont celui de toutes les versions < 1.8.2f)
* (sauf pour id_table, qui n'est pas numérique jusqu'à [5743])
* (id_base est une variable de la config des widgets de WordPress)
*/
$_exceptions = array('id_table', 'id_base', 'id_parent', 'id_article_pdf');
foreach ($_GET as $var => $val) {
if (
$_GET[$var] and strncmp($var, "id_", 3) == 0
and !in_array($var, $_exceptions)
) {
$_GET[$var] = is_array($_GET[$var]) ? @array_map('intval', $_GET[$var]) : intval($_GET[$var]);
}
}
foreach ($_POST as $var => $val) {
if (
$_POST[$var] and strncmp($var, "id_", 3) == 0
and !in_array($var, $_exceptions)
) {
$_POST[$var] = is_array($_POST[$var]) ? @array_map('intval', $_POST[$var]) : intval($_POST[$var]);
}
}
foreach ($GLOBALS as $var => $val) {
if (
$GLOBALS[$var] and strncmp($var, "id_", 3) == 0
and !in_array($var, $_exceptions)
) {
$GLOBALS[$var] = is_array($GLOBALS[$var]) ? @array_map('intval', $GLOBALS[$var]) : intval($GLOBALS[$var]);
}
}
/*
* Interdit la variable $cjpeg_command, qui était utilisée sans
* précaution dans certaines versions de dev (1.8b2 -> 1.8b5)
*/
$cjpeg_command = '';
/*
* Contrôle de quelques variables (XSS)
*/
foreach (array('lang', 'var_recherche', 'aide', 'var_lang_r', 'lang_r', 'var_ajax_ancre', 'nom_fichier') as $var) {
if (isset($_GET[$var])) {
$_REQUEST[$var] = $GLOBALS[$var] = $_GET[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_GET[$var]);
}
if (isset($_POST[$var])) {
$_REQUEST[$var] = $GLOBALS[$var] = $_POST[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_POST[$var]);
}
}
/*
* Filtre l'accès à spip_acces_doc (injection SQL en 1.8.2x)
*/
if (isset($_SERVER['REQUEST_URI'])) {
if (preg_match(',^(.*/)?spip_acces_doc\.,', (string)$_SERVER['REQUEST_URI'])) {
$file = addslashes((string)$_GET['file']);
}
}
/*
* Pas d'inscription abusive
*/
if (
isset($_REQUEST['mode']) and isset($_REQUEST['page'])
and !in_array($_REQUEST['mode'], array("6forum", "1comite"))
and $_REQUEST['page'] == "identifiants"
) {
$ecran_securite_raison = "identifiants";
}
/*
* Agenda joue à l'injection php
*/
if (
isset($_REQUEST['partie_cal'])
and $_REQUEST['partie_cal'] !== htmlentities((string)$_REQUEST['partie_cal'])
) {
$ecran_securite_raison = "partie_cal";
}
if (
isset($_REQUEST['echelle'])
and $_REQUEST['echelle'] !== htmlentities((string)$_REQUEST['echelle'])
) {
$ecran_securite_raison = "echelle";
}
/*
* Espace privé
*/
if (
isset($_REQUEST['exec'])
and !preg_match(',^[\w-]+$,', (string)$_REQUEST['exec'])
) {
$ecran_securite_raison = "exec";
}
if (
isset($_REQUEST['cherche_auteur'])
and preg_match(',[<],', (string)$_REQUEST['cherche_auteur'])
) {
$ecran_securite_raison = "cherche_auteur";
}
if (
isset($_REQUEST['exec'])
and $_REQUEST['exec'] == 'auteurs'
and isset($_REQUEST['recherche'])
and preg_match(',[<],', (string)$_REQUEST['recherche'])
) {
$ecran_securite_raison = "recherche";
}
if (
isset($_REQUEST['exec'])
and $_REQUEST['exec'] == 'info_plugin'
and isset($_REQUEST['plugin'])
and preg_match(',[<],', (string)$_REQUEST['plugin'])
) {
$ecran_securite_raison = "plugin";
}
if (
isset($_REQUEST['exec'])
and $_REQUEST['exec'] == 'puce_statut'
and isset($_REQUEST['id'])
and !intval($_REQUEST['id'])
) {
$ecran_securite_raison = "puce_statut";
}
if (
isset($_REQUEST['action'])
and $_REQUEST['action'] == 'configurer'
) {
if (
@file_exists('inc_version.php')
or @file_exists('ecrire/inc_version.php')
) {
function action_configurer() {
include_spip('inc/autoriser');
if (!autoriser('configurer', _request('configuration'))) {
include_spip('inc/minipres');
echo minipres(_T('info_acces_interdit'));
exit;
}
require _DIR_RESTREINT . 'action/configurer.php';
action_configurer_dist();
}
}
}
if (
isset($_REQUEST['action'])
and $_REQUEST['action'] == 'ordonner_liens_documents'
and isset($_REQUEST['ordre'])
and is_string($_REQUEST['ordre'])
) {
$ecran_securite_raison = "ordre a la chaine";
}
/*
* Bloque les requêtes contenant %00 (manipulation d'include)
*/
if (strpos(
(function_exists('get_magic_quotes_gpc') and @get_magic_quotes_gpc())
? stripslashes(serialize($_REQUEST))
: serialize($_REQUEST),
chr(0)
) !== false) {
$ecran_securite_raison = "%00";
}
/*
* Bloque les requêtes fond=formulaire_
*/
if (
isset($_REQUEST['fond'])
and preg_match(',^formulaire_,i', $_REQUEST['fond'])
) {
$ecran_securite_raison = "fond=formulaire_";
}
/*
* Bloque les requêtes du type ?GLOBALS[type_urls]=toto (bug vieux php)
*/
if (isset($_REQUEST['GLOBALS'])) {
$ecran_securite_raison = "GLOBALS[GLOBALS]";
}
/*
* Bloque les requêtes des bots sur:
* les agenda
* les paginations entremélées
*/
if (_IS_BOT) {
if (
(isset($_REQUEST['echelle']) and isset($_REQUEST['partie_cal']) and isset($_REQUEST['type']))
or (strpos((string)$_SERVER['REQUEST_URI'], 'debut_') and preg_match(',[?&]debut_.*&debut_,', (string)$_SERVER['REQUEST_URI']))
or (isset($_REQUEST['calendrier_annee']) and strpos((string)$_SERVER['REQUEST_URI'], 'debut_'))
or (isset($_REQUEST['calendrier_annee']) and preg_match(',[?&]calendrier_annee=.*&calendrier_annee=,', (string)$_SERVER['REQUEST_URI']))
) {
$ecran_securite_raison = "robot agenda/double pagination";
}
}
/*
* Bloque une vieille page de tests de CFG (<1.11)
* Bloque un XSS sur une page inexistante
*/
if (isset($_REQUEST['page'])) {
if ($_REQUEST['page'] == 'test_cfg') {
$ecran_securite_raison = "test_cfg";
}
if ($_REQUEST['page'] !== htmlspecialchars((string)$_REQUEST['page'])) {
$ecran_securite_raison = "xsspage";
}
if (
$_REQUEST['page'] == '404'
and isset($_REQUEST['erreur'])
) {
$ecran_securite_raison = "xss404";
}
}
/*
* XSS par array
*/
foreach (array('var_login') as $var) {
if (isset($_REQUEST[$var]) and is_array($_REQUEST[$var])) {
$ecran_securite_raison = "xss " . $var;
}
}
/*
* Parade antivirale contre un cheval de troie
*/
if (!function_exists('tmp_lkojfghx')) {
function tmp_lkojfghx() {}
function tmp_lkojfghx2($a = 0, $b = 0, $c = 0, $d = 0) {
// si jamais on est arrivé ici sur une erreur php
// et qu'un autre gestionnaire d'erreur est défini, l'appeller
if ($b && $GLOBALS['tmp_xhgfjokl']) {
call_user_func($GLOBALS['tmp_xhgfjokl'], $a, $b, $c, $d);
}
}
}
if (isset($_POST['tmp_lkojfghx3'])) {
$ecran_securite_raison = "gumblar";
}
/*
* Outils XML mal sécurisés < 2.0.9
*/
if (isset($_REQUEST['transformer_xml'])) {
$ecran_securite_raison = "transformer_xml";
}
/*
* Outils XML mal sécurisés again
*/
if (isset($_REQUEST['var_url']) and $_REQUEST['var_url'] and isset($_REQUEST['exec']) and $_REQUEST['exec'] == 'valider_xml') {
$url = trim($_REQUEST['var_url']);
if (
strncmp($url, '/', 1) == 0
or (($p = strpos($url, '..')) !== false and strpos($url, '..', $p + 3) !== false)
or (($p = strpos($url, '..')) !== false and strpos($url, 'IMG', $p + 3) !== false)
or (strpos($url, '://') !== false or strpos($url, ':\\') !== false)
) {
$ecran_securite_raison = 'URL interdite pour var_url';
}
}
/*
* Sauvegarde mal securisée < 2.0.9
*/
if (
isset($_REQUEST['nom_sauvegarde'])
and strstr((string)$_REQUEST['nom_sauvegarde'], '/')
) {
$ecran_securite_raison = 'nom_sauvegarde manipulee';
}
if (
isset($_REQUEST['znom_sauvegarde'])
and strstr((string)$_REQUEST['znom_sauvegarde'], '/')
) {
$ecran_securite_raison = 'znom_sauvegarde manipulee';
}
/*
* op permet des inclusions arbitraires ;
* on vérifie 'page' pour ne pas bloquer ... drupal
*/
if (
isset($_REQUEST['op']) and isset($_REQUEST['page'])
and $_REQUEST['op'] !== preg_replace('/[^\\-\w]/', '', $_REQUEST['op'])
) {
$ecran_securite_raison = 'op';
}
/*
* Forms & Table ne se méfiait pas assez des uploads de fichiers
*/
if (count($_FILES)) {
foreach ($_FILES as $k => $v) {
if (
preg_match(',^fichier_\d+$,', $k)
and preg_match(',\.php,i', $v['name'])
) {
unset($_FILES[$k]);
}
}
}
/*
* et Contact trop laxiste avec une variable externe
* on bloque pas le post pour eviter de perdre des donnees mais on unset la variable et c'est tout
*/
if (isset($_REQUEST['pj_enregistrees_nom']) and $_REQUEST['pj_enregistrees_nom']) {
unset($_REQUEST['pj_enregistrees_nom']);
unset($_GET['pj_enregistrees_nom']);
unset($_POST['pj_enregistrees_nom']);
}
/*
* reinstall=oui un peu trop permissif
*/
if (
isset($_REQUEST['reinstall'])
and $_REQUEST['reinstall'] == 'oui'
) {
$ecran_securite_raison = 'reinstall=oui';
}
/*
* Pas d'action pendant l'install
*/
if (isset($_REQUEST['exec']) and $_REQUEST['exec'] === 'install' and isset($_REQUEST['action'])) {
$ecran_securite_raison = 'install&action impossibles';
}
/*
* Échappement xss referer
*/
if (isset($_SERVER['HTTP_REFERER'])) {
$_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
}
/*
* Echappement HTTP_X_FORWARDED_HOST
*/
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_X_FORWARDED_HOST'] = strtr($_SERVER['HTTP_X_FORWARDED_HOST'], "<>?\"\{\}\$'` \r\n", '____________');
}
/*
* Pas d'erreur dans l'erreur
*/
if (isset($_REQUEST['var_erreur']) and isset($_REQUEST['page']) and $_REQUEST['page'] === 'login') {
if (strlen($_REQUEST['var_erreur']) !== strcspn($_REQUEST['var_erreur'], '<>')) {
$ecran_securite_raison = 'var_erreur incorrecte';
}
}
/*
* Réinjection des clés en html dans l'admin r19561
*/
if (
(isset($_SERVER['REQUEST_URI']) and strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false)
or isset($_REQUEST['var_memotri'])
) {
$zzzz = implode("", array_keys($_REQUEST));
if (strlen($zzzz) != strcspn($zzzz, '<>"\'')) {
$ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
}
}
/*
* Injection par connect
*/
if (
isset($_REQUEST['connect'])
// cas qui permettent de sortir d'un commentaire PHP
and (
strpos($_REQUEST['connect'], "?") !== false
or strpos($_REQUEST['connect'], "<") !== false
or strpos($_REQUEST['connect'], ">") !== false
or strpos($_REQUEST['connect'], "\n") !== false
or strpos($_REQUEST['connect'], "\r") !== false
)
) {
$ecran_securite_raison = "malformed connect argument";
}
/*
* _oups donc
*/
if (
isset($_REQUEST['_oups'])
and base64_decode($_REQUEST['_oups'], true) === false) {
$ecran_securite_raison = "malformed _oups argument";
}
if (
isset($_REQUEST['formulaire_action_args']) || isset($_REQUEST['var_login'])
) {
foreach ($_REQUEST as $k => $v) {
if (is_string($v)
and strpbrk($v, "&\"'<>") !== false
and preg_match(',^[abis]:\d+[:;],', $v)
and __ecran_test_if_serialized($v)
) {
$_REQUEST[$k] = htmlspecialchars($v, ENT_QUOTES);
if (isset($_POST[$k])) $_POST[$k] = $_REQUEST[$k];
if (isset($_GET[$k])) $_GET[$k] = $_REQUEST[$k];
}
}
}
/**
* Version simplifiée de https://developer.wordpress.org/reference/functions/is_serialized/
*/
function __ecran_test_if_serialized($data) {
$data = trim($data);
if ('N;' === $data) {return true;}
if (strlen($data) < 4) {return false;}
if (':' !== $data[1]) {return false;}
$semicolon = strpos($data, ';');
$brace = strpos($data, '}');
// Either ; or } must exist.
if (false === $semicolon && false === $brace) {return false;}
// But neither must be in the first X characters.
if (false !== $semicolon && $semicolon < 3) {return false;}
if (false !== $brace && $brace < 4) {return false;}
$token = $data[0];
if (in_array($token, array('s', 'S', 'a', 'O', 'C', 'o', 'E'))) {
if (in_array($token, array('s', 'S')) and false === strpos($data, '"')) {return false;}
return (bool)preg_match("/^{$token}:[0-9]+:/s", $data);
} elseif (in_array($token, array('b', 'i', 'd'))) {
return (bool)preg_match("/^{$token}:[0-9.E+-]+;/", $data);
}
return false;
}
/*
* S'il y a une raison de mourir, mourons
*/
if (isset($ecran_securite_raison)) {
header("HTTP/1.0 403 Forbidden");
header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: text/html");
header("Connection: close");
die("<html><title>Error 403: Forbidden</title><body><h1>Error 403</h1><p>You are not authorized to view this page ($ecran_securite_raison)</p></body></html>");
}
/*
* Un filtre filtrer_entites securise
*/
if (!function_exists('filtre_filtrer_entites_dist')) {
function filtre_filtrer_entites_dist($t) {
include_spip('inc/texte');
return interdire_scripts(filtrer_entites($t));
}
}
/*
* Fin sécurité
*/
/*
* Bloque les bots quand le load déborde
*/
if (!defined('_ECRAN_SECURITE_LOAD')) {
define('_ECRAN_SECURITE_LOAD', 4);
}
if (
defined('_ECRAN_SECURITE_LOAD')
and _ECRAN_SECURITE_LOAD > 0
and _IS_BOT
and !_IS_BOT_FRIEND
and $_SERVER['REQUEST_METHOD'] === 'GET'
and (
(function_exists('sys_getloadavg')
and $load = sys_getloadavg()
and is_array($load)
and $load = array_shift($load))
or
(@is_readable('/proc/loadavg')
and $load = file_get_contents('/proc/loadavg')
and $load = floatval($load))
)
and $load > _ECRAN_SECURITE_LOAD // eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
and rand(0, (int) ($load * $load)) > _ECRAN_SECURITE_LOAD * _ECRAN_SECURITE_LOAD
) {
//https://webmasters.stackexchange.com/questions/65674/should-i-return-a-429-or-503-status-code-to-a-bot
header("HTTP/1.0 429 Too Many Requests");
header("Retry-After: 300");
header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: text/html");
header("Connection: close");
die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");
}