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