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

Last change on this file was 118902, checked in by cedric@…, 7 weeks ago

Feu les temps modernes (report du core)

File size: 15.4 KB
Line 
1<?php
2
3/*
4 * ecran_securite.php
5 * ------------------
6 */
7
8define('_ECRAN_SECURITE', '1.3.13'); // 2019-12-04
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}
338if (isset($_REQUEST['action'])
339and $_REQUEST['action'] == 'ordonner_liens_documents'
340and isset($_REQUEST['ordre'])
341and is_string($_REQUEST['ordre'])){
342        $ecran_securite_raison = "ordre a la chaine";
343}
344
345
346/*
347 * Bloque les requêtes contenant %00 (manipulation d'include)
348 */
349if (strpos(
350        @get_magic_quotes_gpc() ?
351                stripslashes(serialize($_REQUEST)) : serialize($_REQUEST),
352        chr(0)
353) !== false)
354        $ecran_securite_raison = "%00";
355
356/*
357 * Bloque les requêtes fond=formulaire_
358 */
359if (isset($_REQUEST['fond'])
360and preg_match(',^formulaire_,i', $_REQUEST['fond']))
361        $ecran_securite_raison = "fond=formulaire_";
362
363/*
364 * Bloque les requêtes du type ?GLOBALS[type_urls]=toto (bug vieux php)
365 */
366if (isset($_REQUEST['GLOBALS']))
367        $ecran_securite_raison = "GLOBALS[GLOBALS]";
368
369/*
370 * Bloque les requêtes des bots sur:
371 * les agenda
372 * les paginations entremélées
373 */
374if (_IS_BOT and (
375        (isset($_REQUEST['echelle']) and isset($_REQUEST['partie_cal']) and isset($_REQUEST['type']))
376        or (strpos((string)$_SERVER['REQUEST_URI'], 'debut_') and preg_match(',[?&]debut_.*&debut_,', (string)$_SERVER['REQUEST_URI']))
377                or (isset($_REQUEST['calendrier_annee']) and strpos((string)$_SERVER['REQUEST_URI'], 'debut_') )
378                or (isset($_REQUEST['calendrier_annee']) and preg_match(',[?&]calendrier_annee=.*&calendrier_annee=,', (string)$_SERVER['REQUEST_URI']))
379)
380)
381        $ecran_securite_raison = "robot agenda/double pagination";
382
383/*
384 * Bloque une vieille page de tests de CFG (<1.11)
385 * Bloque un XSS sur une page inexistante
386 */
387if (isset($_REQUEST['page'])) {
388        if ($_REQUEST['page'] == 'test_cfg')
389                $ecran_securite_raison = "test_cfg";
390        if ($_REQUEST['page'] !== htmlspecialchars((string)$_REQUEST['page']))
391                $ecran_securite_raison = "xsspage";
392        if ($_REQUEST['page'] == '404'
393        and isset($_REQUEST['erreur']))
394                $ecran_securite_raison = "xss404";
395}
396
397/*
398 * XSS par array
399 */
400foreach (array('var_login') as $var)
401if (isset($_REQUEST[$var]) and is_array($_REQUEST[$var]))
402        $ecran_securite_raison = "xss ".$var;
403
404/*
405 * Parade antivirale contre un cheval de troie
406 */
407if (!function_exists('tmp_lkojfghx')) {
408        function tmp_lkojfghx() {}
409        function tmp_lkojfghx2($a = 0, $b = 0, $c = 0, $d = 0) {
410                // si jamais on est arrivé ici sur une erreur php
411                // et qu'un autre gestionnaire d'erreur est défini, l'appeller
412                if ($b && $GLOBALS['tmp_xhgfjokl'])
413                        call_user_func($GLOBALS['tmp_xhgfjokl'], $a, $b, $c, $d);
414        }
415}
416if (isset($_POST['tmp_lkojfghx3']))
417        $ecran_securite_raison = "gumblar";
418
419/*
420 * Outils XML mal sécurisés < 2.0.9
421 */
422if (isset($_REQUEST['transformer_xml']))
423        $ecran_securite_raison = "transformer_xml";
424
425/*
426 * Outils XML mal sécurisés again
427 */
428if (isset($_REQUEST['var_url']) and $_REQUEST['var_url'] and isset($_REQUEST['exec']) and $_REQUEST['exec']=='valider_xml'){
429        $url = trim($_REQUEST['var_url']);
430        if (strncmp($url,'/',1)==0
431          or (($p=strpos($url,'..'))!==false AND strpos($url,'..',$p+3)!==false)
432          or (($p=strpos($url,'..'))!==false AND strpos($url,'IMG',$p+3)!==false)
433                or (strpos($url,'://')!==false or strpos($url,':\\')!==false)) {
434                $ecran_securite_raison = 'URL interdite pour var_url';
435        }
436}
437
438/*
439 * Sauvegarde mal securisée < 2.0.9
440 */
441if (isset($_REQUEST['nom_sauvegarde'])
442and strstr((string)$_REQUEST['nom_sauvegarde'], '/'))
443        $ecran_securite_raison = 'nom_sauvegarde manipulee';
444if (isset($_REQUEST['znom_sauvegarde'])
445and strstr((string)$_REQUEST['znom_sauvegarde'], '/'))
446        $ecran_securite_raison = 'znom_sauvegarde manipulee';
447
448
449/*
450 * op permet des inclusions arbitraires ;
451 * on vérifie 'page' pour ne pas bloquer ... drupal
452 */
453if (isset($_REQUEST['op']) and isset($_REQUEST['page'])
454and $_REQUEST['op'] !== preg_replace('/[^\-\w]/', '', $_REQUEST['op']))
455        $ecran_securite_raison = 'op';
456
457/*
458 * Forms & Table ne se méfiait pas assez des uploads de fichiers
459 */
460if (count($_FILES)){
461        foreach($_FILES as $k => $v){
462                 if (preg_match(',^fichier_\d+$,', $k)
463                 and preg_match(',\.php,i', $v['name']))
464                        unset($_FILES[$k]);
465        }
466}
467/*
468 * et Contact trop laxiste avec une variable externe
469 * on bloque pas le post pour eviter de perdre des donnees mais on unset la variable et c'est tout
470 */
471if (isset($_REQUEST['pj_enregistrees_nom']) and $_REQUEST['pj_enregistrees_nom']){
472        unset($_REQUEST['pj_enregistrees_nom']);
473        unset($_GET['pj_enregistrees_nom']);
474        unset($_POST['pj_enregistrees_nom']);
475}
476
477/*
478 * reinstall=oui un peu trop permissif
479 */
480if (isset($_REQUEST['reinstall'])
481and $_REQUEST['reinstall'] == 'oui')
482        $ecran_securite_raison = 'reinstall=oui';
483
484/*
485 * Pas d'action pendant l'install
486 */
487if (isset($_REQUEST['exec']) and $_REQUEST['exec'] === 'install' and isset($_REQUEST['action'])) {
488        $ecran_securite_raison = 'install&action impossibles';
489}
490
491/*
492 * Échappement xss referer
493 */
494if (isset($_SERVER['HTTP_REFERER']))
495        $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
496
497
498/*
499 * Echappement HTTP_X_FORWARDED_HOST
500 */
501if (isset($_SERVER['HTTP_X_FORWARDED_HOST']))
502        $_SERVER['HTTP_X_FORWARDED_HOST'] = strtr($_SERVER['HTTP_X_FORWARDED_HOST'], "<>?\"\{\}\$'` \r\n", '____________');
503
504
505/*
506 * Pas d'erreur dans l'erreur
507 */
508if (isset($_REQUEST['var_erreur']) and isset($_REQUEST['page']) and $_REQUEST['page'] === 'login') {
509        if (strlen($_REQUEST['var_erreur']) !== strcspn($_REQUEST['var_erreur'], '<>'))
510                $ecran_securite_raison = 'var_erreur incorrecte';
511}
512
513
514/*
515 * Réinjection des clés en html dans l'admin r19561
516 */
517if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false or isset($_REQUEST['var_memotri'])){
518        $zzzz = implode("", array_keys($_REQUEST));
519        if (strlen($zzzz) != strcspn($zzzz, '<>"\''))
520                $ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
521}
522
523/*
524 * Injection par connect
525 */
526if (isset($_REQUEST['connect'])
527        and
528        // cas qui permettent de sortir d'un commentaire PHP
529        (strpos($_REQUEST['connect'], "?") !== false
530         or strpos($_REQUEST['connect'], "<") !== false
531         or strpos($_REQUEST['connect'], ">") !== false
532         or strpos($_REQUEST['connect'], "\n") !== false
533         or strpos($_REQUEST['connect'], "\r") !== false)
534        ) {
535        $ecran_securite_raison = "malformed connect argument";
536}
537
538/*
539 * S'il y a une raison de mourir, mourons
540 */
541if (isset($ecran_securite_raison)) {
542        header("HTTP/1.0 403 Forbidden");
543        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
544        header("Cache-Control: no-cache, must-revalidate");
545        header("Pragma: no-cache");
546        header("Content-Type: text/html");
547        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>");
548}
549
550/*
551 * Un filtre filtrer_entites securise
552 */
553if (!function_exists('filtre_filtrer_entites_dist')) {
554        function filtre_filtrer_entites_dist($t) {
555                include_spip('inc/texte');
556                return interdire_scripts(filtrer_entites($t));
557        }
558}
559
560
561/*
562 * Fin sécurité
563 */
564
565
566
567/*
568 * Bloque les bots quand le load déborde
569 */
570if (!defined('_ECRAN_SECURITE_LOAD'))
571        define('_ECRAN_SECURITE_LOAD', 4);
572
573if (
574        defined('_ECRAN_SECURITE_LOAD')
575        and _ECRAN_SECURITE_LOAD > 0
576        and _IS_BOT
577        and !_IS_BOT_FRIEND
578        and $_SERVER['REQUEST_METHOD'] === 'GET'
579        and (
580                (function_exists('sys_getloadavg')
581                  and $load = sys_getloadavg()
582                  and is_array($load)
583                  and $load = array_shift($load)
584                )
585                or
586                (@is_readable('/proc/loadavg')
587                  and $load = file_get_contents('/proc/loadavg')
588                  and $load = floatval($load)
589                )
590        )
591        and $load > _ECRAN_SECURITE_LOAD // eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
592        and rand(0, $load * $load) > _ECRAN_SECURITE_LOAD * _ECRAN_SECURITE_LOAD
593) {
594        //https://webmasters.stackexchange.com/questions/65674/should-i-return-a-429-or-503-status-code-to-a-bot
595        header("HTTP/1.0 429 Too Many Requests");
596        header("Retry-After: 300");
597        header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
598        header("Cache-Control: no-cache, must-revalidate");
599        header("Pragma: no-cache");
600        header("Content-Type: text/html");
601        die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");
602}
603
Note: See TracBrowser for help on using the repository browser.