[SPIP] v3.0.22-->v3.0.24
[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.2.7'); // 2016-09-30
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 * Détecteur de robot d'indexation
31 */
32 if (!defined('_IS_BOT'))
33 define('_IS_BOT',
34 isset($_SERVER['HTTP_USER_AGENT'])
35 and preg_match(
36 // mots generiques
37 ',bot|slurp|crawler|spider|webvac|yandex|'
38 // MSIE 6.0 est un botnet 99,9% du temps, on traite donc ce USER_AGENT comme un bot
39 . 'MSIE 6\.0|'
40 // UA plus cibles
41 . '80legs|accoona|AltaVista|ASPSeek|Baidu|Charlotte|EC2LinkFinder|eStyle|flipboard|hootsuite|FunWebProducts|Google|Genieo|INA dlweb|InfegyAtlas|Java VM|LiteFinder|Lycos|MetaURI|Moreover|Rambler|Scooter|ScrubbyBloglines|Yahoo|Yeti'
42 . ',i', (string) $_SERVER['HTTP_USER_AGENT'])
43 );
44
45 /*
46 * Interdit de passer une variable id_article (ou id_xxx) qui ne
47 * soit pas numérique (ce qui bloque l'exploitation de divers trous
48 * de sécurité, dont celui de toutes les versions < 1.8.2f)
49 * (sauf pour id_table, qui n'est pas numérique jusqu'à [5743])
50 * (id_base est une variable de la config des widgets de WordPress)
51 */
52 foreach ($_GET as $var => $val)
53 if ($_GET[$var] and strncmp($var, "id_", 3) == 0
54 and !in_array($var, array('id_table', 'id_base')))
55 $_GET[$var] = is_array($_GET[$var])?@array_map('intval', $_GET[$var]):intval($_GET[$var]);
56 foreach ($_POST as $var => $val)
57 if ($_POST[$var] and strncmp($var, "id_", 3) == 0
58 and !in_array($var, array('id_table', 'id_base')))
59 $_POST[$var] = is_array($_POST[$var])?@array_map('intval', $_POST[$var]):intval($_POST[$var]);
60 foreach ($GLOBALS as $var => $val)
61 if ($GLOBALS[$var] and strncmp($var, "id_", 3) == 0
62 and !in_array($var, array('id_table', 'id_base')))
63 $GLOBALS[$var] = is_array($GLOBALS[$var])?@array_map('intval', $GLOBALS[$var]):intval($GLOBALS[$var]);
64
65 /*
66 * Interdit la variable $cjpeg_command, qui était utilisée sans
67 * précaution dans certaines versions de dev (1.8b2 -> 1.8b5)
68 */
69 $cjpeg_command = '';
70
71 /*
72 * Contrôle de quelques variables (XSS)
73 */
74 foreach(array('lang', 'var_recherche', 'aide', 'var_lang_r', 'lang_r', 'var_ajax_ancre') as $var) {
75 if (isset($_GET[$var]))
76 $_REQUEST[$var] = $GLOBALS[$var] = $_GET[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_GET[$var]);
77 if (isset($_POST[$var]))
78 $_REQUEST[$var] = $GLOBALS[$var] = $_POST[$var] = preg_replace(',[^\w\,/#&;-]+,', ' ', (string)$_POST[$var]);
79 }
80
81 /*
82 * Filtre l'accès à spip_acces_doc (injection SQL en 1.8.2x)
83 */
84 if (preg_match(',^(.*/)?spip_acces_doc\.,', (string)$_SERVER['REQUEST_URI'])) {
85 $file = addslashes((string)$_GET['file']);
86 }
87
88 /*
89 * Pas d'inscription abusive
90 */
91 if (isset($_REQUEST['mode']) and isset($_REQUEST['page'])
92 and !in_array($_REQUEST['mode'], array("6forum", "1comite"))
93 and $_REQUEST['page'] == "identifiants")
94 $ecran_securite_raison = "identifiants";
95
96 /*
97 * Agenda joue à l'injection php
98 */
99 if (isset($_REQUEST['partie_cal'])
100 and $_REQUEST['partie_cal'] !== htmlentities((string)$_REQUEST['partie_cal']))
101 $ecran_securite_raison = "partie_cal";
102 if (isset($_REQUEST['echelle'])
103 and $_REQUEST['echelle'] !== htmlentities((string)$_REQUEST['echelle']))
104 $ecran_securite_raison = "echelle";
105
106 /*
107 * Espace privé
108 */
109 if (isset($_REQUEST['exec'])
110 and !preg_match(',^[\w-]+$,', (string)$_REQUEST['exec']))
111 $ecran_securite_raison = "exec";
112 if (isset($_REQUEST['cherche_auteur'])
113 and preg_match(',[<],', (string)$_REQUEST['cherche_auteur']))
114 $ecran_securite_raison = "cherche_auteur";
115 if (isset($_REQUEST['exec'])
116 and $_REQUEST['exec'] == 'auteurs'
117 and preg_match(',[<],', (string)$_REQUEST['recherche']))
118 $ecran_securite_raison = "recherche";
119 if (isset($_REQUEST['action'])
120 and $_REQUEST['action'] == 'configurer') {
121 if (@file_exists('inc_version.php')
122 or @file_exists('ecrire/inc_version.php')) {
123 function action_configurer() {
124 include_spip('inc/autoriser');
125 if(!autoriser('configurer', _request('configuration'))) {
126 include_spip('inc/minipres');
127 echo minipres(_T('info_acces_interdit'));
128 exit;
129 }
130 require _DIR_RESTREINT.'action/configurer.php';
131 action_configurer_dist();
132 }
133 }
134 }
135
136 /*
137 * Bloque les requêtes contenant %00 (manipulation d'include)
138 */
139 if (strpos(
140 @get_magic_quotes_gpc() ?
141 stripslashes(serialize($_REQUEST)) : serialize($_REQUEST),
142 chr(0)
143 ) !== false)
144 $ecran_securite_raison = "%00";
145
146 /*
147 * Bloque les requêtes fond=formulaire_
148 */
149 if (isset($_REQUEST['fond'])
150 and preg_match(',^formulaire_,i', $_REQUEST['fond']))
151 $ecran_securite_raison = "fond=formulaire_";
152
153 /*
154 * Bloque les requêtes du type ?GLOBALS[type_urls]=toto (bug vieux php)
155 */
156 if (isset($_REQUEST['GLOBALS']))
157 $ecran_securite_raison = "GLOBALS[GLOBALS]";
158
159 /*
160 * Bloque les requêtes des bots sur:
161 * les agenda
162 * les paginations entremélées
163 */
164 if (_IS_BOT and (
165 (isset($_REQUEST['echelle']) and isset($_REQUEST['partie_cal']) and isset($_REQUEST['type']))
166 or (strpos((string)$_SERVER['REQUEST_URI'], 'debut_') and preg_match(',[?&]debut_.*&debut_,', (string)$_SERVER['REQUEST_URI']))
167 )
168 )
169 $ecran_securite_raison = "robot agenda/double pagination";
170
171 /*
172 * Bloque une vieille page de tests de CFG (<1.11)
173 * Bloque un XSS sur une page inexistante
174 */
175 if (isset($_REQUEST['page'])) {
176 if ($_REQUEST['page'] == 'test_cfg')
177 $ecran_securite_raison = "test_cfg";
178 if ($_REQUEST['page'] !== htmlspecialchars((string)$_REQUEST['page']))
179 $ecran_securite_raison = "xsspage";
180 if ($_REQUEST['page'] == '404'
181 and isset($_REQUEST['erreur']))
182 $ecran_securite_raison = "xss404";
183 }
184
185 /*
186 * XSS par array
187 */
188 foreach (array('var_login') as $var)
189 if (isset($_REQUEST[$var]) and is_array($_REQUEST[$var]))
190 $ecran_securite_raison = "xss ".$var;
191
192 /*
193 * Parade antivirale contre un cheval de troie
194 */
195 if (!function_exists('tmp_lkojfghx')) {
196 function tmp_lkojfghx() {}
197 function tmp_lkojfghx2($a = 0, $b = 0, $c = 0, $d = 0) {
198 // si jamais on est arrivé ici sur une erreur php
199 // et qu'un autre gestionnaire d'erreur est défini, l'appeller
200 if ($b && $GLOBALS['tmp_xhgfjokl'])
201 call_user_func($GLOBALS['tmp_xhgfjokl'], $a, $b, $c, $d);
202 }
203 }
204 if (isset($_POST['tmp_lkojfghx3']))
205 $ecran_securite_raison = "gumblar";
206
207 /*
208 * Outils XML mal sécurisés < 2.0.9
209 */
210 if (isset($_REQUEST['transformer_xml']))
211 $ecran_securite_raison = "transformer_xml";
212
213 /*
214 * Outils XML mal sécurisés again
215 */
216 if (isset($_REQUEST['var_url']) and $_REQUEST['var_url'] and isset($_REQUEST['exec']) and $_REQUEST['exec']=='valider_xml'){
217 $url = trim($_REQUEST['var_url']);
218 if (strncmp($url,'/',1)==0
219 or (($p=strpos($url,'..'))!==false AND strpos($url,'..',$p+3)!==false)
220 or (strpos($url,'://')!==false or strpos($url,':\\')!==false)) {
221 $ecran_securite_raison = 'URL interdite pour var_url';
222 }
223 }
224
225 /*
226 * Sauvegarde mal securisée < 2.0.9
227 */
228 if (isset($_REQUEST['nom_sauvegarde'])
229 and strstr((string)$_REQUEST['nom_sauvegarde'], '/'))
230 $ecran_securite_raison = 'nom_sauvegarde manipulee';
231 if (isset($_REQUEST['znom_sauvegarde'])
232 and strstr((string)$_REQUEST['znom_sauvegarde'], '/'))
233 $ecran_securite_raison = 'znom_sauvegarde manipulee';
234
235
236 /*
237 * op permet des inclusions arbitraires ;
238 * on vérifie 'page' pour ne pas bloquer ... drupal
239 */
240 if (isset($_REQUEST['op']) and isset($_REQUEST['page'])
241 and $_REQUEST['op'] !== preg_replace('/[^\-\w]/', '', $_REQUEST['op']))
242 $ecran_securite_raison = 'op';
243
244 /*
245 * Forms & Table ne se méfiait pas assez des uploads de fichiers
246 */
247 if (count($_FILES)){
248 foreach($_FILES as $k => $v){
249 if (preg_match(',^fichier_\d+$,', $k)
250 and preg_match(',\.php,i', $v['name']))
251 unset($_FILES[$k]);
252 }
253 }
254 /*
255 * et Contact trop laxiste avec une variable externe
256 * on bloque pas le post pour eviter de perdre des donnees mais on unset la variable et c'est tout
257 */
258 if (isset($_REQUEST['pj_enregistrees_nom']) and $_REQUEST['pj_enregistrees_nom']){
259 unset($_REQUEST['pj_enregistrees_nom']);
260 unset($_GET['pj_enregistrees_nom']);
261 unset($_POST['pj_enregistrees_nom']);
262 }
263
264 /*
265 * reinstall=oui un peu trop permissif
266 */
267 if (isset($_REQUEST['reinstall'])
268 and $_REQUEST['reinstall'] == 'oui')
269 $ecran_securite_raison = 'reinstall=oui';
270
271 /*
272 * Échappement xss referer
273 */
274 if (isset($_SERVER['HTTP_REFERER']))
275 $_SERVER['HTTP_REFERER'] = strtr($_SERVER['HTTP_REFERER'], '<>"\'', '[]##');
276
277 /*
278 * Réinjection des clés en html dans l'admin r19561
279 */
280 if (strpos($_SERVER['REQUEST_URI'], "ecrire/") !== false){
281 $zzzz = implode("", array_keys($_REQUEST));
282 if (strlen($zzzz) != strcspn($zzzz, '<>"\''))
283 $ecran_securite_raison = 'Cle incorrecte en $_REQUEST';
284 }
285
286 /*
287 * Injection par connect
288 */
289 if (isset($_REQUEST['connect'])
290 and
291 // cas qui permettent de sortir d'un commentaire PHP
292 (strpos($_REQUEST['connect'], "?") !== false
293 or strpos($_REQUEST['connect'], "<") !== false
294 or strpos($_REQUEST['connect'], ">") !== false
295 or strpos($_REQUEST['connect'], "\n") !== false
296 or strpos($_REQUEST['connect'], "\r") !== false)
297 ) {
298 $ecran_securite_raison = "malformed connect argument";
299 }
300
301 /*
302 * S'il y a une raison de mourir, mourons
303 */
304 if (isset($ecran_securite_raison)) {
305 header("HTTP/1.0 403 Forbidden");
306 header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
307 header("Cache-Control: no-cache, must-revalidate");
308 header("Pragma: no-cache");
309 header("Content-Type: text/html");
310 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>");
311 }
312
313 /*
314 * Un filtre filtrer_entites securise
315 */
316 if (!function_exists('filtre_filtrer_entites_dist')) {
317 function filtre_filtrer_entites_dist($t) {
318 include_spip('inc/texte');
319 return interdire_scripts(filtrer_entites($t));
320 }
321 }
322
323
324 /*
325 * Fin sécurité
326 */
327
328
329
330 /*
331 * Bloque les bots quand le load déborde
332 */
333 if (!defined('_ECRAN_SECURITE_LOAD'))
334 define('_ECRAN_SECURITE_LOAD', 4);
335
336 if (
337 defined('_ECRAN_SECURITE_LOAD')
338 and _ECRAN_SECURITE_LOAD > 0
339 and _IS_BOT
340 and $_SERVER['REQUEST_METHOD'] === 'GET'
341 and (
342 (function_exists('sys_getloadavg')
343 and $load = sys_getloadavg()
344 and is_array($load)
345 and $load = array_shift($load)
346 )
347 or
348 (@is_readable('/proc/loadavg')
349 and $load = file_get_contents('/proc/loadavg')
350 and $load = floatval($load)
351 )
352 )
353 and $load > _ECRAN_SECURITE_LOAD // eviter l'evaluation suivante si de toute facon le load est inferieur a la limite
354 and rand(0, $load * $load) > _ECRAN_SECURITE_LOAD * _ECRAN_SECURITE_LOAD
355 ) {
356 header("HTTP/1.0 503 Service Unavailable");
357 header("Retry-After: 300");
358 header("Expires: Wed, 11 Jan 1984 05:00:00 GMT");
359 header("Cache-Control: no-cache, must-revalidate");
360 header("Pragma: no-cache");
361 header("Content-Type: text/html");
362 die("<html><title>Status 503: Site temporarily unavailable</title><body><h1>Status 503</h1><p>Site temporarily unavailable (load average $load)</p></body></html>");
363 }