\n" .
"\t" . $allow . "\n" .
"\n";
$allowStaticAssets =
"# Serve HTML files as text/html mime type - Note: requires mod_mime apache module!\n" .
"\n" .
" AddHandler text/html .html\n" .
" AddHandler text/html .htm\n" .
"\n\n" .
"# Allow to serve static files which are safe\n" .
"\n" .
$allow . "\n" .
"\n";
$noCachePreview = "
# do not cache preview container files
Header set Cache-Control \"Cache-Control: private, no-cache, no-store\"
";
$directoriesToProtect = array(
'/js' => $allowAny . $noCachePreview,
'/libs' => $denyAll . $allowStaticAssets,
'/vendor' => $denyAll . $allowStaticAssets,
'/plugins' => $denyAll . $allowStaticAssets,
'/misc/user' => $denyAll . $allowStaticAssets,
'/node_modules' => $denyAll . $allowStaticAssets,
);
foreach ($directoriesToProtect as $directoryToProtect => $content) {
self::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content);
}
// deny access to these folders
$directoriesToProtect = array(
PIWIK_USER_PATH . '/config' => $denyAll,
PIWIK_INCLUDE_PATH. '/core' => $denyAll,
PIWIK_INCLUDE_PATH . '/lang' => $denyAll,
StaticContainer::get('path.tmp') => $denyAll,
);
if (!empty($GLOBALS['CONFIG_INI_PATH_RESOLVER']) && is_callable($GLOBALS['CONFIG_INI_PATH_RESOLVER'])) {
$file = call_user_func($GLOBALS['CONFIG_INI_PATH_RESOLVER']);
$directoriesToProtect[dirname($file)] = $denyAll;
}
$gitDir = PIWIK_INCLUDE_PATH . '/.git';
if (is_dir($gitDir) && is_writable($gitDir)) {
$directoriesToProtect[$gitDir] = $denyAll;
}
foreach ($directoriesToProtect as $directoryToProtect => $content) {
self::createHtAccess($directoryToProtect, $overwrite = true, $content);
}
}
/**
* Create .htaccess file in specified directory
*
* Apache-specific; for IIS @see web.config
*
* .htaccess files are created on all webservers even Nginx, as sometimes Nginx knows how to handle .htaccess files
*
* @param string $path without trailing slash
* @param bool $overwrite whether to overwrite an existing file or not
* @param string $content
*/
protected static function createHtAccess($path, $overwrite, $content)
{
$file = $path . '/.htaccess';
$content = "# This file is auto generated by Matomo, do not edit directly\n# Please report any issue or improvement directly to the Matomo team.\n\n" . $content;
if ($overwrite || !file_exists($file)) {
@file_put_contents($file, $content, LOCK_EX);
}
}
/**
* Generate IIS web.config files to restrict access
*
* Note: for IIS 7 and above
*/
protected static function createWebConfigFiles()
{
if (!SettingsServer::isIIS()) {
return;
}
@file_put_contents(PIWIK_INCLUDE_PATH . '/web.config',
'
');
// deny direct access to .php files
$directoriesToProtect = array(
'/libs',
'/vendor',
'/plugins',
'/node_modules',
);
$additionForPlugins = '
';
foreach ($directoriesToProtect as $directoryToProtect) {
@file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
'
' . ($directoryToProtect === '/plugins' ? $additionForPlugins : '') . '
');
}
}
public static function deleteWebConfigFiles()
{
$path = PIWIK_INCLUDE_PATH;
@unlink($path . '/web.config');
@unlink($path . '/libs/web.config');
@unlink($path . '/vendor/web.config');
@unlink($path . '/plugins/web.config');
@unlink($path . '/node_modules/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) {
$path = PIWIK_DOCUMENT_ROOT . $file;
if(!file_exists($path)) {
@file_put_contents($path, '');
}
}
}
/**
* @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
*/
public 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
*/
public 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 and web.config files that Matomo 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',
'/node_modules',
'/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);
}
}
}
}
}