8 define('_ECRAN_SECURITE', '1.4.1'); // 2021-03-12
11 * Documentation : http://www.spip.net/fr_article4200.html
17 if (isset($_GET['test_ecran_securite']))
18 $ecran_securite_raison = 'test '._ECRAN_SECURITE
;
20 if (file_exists($f = __DIR__
. DIRECTORY_SEPARATOR
. 'ecran_securite_options.php')) {
26 * var_isbot=0 peut etre utilise par un bot de monitoring pour surveiller la disponibilite d'un site vu par les users
27 * var_isbot=1 peut etre utilise pour monitorer la disponibilite pour les bots (sujets a 503 de delestage si
28 * le load depasse ECRAN_SECURITE_LOAD)
30 if (!defined('_IS_BOT') and isset($_GET['var_isbot'])){
31 define('_IS_BOT', $_GET['var_isbot'] ?
true : false);
35 * Détecteur de robot d'indexation
37 if (!defined('_IS_BOT')){
39 isset($_SERVER['HTTP_USER_AGENT'])
41 . implode ('|', array(
52 'MSIE 6\.0', // botnet 99,9% du temps
61 'adressendeutschland',
94 'facebookexternalhit',
112 'iltrovatore-setaccio',
127 'loadimpactpageanalyzer',
132 'mediapartners-google',
137 'netcraftsurveyagent',
197 'universalfeedparser',
227 (string)$_SERVER['HTTP_USER_AGENT'])
230 if (!defined('_IS_BOT_FRIEND')){
231 define('_IS_BOT_FRIEND',
232 isset($_SERVER['HTTP_USER_AGENT'])
234 ',' . implode('|', array(
235 'facebookexternalhit',
240 (string)$_SERVER['HTTP_USER_AGENT']
246 * Interdit de passer une variable id_article (ou id_xxx) qui ne
247 * soit pas numérique (ce qui bloque l'exploitation de divers trous
248 * de sécurité, dont celui de toutes les versions < 1.8.2f)
249 * (sauf pour id_table, qui n'est pas numérique jusqu'à [5743])
250 * (id_base est une variable de la config des widgets de WordPress)
252 $_exceptions = array('id_table','id_base','id_parent','id_article_pdf');
253 foreach ($_GET as $var => $val)
254 if ($_GET[$var] and strncmp($var, "id_", 3) == 0
255 and !in_array($var, $_exceptions))
256 $_GET[$var] = is_array($_GET[$var])?@array_map
('intval', $_GET[$var]):intval($_GET[$var]);
257 foreach ($_POST as $var => $val)
258 if ($_POST[$var] and strncmp($var, "id_", 3) == 0
259 and !in_array($var, $_exceptions))
260 $_POST[$var] = is_array($_POST[$var])?@array_map
('intval', $_POST[$var]):intval($_POST[$var]);
261 foreach ($GLOBALS as $var => $val)
262 if ($GLOBALS[$var] and strncmp($var, "id_", 3) == 0
263 and !in_array($var, $_exceptions))
264 $GLOBALS[$var] = is_array($GLOBALS[$var])?@array_map
('intval', $GLOBALS[$var]):intval($GLOBALS[$var]);
267 * Interdit la variable $cjpeg_command, qui était utilisée sans
268 * précaution dans certaines versions de dev (1.8b2 -> 1.8b5)
273 * Contrôle de quelques variables (XSS)
275 foreach(array('lang', 'var_recherche', 'aide', 'var_lang_r', 'lang_r', 'var_ajax_ancre', 'nom_fichier') as $var) {
276 if (isset($_GET[$var]))
277 $_REQUEST[$var] = $GLOBALS[$var] = $_GET[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_GET[$var]);
278 if (isset($_POST[$var]))
279 $_REQUEST[$var] = $GLOBALS[$var] = $_POST[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_POST[$var]);
283 * Filtre l'accès à spip_acces_doc (injection SQL en 1.8.2x)
285 if (preg_match(',^(.*/)?spip_acces_doc\.,', (string)$_SERVER['REQUEST_URI'])) {
286 $file = addslashes((string)$_GET['file']);
290 * Pas d'inscription abusive
292 if (isset($_REQUEST['mode']) and isset($_REQUEST['page'])
293 and !in_array($_REQUEST['mode'], array("6forum", "1comite"))
294 and $_REQUEST['page'] == "identifiants")
295 $ecran_securite_raison = "identifiants";
298 * Agenda joue à l'injection php
300 if (isset($_REQUEST['partie_cal'])
301 and $_REQUEST['partie_cal'] !== htmlentities((string)$_REQUEST['partie_cal']))
302 $ecran_securite_raison = "partie_cal";
303 if (isset($_REQUEST['echelle'])
304 and $_REQUEST['echelle'] !== htmlentities((string)$_REQUEST['echelle']))
305 $ecran_securite_raison = "echelle";
310 if (isset($_REQUEST['exec'])
311 and !preg_match(',^[\w-]+$,', (string)$_REQUEST['exec']))
312 $ecran_securite_raison = "exec";
313 if (isset($_REQUEST['cherche_auteur'])
314 and preg_match(',[<],', (string)$_REQUEST['cherche_auteur']))
315 $ecran_securite_raison = "cherche_auteur";
316 if (isset($_REQUEST['exec'])
317 and $_REQUEST['exec'] == 'auteurs'
318 and preg_match(',[<],', (string)$_REQUEST['recherche']))
319 $ecran_securite_raison = "recherche";
320 if (isset($_REQUEST['exec'])
321 and $_REQUEST['exec'] == 'info_plugin'
322 and preg_match(',[<],', (string)$_REQUEST['plugin']))
323 $ecran_securite_raison = "plugin";
324 if (isset($_REQUEST['exec'])
325 and $_REQUEST['exec'] == 'puce_statut'
326 and isset($_REQUEST['id'])
327 and !intval($_REQUEST['id']))
328 $ecran_securite_raison = "puce_statut";
329 if (isset($_REQUEST['action'])
330 and $_REQUEST['action'] == 'configurer') {
331 if (@file_exists
('inc_version.php')
332 or @file_exists
('ecrire/inc_version.php')) {
333 function action_configurer() {
334 include_spip('inc/autoriser');
335 if(!autoriser('configurer', _request('configuration'))) {
336 include_spip('inc/minipres');
337 echo minipres(_T('info_acces_interdit'));
340 require _DIR_RESTREINT
.'action/configurer.php';
341 action_configurer_dist();
345 if (isset($_REQUEST['action'])
346 and $_REQUEST['action'] == 'ordonner_liens_documents'
347 and isset($_REQUEST['ordre'])
348 and is_string($_REQUEST['ordre'])){
349 $ecran_securite_raison = "ordre a la chaine";
354 * Bloque les requêtes contenant %00 (manipulation d'include)
357 @get_magic_quotes_gpc
() ?
358 stripslashes(serialize($_REQUEST)) : serialize($_REQUEST),
361 $ecran_securite_raison = "%00";
364 * Bloque les requêtes fond=formulaire_
366 if (isset($_REQUEST['fond'])
367 and preg_match(',^formulaire_,i', $_REQUEST['fond']))
368 $ecran_securite_raison = "fond=formulaire_";
371 * Bloque les requêtes du type ?GLOBALS[type_urls]=toto (bug vieux php)
373 if (isset($_REQUEST['GLOBALS']))
374 $ecran_securite_raison = "GLOBALS[GLOBALS]";
377 * Bloque les requêtes des bots sur:
379 * les paginations entremélées
382 (isset($_REQUEST['echelle']) and isset($_REQUEST['partie_cal']) and isset($_REQUEST['type']))
383 or (strpos((string)$_SERVER['REQUEST_URI'], 'debut_') and preg_match(',[?&]debut_.*&debut_,', (string)$_SERVER['REQUEST_URI']))
384 or (isset($_REQUEST['calendrier_annee']) and strpos((string)$_SERVER['REQUEST_URI'], 'debut_') )
385 or (isset($_REQUEST['calendrier_annee']) and preg_match(',[?&]calendrier_annee=.*&calendrier_annee=,', (string)$_SERVER['REQUEST_URI']))
388 $ecran_securite_raison = "robot agenda/double pagination";
391 * Bloque une vieille page de tests de CFG (<1.11)
392 * Bloque un XSS sur une page inexistante
394 if (isset($_REQUEST['page'])) {
395 if ($_REQUEST['page'] == 'test_cfg')
396 $ecran_securite_raison = "test_cfg";
397 if ($_REQUEST['page'] !== htmlspecialchars((string)$_REQUEST['page']))
398 $ecran_securite_raison = "xsspage";
399 if ($_REQUEST['page'] == '404'
400 and isset($_REQUEST['erreur']))
401 $ecran_securite_raison = "xss404";
407 foreach (array('var_login') as $var)
408 if (isset($_REQUEST[$var]) and is_array($_REQUEST[$var]))
409 $ecran_securite_raison = "xss ".$var;
412 * Parade antivirale contre un cheval de troie
414 if (!function_exists('tmp_lkojfghx')) {
415 function tmp_lkojfghx() {}
416 function tmp_lkojfghx2($a = 0, $b = 0, $c = 0, $d = 0) {
417 // si jamais on est arrivé ici sur une erreur php
418 // et qu'un autre gestionnaire d'erreur est défini, l'appeller
419 if ($b && $GLOBALS['tmp_xhgfjokl'])
420 call_user_func($GLOBALS['tmp_xhgfjokl'], $a, $b, $c, $d);
423 if (isset($_POST['tmp_lkojfghx3']))
424 $ecran_securite_raison = "gumblar";
427 * Outils XML mal sécurisés < 2.0.9
429 if (isset($_REQUEST['transformer_xml']))
430 $ecran_securite_raison = "transformer_xml";
433 * Outils XML mal sécurisés again
435 if (isset($_REQUEST['var_url']) and $_REQUEST['var_url'] and isset($_REQUEST['exec']) and $_REQUEST['exec']=='valider_xml'){
436 $url = trim($_REQUEST['var_url']);
437 if (strncmp($url,'/',1)==0
438 or (($p=strpos($url,'..'))!==false AND strpos($url,'..',$p+
3)!==false)
439 or (($p=strpos($url,'..'))!==false AND strpos($url,'IMG',$p+
3)!==false)
440 or (strpos($url,'://')!==false or strpos($url,':\\')!==false)) {
441 $ecran_securite_raison = 'URL interdite pour var_url';
446 * Sauvegarde mal securisée < 2.0.9
448 if (isset($_REQUEST['nom_sauvegarde'])
449 and strstr((string)$_REQUEST['nom_sauvegarde'], '/'))
450 $ecran_securite_raison = 'nom_sauvegarde manipulee';
451 if (isset($_REQUEST['znom_sauvegarde'])
452 and strstr((string)$_REQUEST['znom_sauvegarde'], '/'))
453 $ecran_securite_raison = 'znom_sauvegarde manipulee';
457 * op permet des inclusions arbitraires ;
458 * on vérifie 'page' pour ne pas bloquer ... drupal
460 if (isset($_REQUEST['op']) and isset($_REQUEST['page'])
461 and $_REQUEST['op'] !== preg_replace('/[^\-\w]/', '', $_REQUEST['op']))
462 $ecran_securite_raison = 'op';
465 * Forms & Table ne se méfiait pas assez des uploads de fichiers
468 foreach($_FILES as $k => $v){
469 if (preg_match(',^fichier_\d+$,', $k)
470 and preg_match(',\.php,i', $v['name']))
475 * et Contact trop laxiste avec une variable externe
476 * on bloque pas le post pour eviter de perdre des donnees mais on unset la variable et c'est tout
478 if (isset($_REQUEST['pj_enregistrees_nom']) and $_REQUEST['pj_enregistrees_nom']){
479 unset($_REQUEST['pj_enregistrees_nom']);
480 unset($_GET['pj_enregistrees_nom']);
481 unset($_POST['pj_enregistrees_nom']);
485 * reinstall=oui un peu trop permissif
487 if (isset($_REQUEST['reinstall'])
488 and $_REQUEST['reinstall'] == 'oui')
489 $ecran_securite_raison = 'reinstall=oui';
492 * Pas d'action pendant l'install
494 if (isset($_REQUEST['exec']) and $_REQUEST['exec'] === 'install' and isset($_REQUEST['action'])) {
495 $ecran_securite_raison = 'install&action impossibles';
499 * Échappement xss referer
501 if (isset($_SERVER['HTTP_REFERER']))
502 $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
506 * Echappement HTTP_X_FORWARDED_HOST
508 if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
509 $_SERVER['HTTP_X_FORWARDED_HOST'] = strtr($_SERVER['HTTP_X_FORWARDED_HOST'], "<>?\"\{\}\$'` \r\n", '____________');
513 * Pas d'erreur dans l'erreur
515 if (isset($_REQUEST['var_erreur']) and isset($_REQUEST['page']) and $_REQUEST['page'] === 'login') {
516 if (strlen($_REQUEST['var_erreur']) !== strcspn($_REQUEST['var_erreur'], '<>'))
517 $ecran_securite_raison = 'var_erreur incorrecte';
522 * Réinjection des clés en html dans l'admin r19561
524 if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false or isset($_REQUEST['var_memotri'])){
525 $zzzz = implode("", array_keys($_REQUEST));
526 if (strlen($zzzz) != strcspn($zzzz, '<>"\''))
527 $ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
531 * Injection par connect
533 if (isset($_REQUEST['connect'])
535 // cas qui permettent de sortir d'un commentaire PHP
536 (strpos($_REQUEST['connect'], "?") !== false
537 or strpos($_REQUEST['connect'], "<") !== false
538 or strpos($_REQUEST['connect'], ">") !== false
539 or strpos($_REQUEST['connect'], "\n") !== false
540 or strpos($_REQUEST['connect'], "\r") !== false)
542 $ecran_securite_raison = "malformed connect argument";
546 * S'il y a une raison de mourir, mourons
548 if (isset($ecran_securite_raison)) {
549 header("HTTP/1.0 403 Forbidden");
550 header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
551 header("Cache-Control: no-cache, must-revalidate");
552 header("Pragma: no-cache");
553 header("Content-Type: text/html");
554 header("Connection: close");
555 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>");
559 * Un filtre filtrer_entites securise
561 if (!function_exists('filtre_filtrer_entites_dist')) {
562 function filtre_filtrer_entites_dist($t) {
563 include_spip('inc/texte');
564 return interdire_scripts(filtrer_entites($t));
576 * Bloque les bots quand le load déborde
578 if (!defined('_ECRAN_SECURITE_LOAD'))
579 define('_ECRAN_SECURITE_LOAD', 4);
582 defined('_ECRAN_SECURITE_LOAD')
583 and _ECRAN_SECURITE_LOAD
> 0
586 and $_SERVER['REQUEST_METHOD'] === 'GET'
588 (function_exists('sys_getloadavg')
589 and $load = sys_getloadavg()
591 and $load = array_shift($load)
594 (@is_readable
('/proc/loadavg')
595 and $load = file_get_contents('/proc/loadavg')
596 and $load = floatval($load)
599 and $load > _ECRAN_SECURITE_LOAD
// eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
600 and rand(0, $load * $load) > _ECRAN_SECURITE_LOAD
* _ECRAN_SECURITE_LOAD
602 //https://webmasters.stackexchange.com/questions/65674/should-i-return-a-429-or-503-status-code-to-a-bot
603 header("HTTP/1.0 429 Too Many Requests");
604 header("Retry-After: 300");
605 header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
606 header("Cache-Control: no-cache, must-revalidate");
607 header("Pragma: no-cache");
608 header("Content-Type: text/html");
609 header("Connection: close");
610 die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");