|
| 1 | +<?php |
| 2 | + |
| 3 | +/* |
| 4 | + * OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione |
| 5 | + * Copyright (C) DevCode s.r.l. |
| 6 | + * |
| 7 | + * This program is free software: you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU General Public License as published by |
| 9 | + * the Free Software Foundation, either version 3 of the License, or |
| 10 | + * (at your option) any later version. |
| 11 | + * |
| 12 | + * This program is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU General Public License |
| 18 | + * along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 19 | + */ |
| 20 | + |
| 21 | +namespace Modules\Aggiornamenti\Controlli; |
| 22 | + |
| 23 | +/** |
| 24 | + * Controllo per verificare la presenza dei file .htaccess critici nelle cartelle di sistema. |
| 25 | + */ |
| 26 | +class FileHtaccess extends Controllo |
| 27 | +{ |
| 28 | + /** |
| 29 | + * Definizione delle cartelle che richiedono un file .htaccess e il relativo contenuto. |
| 30 | + */ |
| 31 | + protected static $htaccess_config = [ |
| 32 | + '' => [ |
| 33 | + 'description' => 'File di configurazione principale Apache per la sicurezza del gestionale', |
| 34 | + 'content' => null, // Contenuto generato dinamicamente |
| 35 | + ], |
| 36 | + 'files' => [ |
| 37 | + 'description' => 'Cartella per i file allegati degli utenti', |
| 38 | + 'content' => "# Disable directory listing\nOptions -Indexes\n\n# Disable PHP rendering\n<IfModule mod_php5.c>\n php_flag engine off\n</IfModule>\n<IfModule mod_php7.c>\n php_flag engine off\n</IfModule>\n", |
| 39 | + ], |
| 40 | + 'logs' => [ |
| 41 | + 'description' => 'Cartella per i file di log', |
| 42 | + 'content' => "Deny from all\n", |
| 43 | + ], |
| 44 | + 'backup' => [ |
| 45 | + 'description' => 'Cartella per i backup', |
| 46 | + 'content' => "Deny from all\n", |
| 47 | + ], |
| 48 | + 'update' => [ |
| 49 | + 'description' => 'Cartella per gli aggiornamenti', |
| 50 | + 'content' => "Deny from all\n", |
| 51 | + ], |
| 52 | + 'locale' => [ |
| 53 | + 'description' => 'Cartella per le traduzioni', |
| 54 | + 'content' => "Deny from all\n", |
| 55 | + ], |
| 56 | + ]; |
| 57 | + |
| 58 | + /** |
| 59 | + * Contenuto del file .htaccess principale (root). |
| 60 | + */ |
| 61 | + protected static function getRootHtaccessContent() |
| 62 | + { |
| 63 | + return <<<'HTACCESS' |
| 64 | +# Remove autoindex |
| 65 | +<IfModule mod_autoindex.c> |
| 66 | + IndexIgnore */* |
| 67 | +</IfModule> |
| 68 | +
|
| 69 | +# Deny access to files starting with a dot (e.g. .htaccess, .git) |
| 70 | +<FilesMatch "^\."> |
| 71 | + Require all denied |
| 72 | +</FilesMatch> |
| 73 | +
|
| 74 | +# Deny access to certain file types like log, sql, htaccess, etc. |
| 75 | +<FilesMatch "\.(ini|psd|log|sh|sql|md|lock|phar)$"> |
| 76 | + Require all denied |
| 77 | +</FilesMatch> |
| 78 | +
|
| 79 | +# Deny access to VERSION, REVISION, LICENSE, and config files |
| 80 | +<Files ~ "(VERSION$|REVISION$|LICENSE|(config.inc|config.example).php|(composer|package).json|gulpfile.js)"> |
| 81 | + Require all denied |
| 82 | +</Files> |
| 83 | +
|
| 84 | +# Disable indexing of php, html, htm, pdf files |
| 85 | +ServerSignature Off |
| 86 | +<IfModule mod_headers.c> |
| 87 | + Header set X-Robots-Tag: "noindex,nofollow" |
| 88 | + Header set X-Content-Type-Options nosniff |
| 89 | +</IfModule> |
| 90 | +
|
| 91 | +<IfModule mod_rewrite.c> |
| 92 | + RewriteEngine On |
| 93 | +
|
| 94 | + # Tell PHP that the mod_rewrite module is ENABLED. |
| 95 | + <IfModule mod_env.c> |
| 96 | + SetEnv HTTP_MOD_REWRITE On |
| 97 | + </IfModule> |
| 98 | +
|
| 99 | + # Deny access to protected folders |
| 100 | + RewriteRule ^backup/ - [F,L] |
| 101 | + RewriteRule ^docs/ - [F,L] |
| 102 | + RewriteRule ^include/ - [F,L] |
| 103 | + RewriteRule ^locale/ - [F,L] |
| 104 | + RewriteRule ^logs/ - [F,L] |
| 105 | + RewriteRule ^update/ - [F,L] |
| 106 | +
|
| 107 | + # Deny access to svn, git, node_modules and vendor folders |
| 108 | + RewriteRule ^.git/ - [F,L] |
| 109 | + RewriteRule ^.svn/ - [F,L] |
| 110 | + RewriteRule ^node_modules/ - [F,L] |
| 111 | + RewriteRule ^vendor/ - [F,L] |
| 112 | +
|
| 113 | + # Disable HTTP TRACE |
| 114 | + RewriteCond %{REQUEST_METHOD} ^TRACE |
| 115 | + RewriteRule .* - [F] |
| 116 | +
|
| 117 | + # Prevent hacks |
| 118 | + # proc/self/environ? no way! |
| 119 | + RewriteCond %{QUERY_STRING} proc/self/environ [OR] |
| 120 | +
|
| 121 | + # Block out any script trying to set a mosConfig value through the URL |
| 122 | + RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR] |
| 123 | +
|
| 124 | + # Block out any script trying to base64_encode crap to send via URL |
| 125 | + RewriteCond %{QUERY_STRING} base64_encode.*(.*) [OR] |
| 126 | +
|
| 127 | + # Block out any script that includes a <script> tag in URL |
| 128 | + RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR] |
| 129 | +
|
| 130 | + # Block out any script trying to set a PHP GLOBALS variable via URL |
| 131 | + RewriteCond %{QUERY_STRING} GLOBALS(=|[|\%[0-9A-Z]{0,2}) [OR] |
| 132 | +
|
| 133 | + # Block out any script trying to modify a _REQUEST variable via URL |
| 134 | + RewriteCond %{QUERY_STRING} _REQUEST(=|[|\%[0-9A-Z]{0,2}) |
| 135 | +
|
| 136 | + # Set an environment variable for bad bots using user-agent patterns |
| 137 | + SetEnvIfNoCase User-Agent ".*(craftbot|download|extract|stripper|sucker|ninja|clshttp|webspider|leacher|collector|grabber|webpictures).*" HTTP_SAFE_BADBOT |
| 138 | + SetEnvIfNoCase User-Agent ".*(libwww-perl|aesop_com_spiderman).*" HTTP_SAFE_BADBOT |
| 139 | +
|
| 140 | + # Deny access to requests from this environment variable |
| 141 | + <RequireAll> |
| 142 | + Require all granted |
| 143 | + Require not env HTTP_SAFE_BADBOT |
| 144 | + </RequireAll> |
| 145 | +</ifModule> |
| 146 | +
|
| 147 | +# Compress text, html, javascript, css, ecc... |
| 148 | +<IfModule mod_gzip.c> |
| 149 | + mod_gzip_on Yes |
| 150 | + mod_gzip_dechunk Yes |
| 151 | + mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$ |
| 152 | + mod_gzip_item_include handler ^cgi-script$ |
| 153 | + mod_gzip_item_include mime ^text/.* |
| 154 | + mod_gzip_item_include mime ^application/x-javascript.* |
| 155 | + mod_gzip_item_exclude mime ^image/.* |
| 156 | + mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* |
| 157 | +</IfModule> |
| 158 | +
|
| 159 | +<IfModule mod_mime.c> |
| 160 | + AddType text/javascript mjs |
| 161 | +</IfModule> |
| 162 | +HTACCESS; |
| 163 | + } |
| 164 | + |
| 165 | + public function getName() |
| 166 | + { |
| 167 | + return tr('File .htaccess di sistema'); |
| 168 | + } |
| 169 | + |
| 170 | + public function getType($record) |
| 171 | + { |
| 172 | + return 'warning'; |
| 173 | + } |
| 174 | + |
| 175 | + public function getOptions($record) |
| 176 | + { |
| 177 | + return [ |
| 178 | + [ |
| 179 | + 'name' => tr('Rigenera file'), |
| 180 | + 'icon' => 'fa fa-refresh', |
| 181 | + 'color' => 'success', |
| 182 | + 'params' => ['action' => 'regenerate'], |
| 183 | + ], |
| 184 | + ]; |
| 185 | + } |
| 186 | + |
| 187 | + public function hasGlobalActions() |
| 188 | + { |
| 189 | + return true; |
| 190 | + } |
| 191 | + |
| 192 | + public function getGlobalActions() |
| 193 | + { |
| 194 | + $missing_count = count($this->results); |
| 195 | + if ($missing_count === 0) { |
| 196 | + return []; |
| 197 | + } |
| 198 | + |
| 199 | + return [ |
| 200 | + [ |
| 201 | + 'name' => tr('Rigenera tutti i file mancanti'), |
| 202 | + 'icon' => 'fa fa-refresh', |
| 203 | + 'color' => 'success', |
| 204 | + 'params' => ['action' => 'regenerate_all'], |
| 205 | + 'badge' => $missing_count.' '.tr('file'), |
| 206 | + ], |
| 207 | + ]; |
| 208 | + } |
| 209 | + |
| 210 | + public function check() |
| 211 | + { |
| 212 | + foreach (self::$htaccess_config as $folder => $config) { |
| 213 | + // Gestione speciale per la directory root |
| 214 | + if ($folder === '') { |
| 215 | + $folder_path = base_dir(); |
| 216 | + $htaccess_path = $folder_path.'/.htaccess'; |
| 217 | + $display_path = '.htaccess'; |
| 218 | + $display_folder = tr('directory principale'); |
| 219 | + } else { |
| 220 | + $folder_path = base_dir().'/'.$folder; |
| 221 | + $htaccess_path = $folder_path.'/.htaccess'; |
| 222 | + $display_path = $folder.'/.htaccess'; |
| 223 | + $display_folder = '<code>'.$folder.'</code>'; |
| 224 | + |
| 225 | + // Verifica se la cartella esiste (solo per sottocartelle) |
| 226 | + if (!is_dir($folder_path)) { |
| 227 | + continue; |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + // Verifica se il file .htaccess esiste |
| 232 | + if (!file_exists($htaccess_path)) { |
| 233 | + $this->addResult([ |
| 234 | + 'id' => 'htaccess_'.($folder === '' ? 'root' : $folder), |
| 235 | + 'folder' => $folder, |
| 236 | + 'nome' => '<strong>.htaccess</strong><br><small class="text-muted">'.$display_path.'</small>', |
| 237 | + 'descrizione' => tr('File .htaccess mancante nella _FOLDER_', ['_FOLDER_' => $display_folder]).'<br><small class="text-muted">'.$config['description'].'</small>', |
| 238 | + ]); |
| 239 | + } |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + public function execute($record, $params = []) |
| 244 | + { |
| 245 | + $action = $params['action'] ?? ''; |
| 246 | + |
| 247 | + if ($action === 'regenerate') { |
| 248 | + return $this->regenerateHtaccess($record['folder']); |
| 249 | + } |
| 250 | + |
| 251 | + return tr('Azione non supportata'); |
| 252 | + } |
| 253 | + |
| 254 | + /** |
| 255 | + * Rigenera tutti i file .htaccess mancanti. |
| 256 | + */ |
| 257 | + public function solveGlobal($params = []) |
| 258 | + { |
| 259 | + $action = $params['action'] ?? ''; |
| 260 | + $results = []; |
| 261 | + |
| 262 | + if ($action === 'regenerate_all') { |
| 263 | + foreach ($this->results as $record) { |
| 264 | + $results[$record['id']] = $this->regenerateHtaccess($record['folder']); |
| 265 | + } |
| 266 | + } |
| 267 | + |
| 268 | + return $results; |
| 269 | + } |
| 270 | + |
| 271 | + /** |
| 272 | + * Rigenera il file .htaccess per una cartella specifica. |
| 273 | + */ |
| 274 | + protected function regenerateHtaccess($folder) |
| 275 | + { |
| 276 | + if (!isset(self::$htaccess_config[$folder])) { |
| 277 | + return tr('Configurazione non trovata per la cartella _FOLDER_', ['_FOLDER_' => $folder]); |
| 278 | + } |
| 279 | + |
| 280 | + // Gestione speciale per la directory root |
| 281 | + if ($folder === '') { |
| 282 | + $folder_path = base_dir(); |
| 283 | + $display_folder = tr('directory principale'); |
| 284 | + } else { |
| 285 | + $folder_path = base_dir().'/'.$folder; |
| 286 | + $display_folder = $folder; |
| 287 | + |
| 288 | + // Crea la cartella se non esiste (solo per sottocartelle) |
| 289 | + if (!is_dir($folder_path)) { |
| 290 | + if (!mkdir($folder_path, 0755, true)) { |
| 291 | + return tr('Impossibile creare la cartella _FOLDER_', ['_FOLDER_' => $folder]); |
| 292 | + } |
| 293 | + } |
| 294 | + } |
| 295 | + |
| 296 | + $htaccess_path = $folder_path.'/.htaccess'; |
| 297 | + |
| 298 | + // Ottieni il contenuto appropriato |
| 299 | + $content = self::$htaccess_config[$folder]['content']; |
| 300 | + if ($content === null && $folder === '') { |
| 301 | + $content = self::getRootHtaccessContent(); |
| 302 | + } |
| 303 | + |
| 304 | + // Scrivi il contenuto del file .htaccess |
| 305 | + if (file_put_contents($htaccess_path, $content) === false) { |
| 306 | + return tr('Impossibile creare il file .htaccess nella _FOLDER_', ['_FOLDER_' => $display_folder]); |
| 307 | + } |
| 308 | + |
| 309 | + return tr('File .htaccess rigenerato con successo nella _FOLDER_', ['_FOLDER_' => $display_folder]); |
| 310 | + } |
| 311 | +} |
| 312 | + |
0 commit comments