[SPIP] ~v3.2.3-->v3.2.4
[lhc/web/www.git] / www / config / ecran_securite.php
1 <?php
2
3 /*
4 * ecran_securite.php
5 * ------------------
6 */
7
8 define('_ECRAN_SECURITE', '1.3.11'); // 2019-04-08
9
10 /*
11 * Documentation : http://www.spip.net/fr_article4200.html
12 */
13
14 /*
15 * Test utilisateur
16 */
17 if (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 */
26 if (!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 */
33 if (!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 }
226 if (!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');
246 foreach ($_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]);
250 foreach ($_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]);
254 foreach ($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 */
268 foreach(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 */
278 if (preg_match(',^(.*/)?spip_acces_doc\.,', (string)$_SERVER['REQUEST_URI'])) {
279 $file = addslashes((string)$_GET['file']);
280 }
281
282 /*
283 * Pas d'inscription abusive
284 */
285 if (isset($_REQUEST['mode']) and isset($_REQUEST['page'])
286 and !in_array($_REQUEST['mode'], array("6forum", "1comite"))
287 and $_REQUEST['page'] == "identifiants")
288 $ecran_securite_raison = "identifiants";
289
290 /*
291 * Agenda joue à l'injection php
292 */
293 if (isset($_REQUEST['partie_cal'])
294 and $_REQUEST['partie_cal'] !== htmlentities((string)$_REQUEST['partie_cal']))
295 $ecran_securite_raison = "partie_cal";
296 if (isset($_REQUEST['echelle'])
297 and $_REQUEST['echelle'] !== htmlentities((string)$_REQUEST['echelle']))
298 $ecran_securite_raison = "echelle";
299
300 /*
301 * Espace privé
302 */
303 if (isset($_REQUEST['exec'])
304 and !preg_match(',^[\w-]+$,', (string)$_REQUEST['exec']))
305 $ecran_securite_raison = "exec";
306 if (isset($_REQUEST['cherche_auteur'])
307 and preg_match(',[<],', (string)$_REQUEST['cherche_auteur']))
308 $ecran_securite_raison = "cherche_auteur";
309 if (isset($_REQUEST['exec'])
310 and $_REQUEST['exec'] == 'auteurs'
311 and preg_match(',[<],', (string)$_REQUEST['recherche']))
312 $ecran_securite_raison = "recherche";
313 if (isset($_REQUEST['exec'])
314 and $_REQUEST['exec'] == 'info_plugin'
315 and preg_match(',[<],', (string)$_REQUEST['plugin']))
316 $ecran_securite_raison = "plugin";
317 if (isset($_REQUEST['exec'])
318 and $_REQUEST['exec'] == 'puce_statut'
319 and isset($_REQUEST['id'])
320 and !intval($_REQUEST['id']))
321 $ecran_securite_raison = "puce_statut";
322 if (isset($_REQUEST['action'])
323 and $_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 */
342 if (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 */
352 if (isset($_REQUEST['fond'])
353 and 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 */
359 if (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 */
367 if (_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 */
380 if (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 */
393 foreach (array('var_login') as $var)
394 if (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 */
400 if (!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 }
409 if (isset($_POST['tmp_lkojfghx3']))
410 $ecran_securite_raison = "gumblar";
411
412 /*
413 * Outils XML mal sécurisés < 2.0.9
414 */
415 if (isset($_REQUEST['transformer_xml']))
416 $ecran_securite_raison = "transformer_xml";
417
418 /*
419 * Outils XML mal sécurisés again
420 */
421 if (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 */
434 if (isset($_REQUEST['nom_sauvegarde'])
435 and strstr((string)$_REQUEST['nom_sauvegarde'], '/'))
436 $ecran_securite_raison = 'nom_sauvegarde manipulee';
437 if (isset($_REQUEST['znom_sauvegarde'])
438 and 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 */
446 if (isset($_REQUEST['op']) and isset($_REQUEST['page'])
447 and $_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 */
453 if (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 */
464 if (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 */
473 if (isset($_REQUEST['reinstall'])
474 and $_REQUEST['reinstall'] == 'oui')
475 $ecran_securite_raison = 'reinstall=oui';
476
477 /*
478 * Échappement xss referer
479 */
480 if (isset($_SERVER['HTTP_REFERER']))
481 $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
482
483
484 /*
485 * Echappement HTTP_X_FORWARDED_HOST
486 */
487 if (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 */
494 if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false or isset($_REQUEST['var_memotri'])){
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 */
503 if (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 */
518 if (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 */
530 if (!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 */
547 if (!defined('_ECRAN_SECURITE_LOAD'))
548 define('_ECRAN_SECURITE_LOAD', 4);
549
550 if (
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 }