\n" . $allow . "\n" . "\n"; $allowStaticAssets = "# Allow to serve static files which are safe\n" . "\n" . $allow . "\n" . "\n"; $directoriesToProtect = array( '/js' => $allowAny, '/libs' => $denyAll . $allowStaticAssets, '/vendor' => $denyAll . $allowStaticAssets, '/plugins' => $denyAll . $allowStaticAssets, '/misc/user' => $denyAll . $allowStaticAssets, ); foreach ($directoriesToProtect as $directoryToProtect => $content) { self::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content); } // deny access to these folders $directoriesToProtect = array( '/config' => $denyAll, '/core' => $denyAll, '/lang' => $denyAll, '/tmp' => $denyAll, ); foreach ($directoriesToProtect as $directoryToProtect => $content) { self::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content); } } static public function createHtAccessDenyAll($path) { self::createHtAccess($path, $overwrite = false, self::getDenyAllHtaccessContent()); } /** * Create .htaccess file in specified directory * * Apache-specific; for IIS @see web.config * * @param string $path without trailing slash * @param bool $overwrite whether to overwrite an existing file or not * @param string $content */ public static function createHtAccess($path, $overwrite = true, $content) { if (SettingsServer::isApache()) { $file = $path . '/.htaccess'; if ($overwrite || !file_exists($file)) { @file_put_contents($file, $content); } } } /** * Generate IIS web.config files to restrict access * * Note: for IIS 7 and above */ public static function createWebConfigFiles() { @file_put_contents(PIWIK_INCLUDE_PATH . '/web.config', ' '); // deny direct access to .php files $directoriesToProtect = array( '/libs', '/vendor', '/plugins', ); foreach ($directoriesToProtect as $directoryToProtect) { @file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config', ' '); } } /** * Generate default robots.txt, favicon.ico, etc to suppress * 404 (Not Found) errors in the web server logs, if Piwik * is installed in the web root (or top level of subdomain). * * @see misc/crossdomain.xml */ public static function createWebRootFiles() { $filesToCreate = array( '/robots.txt', '/favicon.ico', ); foreach ($filesToCreate as $file) { @file_put_contents(PIWIK_DOCUMENT_ROOT . $file, ''); } } /** * @return string */ protected static function getDenyAllHtaccessContent() { $deny = self::getDenyHtaccessContent(); $denyAll = "# First, deny access to all files in this directory\n" . "\n" . $deny . "\n" . "\n"; return $denyAll; } /** * @return string */ protected static function getDenyHtaccessContent() { # Source: https://github.com/phpbb/phpbb/pull/2386/files#diff-f72a38c4bec79cc6ded3f8e435d6bd55L11 # With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from # module mod_authz_host to a new module called mod_access_compat (which may be # disabled) and a new "Require" syntax has been introduced to mod_authz_host. # We could just conditionally provide both versions, but unfortunately Apache # does not explicitly tell us its version if the module mod_version is not # available. In this case, we check for the availability of module # mod_authz_core (which should be on 2.4 or higher only) as a best guess. $deny = << Order Deny,Allow Deny from All = 2.4> Require all denied Order Deny,Allow Deny from All Require all denied HTACCESS_DENY; return $deny; } /** * @return string */ protected static function getAllowHtaccessContent() { $allow = << Order Allow,Deny Allow from All = 2.4> Require all granted Order Allow,Deny Allow from All Require all granted HTACCESS_ALLOW; return $allow; } /** * Deletes all existing .htaccess files that Piwik may have created * */ public static function deleteHtAccessFiles() { $files = Filesystem::globr(PIWIK_INCLUDE_PATH, ".htaccess"); // that match the list of directories we create htaccess files // (ie. not the root /.htaccess) $directoriesWithAutoHtaccess = array( '/js', '/libs', '/vendor', '/plugins', '/misc/user', '/config', '/core', '/lang', '/tmp', ); foreach ($files as $file) { foreach ($directoriesWithAutoHtaccess as $dirToDelete) { // only delete the first .htaccess and not the ones in sub-directories $pathToDelete = $dirToDelete . '/.htaccess'; if (strpos($file, $pathToDelete) !== false) { @unlink($file); } } } } }