source: spip-zone/_core_/securite/ecran_securite.php @ 113867

Last change on this file since 113867 was 113867, checked in by root, 2 months ago

Ecran de securite :

File size: 14.0 KB
Line 
1<?php
2
3/*
4 * ecran_securite.php
5 * ------------------
6 */
7
8define('_ECRAN_SECURITE', '1.3.9'); // 2019-02-12
9
10/*
11 * Documentation : http://www.spip.net/fr_article4200.html
12 */
13
14/*
15 * Test utilisateur
16 */
17if (isset($_GET['test_ecran_securite']))
18        $ecran_securite_raison = 'test '._ECRAN_SECURITE;
19
20/*
21 * Monitoring
22 * var_isbot=0 peut etre utilise par un bot de monitoring pour surveiller la disponibilite d'un site vu par les users
23 * var_isbot=1 peut etre utilise pour monitorer la disponibilite pour les bots (sujets a 503 de delestage si
24 * le load depasse ECRAN_SECURITE_LOAD)
25 */
26if (!defined('_IS_BOT') and isset($_GET['var_isbot'])){
27        define('_IS_BOT', $_GET['var_isbot'] ? true : false);
28}
29
30/*
31 * Détecteur de robot d'indexation
32 */
33if (!defined('_IS_BOT')){
34        define('_IS_BOT',
35                isset($_SERVER['HTTP_USER_AGENT'])
36                and preg_match(','
37                . implode ('|', array(
38                        // mots generiques
39                        'bot',
40                        'slurp',
41                        'crawler',
42                        'spider',
43                        'webvac',
44                        'yandex',
45                        'MSIE 6\.0', // botnet 99,9% du temps
46                        // UA plus cibles
47                        '200please',
48                        '80legs',
49                        'a6-indexer',
50                        'aboundex',
51                        'accoona',
52                        'addthis',
53                        'adressendeutschland',
54                        'alexa',
55                        'altavista',
56                        'analyticsseo',
57                        'archive',
58                        'aspseek',
59                        'baidu',
60                        'begunadvertising',
61                        'bingpreview',
62                        'bloglines',
63                        'browsershots',
64                        'bubing',
65                        'butterfly',
66                        'changedetection',
67                        'charlotte',
68                        'chilkat',
69                        'china',
70                        'coccoc',
71                        'crowsnest',
72                        'dataminr',
73                        'daumoa',
74                        'dlweb',
75                        'ec2linkfinder',
76                        'estyle',
77                        'ezooms',
78                        'facebookexternalhit',
79                        'facebookplatform',
80                        'fairshare',
81                        'feedfetcher',
82                        'feedfetcher-google',
83                        'feedly',
84                        'fetch',
85                        'flipboardproxy',
86                        'genieo',
87                        'google',
88                        'grapeshot',
89                        'hatena-useragent',
90                        'head',
91                        'hosttracker',
92                        'hubspot',
93                        'ia_archiver',
94                        'ichiro',
95                        'iltrovatore-setaccio',
96                        'immediatenet',
97                        'ina',
98                        'infegyatlas',
99                        'infohelfer',
100                        'instapaper',
101                        'jabse',
102                        'james',
103                        'kumkie',
104                        'linkdex',
105                        'linkfluence',
106                        'linkwalker',
107                        'litefinder',
108                        'loadimpactpageanalyzer',
109                        'luminate',
110                        'lycos',
111                        'lycosa',
112                        'mediapartners-google',
113                        'msai',
114                        'najdi',
115                        'netcraftsurveyagent',
116                        'netestate',
117                        'netseer',
118                        'nuhk',
119                        'owlin',
120                        'panscient',
121                        'parsijoo',
122                        'plukkie',
123                        'proximic',
124                        'qirina',
125                        'qualidator',
126                        'rambler',
127                        'readability',
128                        'sbsearch',
129                        'scooter',
130                        'scrapy',
131                        'scrubby',
132                        'scrubbybloglines',
133                        'shareaholic',
134                        'shopwiki',
135                        'sistrix',
136                        'sitechecker',
137                        'siteexplorer',
138                        'sogou',
139                        'special_archiver',
140                        'speedy',
141                        'spinn3r',
142                        'spreadtrum',
143                        'steeler',
144                        'subscriber',
145                        'suma',
146                        'superdownloads',
147                        'svenska-webbsido',
148                        'teoma',
149                        'thumbshots',
150                        'tineye',
151                        'trendiction',
152                        'tweetedtimes',
153                        'tweetmeme',
154                        'uaslinkchecker',
155                        'undrip',
156                        'unwindfetchor',
157                        'vedma',
158                        'vkshare',
159                        'vm',
160                        'wch',
161                        'webalta',
162                        'webcookies',
163                        'webthumbnail',
164                        'wesee',
165                        'wise-guys',
166                        'woko',
167                        'wotbox',
168                        'y!j-bri',
169                        'y!j-bro',
170                        'y!j-brw',
171                        'y!j-bsc',
172                        'yahoo',
173                        'yahoo!',
174                        'yahooysmcm',
175                        'yats',
176                        'yeti',
177                        'zeerch'
178                )) . ',i',
179                (string)$_SERVER['HTTP_USER_AGENT'])
180        );
181}
182if (!defined('_IS_BOT_FRIEND')){
183        define('_IS_BOT_FRIEND',
184                isset($_SERVER['HTTP_USER_AGENT'])
185                and preg_match(',' . implode ('|', array(
186                        'facebookexternalhit',
187                        'flipboardproxy'
188                )) . ',i',
189                (string)$_SERVER['HTTP_USER_AGENT'])
190        );
191}
192
193/*
194 * Interdit de passer une variable id_article (ou id_xxx) qui ne
195 * soit pas numérique (ce qui bloque l'exploitation de divers trous
196 * de sécurité, dont celui de toutes les versions < 1.8.2f)
197 * (sauf pour id_table, qui n'est pas numérique jusqu'à [5743])
198 * (id_base est une variable de la config des widgets de WordPress)
199 */
200$_exceptions = array('id_table','id_base','id_parent','id_article_pdf');
201foreach ($_GET as $var => $val)
202        if ($_GET[$var] and strncmp($var, "id_", 3) == 0
203                and !in_array($var, $_exceptions))
204                $_GET[$var] = is_array($_GET[$var])?@array_map('intval', $_GET[$var]):intval($_GET[$var]);
205foreach ($_POST as $var => $val)
206        if ($_POST[$var] and strncmp($var, "id_", 3) == 0
207                and !in_array($var, $_exceptions))
208                $_POST[$var] = is_array($_POST[$var])?@array_map('intval', $_POST[$var]):intval($_POST[$var]);
209foreach ($GLOBALS as $var => $val)
210        if ($GLOBALS[$var] and strncmp($var, "id_", 3) == 0
211                and !in_array($var, $_exceptions))
212                $GLOBALS[$var] = is_array($GLOBALS[$var])?@array_map('intval', $GLOBALS[$var]):intval($GLOBALS[$var]);
213
214/*
215 * Interdit la variable $cjpeg_command, qui était utilisée sans
216 * précaution dans certaines versions de dev (1.8b2 -> 1.8b5)
217 */
218$cjpeg_command = '';
219
220/*
221 * Contrôle de quelques variables (XSS)
222 */
223foreach(array('lang', 'var_recherche', 'aide', 'var_lang_r', 'lang_r', 'var_ajax_ancre', 'nom_fichier') as $var) {
224        if (isset($_GET[$var]))
225                $_REQUEST[$var] = $GLOBALS[$var] = $_GET[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_GET[$var]);
226        if (isset($_POST[$var]))
227                $_REQUEST[$var] = $GLOBALS[$var] = $_POST[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_POST[$var]);
228}
229
230/*
231 * Filtre l'accès à spip_acces_doc (injection SQL en 1.8.2x)
232 */
233if (preg_match(',^(.*/)?spip_acces_doc\.,', (string)$_SERVER['REQUEST_URI'])) {
234        $file = addslashes((string)$_GET['file']);
235}
236
237/*
238 * Pas d'inscription abusive
239 */
240if (isset($_REQUEST['mode']) and isset($_REQUEST['page'])
241and !in_array($_REQUEST['mode'], array("6forum", "1comite"))
242and $_REQUEST['page'] == "identifiants")
243        $ecran_securite_raison = "identifiants";
244
245/*
246 * Agenda joue à l'injection php
247 */
248if (isset($_REQUEST['partie_cal'])
249and $_REQUEST['partie_cal'] !== htmlentities((string)$_REQUEST['partie_cal']))
250        $ecran_securite_raison = "partie_cal";
251if (isset($_REQUEST['echelle'])
252and $_REQUEST['echelle'] !== htmlentities((string)$_REQUEST['echelle']))
253        $ecran_securite_raison = "echelle";
254
255/*
256 * Espace privé
257 */
258if (isset($_REQUEST['exec'])
259and !preg_match(',^[\w-]+$,', (string)$_REQUEST['exec']))
260        $ecran_securite_raison = "exec";
261if (isset($_REQUEST['cherche_auteur'])
262and preg_match(',[<],', (string)$_REQUEST['cherche_auteur']))
263        $ecran_securite_raison = "cherche_auteur";
264if (isset($_REQUEST['exec'])
265and $_REQUEST['exec'] == 'auteurs'
266and preg_match(',[<],', (string)$_REQUEST['recherche']))
267        $ecran_securite_raison = "recherche";
268if (isset($_REQUEST['exec'])
269and $_REQUEST['exec'] == 'info_plugin'
270and preg_match(',[<],', (string)$_REQUEST['plugin']))
271        $ecran_securite_raison = "plugin";
272if (isset($_REQUEST['exec'])
273and $_REQUEST['exec'] == 'puce_statut'
274and isset($_REQUEST['id'])
275and !intval($_REQUEST['id']))
276        $ecran_securite_raison = "puce_statut";
277if (isset($_REQUEST['action'])
278and $_REQUEST['action'] == 'configurer') {
279        if (@file_exists('inc_version.php')
280        or @file_exists('ecrire/inc_version.php')) {
281                function action_configurer() {
282                        include_spip('inc/autoriser');
283                        if(!autoriser('configurer', _request('configuration'))) {
284                                include_spip('inc/minipres');
285                                echo minipres(_T('info_acces_interdit'));
286                                exit;
287                        }
288                        require _DIR_RESTREINT.'action/configurer.php';
289                        action_configurer_dist();
290                }
291        }
292}
293
294/*
295 * Bloque les requêtes contenant %00 (manipulation d'include)
296 */
297if (strpos(
298        @get_magic_quotes_gpc() ?
299                stripslashes(serialize($_REQUEST)) : serialize($_REQUEST),
300        chr(0)
301) !== false)
302        $ecran_securite_raison = "%00";
303
304/*
305 * Bloque les requêtes fond=formulaire_
306 */
307if (isset($_REQUEST['fond'])
308and preg_match(',^formulaire_,i', $_REQUEST['fond']))
309        $ecran_securite_raison = "fond=formulaire_";
310
311/*
312 * Bloque les requêtes du type ?GLOBALS[type_urls]=toto (bug vieux php)
313 */
314if (isset($_REQUEST['GLOBALS']))
315        $ecran_securite_raison = "GLOBALS[GLOBALS]";
316
317/*
318 * Bloque les requêtes des bots sur:
319 * les agenda
320 * les paginations entremélées
321 */
322if (_IS_BOT and (
323        (isset($_REQUEST['echelle']) and isset($_REQUEST['partie_cal']) and isset($_REQUEST['type']))
324        or (strpos((string)$_SERVER['REQUEST_URI'], 'debut_') and preg_match(',[?&]debut_.*&debut_,', (string)$_SERVER['REQUEST_URI']))
325                or (isset($_REQUEST['calendrier_annee']) and strpos((string)$_SERVER['REQUEST_URI'], 'debut_') )
326                or (isset($_REQUEST['calendrier_annee']) and preg_match(',[?&]calendrier_annee=.*&calendrier_annee=,', (string)$_SERVER['REQUEST_URI']))
327)
328)
329        $ecran_securite_raison = "robot agenda/double pagination";
330
331/*
332 * Bloque une vieille page de tests de CFG (<1.11)
333 * Bloque un XSS sur une page inexistante
334 */
335if (isset($_REQUEST['page'])) {
336        if ($_REQUEST['page'] == 'test_cfg')
337                $ecran_securite_raison = "test_cfg";
338        if ($_REQUEST['page'] !== htmlspecialchars((string)$_REQUEST['page']))
339                $ecran_securite_raison = "xsspage";
340        if ($_REQUEST['page'] == '404'
341        and isset($_REQUEST['erreur']))
342                $ecran_securite_raison = "xss404";
343}
344
345/*
346 * XSS par array
347 */
348foreach (array('var_login') as $var)
349if (isset($_REQUEST[$var]) and is_array($_REQUEST[$var]))
350        $ecran_securite_raison = "xss ".$var;
351
352/*
353 * Parade antivirale contre un cheval de troie
354 */
355if (!function_exists('tmp_lkojfghx')) {
356        function tmp_lkojfghx() {}
357        function tmp_lkojfghx2($a = 0, $b = 0, $c = 0, $d = 0) {
358                // si jamais on est arrivé ici sur une erreur php
359                // et qu'un autre gestionnaire d'erreur est défini, l'appeller
360                if ($b && $GLOBALS['tmp_xhgfjokl'])
361                        call_user_func($GLOBALS['tmp_xhgfjokl'], $a, $b, $c, $d);
362        }
363}
364if (isset($_POST['tmp_lkojfghx3']))
365        $ecran_securite_raison = "gumblar";
366
367/*
368 * Outils XML mal sécurisés < 2.0.9
369 */
370if (isset($_REQUEST['transformer_xml']))
371        $ecran_securite_raison = "transformer_xml";
372
373/*
374 * Outils XML mal sécurisés again
375 */
376if (isset($_REQUEST['var_url']) and $_REQUEST['var_url'] and isset($_REQUEST['exec']) and $_REQUEST['exec']=='valider_xml'){
377        $url = trim($_REQUEST['var_url']);
378        if (strncmp($url,'/',1)==0
379          or (($p=strpos($url,'..'))!==false AND strpos($url,'..',$p+3)!==false)
380          or (($p=strpos($url,'..'))!==false AND strpos($url,'IMG',$p+3)!==false)
381                or (strpos($url,'://')!==false or strpos($url,':\\')!==false)) {
382                $ecran_securite_raison = 'URL interdite pour var_url';
383        }
384}
385
386/*
387 * Sauvegarde mal securisée < 2.0.9
388 */
389if (isset($_REQUEST['nom_sauvegarde'])
390and strstr((string)$_REQUEST['nom_sauvegarde'], '/'))
391        $ecran_securite_raison = 'nom_sauvegarde manipulee';
392if (isset($_REQUEST['znom_sauvegarde'])
393and strstr((string)$_REQUEST['znom_sauvegarde'], '/'))
394        $ecran_securite_raison = 'znom_sauvegarde manipulee';
395
396
397/*
398 * op permet des inclusions arbitraires ;
399 * on vérifie 'page' pour ne pas bloquer ... drupal
400 */
401if (isset($_REQUEST['op']) and isset($_REQUEST['page'])
402and $_REQUEST['op'] !== preg_replace('/[^\-\w]/', '', $_REQUEST['op']))
403        $ecran_securite_raison = 'op';
404
405/*
406 * Forms & Table ne se méfiait pas assez des uploads de fichiers
407 */
408if (count($_FILES)){
409        foreach($_FILES as $k => $v){
410                 if (preg_match(',^fichier_\d+$,', $k)
411                 and preg_match(',\.php,i', $v['name']))
412                        unset($_FILES[$k]);
413        }
414}
415/*
416 * et Contact trop laxiste avec une variable externe
417 * on bloque pas le post pour eviter de perdre des donnees mais on unset la variable et c'est tout
418 */
419if (isset($_REQUEST['pj_enregistrees_nom']) and $_REQUEST['pj_enregistrees_nom']){
420        unset($_REQUEST['pj_enregistrees_nom']);
421        unset($_GET['pj_enregistrees_nom']);
422        unset($_POST['pj_enregistrees_nom']);
423}
424
425/*
426 * reinstall=oui un peu trop permissif
427 */
428if (isset($_REQUEST['reinstall'])
429and $_REQUEST['reinstall'] == 'oui')
430        $ecran_securite_raison = 'reinstall=oui';
431
432/*
433 * Échappement xss referer
434 */
435if (isset($_SERVER['HTTP_REFERER']))
436        $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
437
438
439/*
440 * Echappement HTTP_X_FORWARDED_HOST
441 */
442if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
443        $_SERVER['HTTP_X_FORWARDED_HOST'] = strtr($_SERVER['HTTP_X_FORWARDED_HOST'], "<>?\"\{\}\$'` \r\n", '____________');
444
445
446/*
447 * Réinjection des clés en html dans l'admin r19561
448 */
449if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false){
450        $zzzz = implode("", array_keys($_REQUEST));
451        if (strlen($zzzz) != strcspn($zzzz, '<>"\''))
452                $ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
453}
454
455/*
456 * Injection par connect
457 */
458if (isset($_REQUEST['connect'])
459        and
460        // cas qui permettent de sortir d'un commentaire PHP
461        (strpos($_REQUEST['connect'], "?") !== false
462         or strpos($_REQUEST['connect'], "<") !== false
463         or strpos($_REQUEST['connect'], ">") !== false
464         or strpos($_REQUEST['connect'], "\n") !== false
465         or strpos($_REQUEST['connect'], "\r") !== false)
466        ) {
467        $ecran_securite_raison = "malformed connect argument";
468}
469
470/*
471 * S'il y a une raison de mourir, mourons
472 */
473if (isset($ecran_securite_raison)) {
474        header("HTTP/1.0 403 Forbidden");
475        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
476        header("Cache-Control: no-cache, must-revalidate");
477        header("Pragma: no-cache");
478        header("Content-Type: text/html");
479        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>");
480}
481
482/*
483 * Un filtre filtrer_entites securise
484 */
485if (!function_exists('filtre_filtrer_entites_dist')) {
486        function filtre_filtrer_entites_dist($t) {
487                include_spip('inc/texte');
488                return interdire_scripts(filtrer_entites($t));
489        }
490}
491
492
493/*
494 * Fin sécurité
495 */
496
497
498
499/*
500 * Bloque les bots quand le load déborde
501 */
502if (!defined('_ECRAN_SECURITE_LOAD'))
503        define('_ECRAN_SECURITE_LOAD', 4);
504
505if (
506        defined('_ECRAN_SECURITE_LOAD')
507        and _ECRAN_SECURITE_LOAD > 0
508        and _IS_BOT
509        and !_IS_BOT_FRIEND
510        and $_SERVER['REQUEST_METHOD'] === 'GET'
511        and (
512                (function_exists('sys_getloadavg')
513                  and $load = sys_getloadavg()
514                  and is_array($load)
515                  and $load = array_shift($load)
516                )
517                or
518                (@is_readable('/proc/loadavg')
519                  and $load = file_get_contents('/proc/loadavg')
520                  and $load = floatval($load)
521                )
522        )
523        and $load > _ECRAN_SECURITE_LOAD // eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
524        and rand(0, $load * $load) > _ECRAN_SECURITE_LOAD * _ECRAN_SECURITE_LOAD
525) {
526        //https://webmasters.stackexchange.com/questions/65674/should-i-return-a-429-or-503-status-code-to-a-bot
527        header("HTTP/1.0 429 Too Many Requests");
528        header("Retry-After: 300");
529        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
530        header("Cache-Control: no-cache, must-revalidate");
531        header("Pragma: no-cache");
532        header("Content-Type: text/html");
533        die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");
534}
Note: See TracBrowser for help on using the repository browser.