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

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

Mise a jour de l'ecran de securite, v1.3.12

File size: 15.2 KB
Line 
1<?php
2
3/*
4 * ecran_securite.php
5 * ------------------
6 */
7
8define('_ECRAN_SECURITE', '1.3.12'); // 2019-09-16
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 * Pas d'action pendant l'install
479 */
480if (isset($_REQUEST['exec']) and $_REQUEST['exec'] === 'install' and isset($_REQUEST['action'])) {
481        $ecran_securite_raison = 'install&action impossibles';
482}
483
484/*
485 * Échappement xss referer
486 */
487if (isset($_SERVER['HTTP_REFERER']))
488        $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
489
490
491/*
492 * Echappement HTTP_X_FORWARDED_HOST
493 */
494if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
495        $_SERVER['HTTP_X_FORWARDED_HOST'] = strtr($_SERVER['HTTP_X_FORWARDED_HOST'], "<>?\"\{\}\$'` \r\n", '____________');
496
497
498/*
499 * Pas d'erreur dans l'erreur
500 */
501if (isset($_REQUEST['var_erreur']) and isset($_REQUEST['page']) and $_REQUEST['page'] === 'login') {
502        if (strlen($_REQUEST['var_erreur']) !== strcspn($_REQUEST['var_erreur'], '<>'))
503                $ecran_securite_raison = 'var_erreur incorrecte';
504}
505
506
507/*
508 * Réinjection des clés en html dans l'admin r19561
509 */
510if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false or isset($_REQUEST['var_memotri'])){
511        $zzzz = implode("", array_keys($_REQUEST));
512        if (strlen($zzzz) != strcspn($zzzz, '<>"\''))
513                $ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
514}
515
516/*
517 * Injection par connect
518 */
519if (isset($_REQUEST['connect'])
520        and
521        // cas qui permettent de sortir d'un commentaire PHP
522        (strpos($_REQUEST['connect'], "?") !== false
523         or strpos($_REQUEST['connect'], "<") !== false
524         or strpos($_REQUEST['connect'], ">") !== false
525         or strpos($_REQUEST['connect'], "\n") !== false
526         or strpos($_REQUEST['connect'], "\r") !== false)
527        ) {
528        $ecran_securite_raison = "malformed connect argument";
529}
530
531/*
532 * S'il y a une raison de mourir, mourons
533 */
534if (isset($ecran_securite_raison)) {
535        header("HTTP/1.0 403 Forbidden");
536        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
537        header("Cache-Control: no-cache, must-revalidate");
538        header("Pragma: no-cache");
539        header("Content-Type: text/html");
540        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>");
541}
542
543/*
544 * Un filtre filtrer_entites securise
545 */
546if (!function_exists('filtre_filtrer_entites_dist')) {
547        function filtre_filtrer_entites_dist($t) {
548                include_spip('inc/texte');
549                return interdire_scripts(filtrer_entites($t));
550        }
551}
552
553
554/*
555 * Fin sécurité
556 */
557
558
559
560/*
561 * Bloque les bots quand le load déborde
562 */
563if (!defined('_ECRAN_SECURITE_LOAD'))
564        define('_ECRAN_SECURITE_LOAD', 4);
565
566if (
567        defined('_ECRAN_SECURITE_LOAD')
568        and _ECRAN_SECURITE_LOAD > 0
569        and _IS_BOT
570        and !_IS_BOT_FRIEND
571        and $_SERVER['REQUEST_METHOD'] === 'GET'
572        and (
573                (function_exists('sys_getloadavg')
574                  and $load = sys_getloadavg()
575                  and is_array($load)
576                  and $load = array_shift($load)
577                )
578                or
579                (@is_readable('/proc/loadavg')
580                  and $load = file_get_contents('/proc/loadavg')
581                  and $load = floatval($load)
582                )
583        )
584        and $load > _ECRAN_SECURITE_LOAD // eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
585        and rand(0, $load * $load) > _ECRAN_SECURITE_LOAD * _ECRAN_SECURITE_LOAD
586) {
587        //https://webmasters.stackexchange.com/questions/65674/should-i-return-a-429-or-503-status-code-to-a-bot
588        header("HTTP/1.0 429 Too Many Requests");
589        header("Retry-After: 300");
590        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
591        header("Cache-Control: no-cache, must-revalidate");
592        header("Pragma: no-cache");
593        header("Content-Type: text/html");
594        die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");
595}
Note: See TracBrowser for help on using the repository browser.