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

Last change on this file since 113872 was 113872, checked in by cedric@…, 6 months ago

Retour a une liste complete de bots telle qu'elle etait sur la version non incremente de la zone, et on incremente

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