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

Last change on this file since 112406 was 112406, checked in by booz@…, 2 months ago

encore des lecteurs de podcast et des bots

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