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