Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions assets/.thumbs/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/backup/.htaccess
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
order deny,allow
deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
9 changes: 7 additions & 2 deletions assets/cache/.htaccess
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
order deny,allow
deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
9 changes: 7 additions & 2 deletions assets/cache/images/.htaccess
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
order deny,allow
allow from all
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Allow from all
</IfModule>
18 changes: 14 additions & 4 deletions assets/docs/.htaccess
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
IndexIgnore */*
<Files "changelog.txt">
Order Allow,Deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/export/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/files/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/images/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/import/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/js/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/js/fileapi/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/js/jeditable/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions assets/site/.htaccess
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
IndexIgnore */*
<Files *.php>
Order Deny,Allow
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions core/.htaccess
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
order deny,allow
deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
9 changes: 8 additions & 1 deletion core/config/session.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,15 @@
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you if it can not be done securely.
|
| Unset, it follows the current request: an HTTPS site gets the Secure
| flag without configuration. Set it explicitly behind a TLS-terminating
| proxy that talks plain HTTP to PHP.
|
*/
'secure' => env('SESSION_SECURE_COOKIE', false),
'secure' => env('SESSION_SECURE_COOKIE') ?? (
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (int)($_SERVER['SERVER_PORT'] ?? 0) === 443
),

/*
|--------------------------------------------------------------------------
Expand Down
80 changes: 80 additions & 0 deletions core/tests/Unit/Security/ApacheConfigHardeningTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/*
|--------------------------------------------------------------------------
| Apache templates and the session cookie
|--------------------------------------------------------------------------
|
| ng.inx refuses /core, /views, dumps, composer.json and PHP under assets; the Apache template
| refused none of them and left that to per-directory .htaccess files written in the Apache 2.2
| dialect, which a 2.4 server without mod_access_compat rejects. The session cookie's Secure flag
| came from SESSION_SECURE_COOKIE alone, so the session proxy (the default) never set it, even
| on an HTTPS site.
|
*/

function evoFile(string $relative): string
{
return (string)file_get_contents(dirname(__DIR__, 4) . '/' . $relative);
}

it('refuses the same paths in the apache template as in the nginx one', function () {
$htaccess = evoFile('ht.access');

expect($htaccess)
->toContain('RewriteRule ^(core|views|vendor|tmp)(/|$) - [F,L]')
->toContain('RewriteRule ^assets/(cache|backup|export|import)(/|$) - [F,L]')
->toContain('RewriteRule ^assets/.*\.php$ - [F,L,NC]')
->toContain('RewriteRule \.(sql|sqlite|db|log|bak|old|orig|save|swp|swo|tpl|inc|ini|env|dist|example|yml|yaml|lock|tar)(\.(gz|bz2|xz|zip|tgz))?$ - [F,L,NC]')
->toContain('RewriteRule ^(composer\.(json|lock)|phpstan\.neon|publiccode\.yml|AGENTS\.md|README\.md|ht\.access|ng\.inx|config\.php(\.example)?)$ - [F,L]');

// the deny rules must run before the assets/manager passthrough that ends rewriting
expect(strpos($htaccess, 'RewriteRule ^(core|views|vendor|tmp)'))
->toBeLessThan(strpos($htaccess, 'RewriteRule ^(manager|assets|js|css|images|img)/.*$ - [L]'));
});

it('writes every shipped per-directory .htaccess in both the 2.2 and the 2.4 dialect', function (string $file) {
$source = evoFile($file);

if (preg_match('/^\s*(Order|Deny|Allow|Require)\b/mi', $source) !== 1) {
expect(true)->toBeTrue(); // rewrite-only file, nothing to check
return;
}

expect($source)
->toContain('<IfModule mod_authz_core.c>')
->toContain('<IfModule !mod_authz_core.c>')
->and(preg_match_all('/Require all (denied|granted)/', $source))
->toBe(preg_match_all('/(Deny|Allow) from all/i', $source));

// a bare 2.2 directive outside its guard is a 500 on a 2.4 server without mod_access_compat
expect(preg_match('/^\s*(Order|Deny from|Allow from)\b/mi', preg_replace('/<IfModule !mod_authz_core\.c>.*?<\/IfModule>/s', '', $source)))
->toBe(0);
})->with(function () {
$root = dirname(__DIR__, 4);
$out = [];
foreach (explode("\n", trim((string)shell_exec('git -C ' . escapeshellarg($root) . ' ls-files'))) as $path) {
if (preg_match('~(^|/)\.htaccess$~', $path)) {
$out[] = $path;
}
}

return $out;
});

it('does not ship an installer .htaccess that strips the CSP the installer sets', function () {
expect(file_exists(dirname(__DIR__, 4) . '/install/.htaccess'))->toBeFalse();
});

it('no longer switches mod_security off for the manager', function () {
expect(evoFile('manager/.htaccess'))->not->toContain('SecFilterEngine');
});

it('marks the session cookie Secure on https unless told otherwise', function () {
$source = evoFile('core/config/session.php');

expect($source)
->toContain("'secure' => env('SESSION_SECURE_COOKIE') ?? (")
->toContain("\$_SERVER['HTTPS'] !== 'off'")
->and($source)->not->toContain("env('SESSION_SECURE_COOKIE', false)");
});
16 changes: 16 additions & 0 deletions ht.access
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ RewriteRule .* - [F,L]
#RewriteCond %{THE_REQUEST} \s/+index\.php\?q=([^\s&]+) [NC]
#RewriteRule ^ /%1? [R=301,L]

# Paths that must never be reachable over HTTP (mirrors ng.inx).
# core, views, vendor, tmp - CMS core (config, .env, storage), Blade layouts, deps, scratch
# assets/(cache|backup|export|import) - generated and writable areas; the per-directory
# .htaccess files there only work with AllowOverride
RewriteRule ^(core|views|vendor|tmp)(/|$) - [F,L]
RewriteRule ^assets/(cache|backup|export|import)(/|$) - [F,L]

# Root-level files that fingerprint the install or describe its layout.
RewriteRule ^(composer\.(json|lock)|phpstan\.neon|publiccode\.yml|AGENTS\.md|README\.md|ht\.access|ng\.inx|config\.php(\.example)?)$ - [F,L]

# Backups, dumps, editor leftovers and PHP include fragments anywhere, including compressed copies.
RewriteRule \.(sql|sqlite|db|log|bak|old|orig|save|swp|swo|tpl|inc|ini|env|dist|example|yml|yaml|lock|tar)(\.(gz|bz2|xz|zip|tgz))?$ - [F,L,NC]

# No PHP execution under assets - uploads live here.
RewriteRule ^assets/.*\.php$ - [F,L,NC]

# Exclude /assets and /manager directories and images from rewrite rules
RewriteRule ^(manager|assets|js|css|images|img)/.*$ - [L]
RewriteRule \.(jpg|jpeg|png|gif|ico)$ - [L]
Expand Down
3 changes: 0 additions & 3 deletions install/.htaccess

This file was deleted.

8 changes: 7 additions & 1 deletion install/assets/.htaccess
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
8 changes: 7 additions & 1 deletion install/src/.htaccess
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
5 changes: 0 additions & 5 deletions manager/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L,QSA]

<IfModule mod_security.c>
# Turn the filtering engine Off
SecFilterEngine Off
</IfModule>

<IfModule mod_expires.c>
ExpiresActive off
</IfModule>
Expand Down
9 changes: 7 additions & 2 deletions manager/media/browser/mcpuk/core/.htaccess
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<Files *>
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions manager/media/browser/mcpuk/doc/.htaccess
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<Files *>
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions manager/media/browser/mcpuk/lang/.htaccess
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<Files *>
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions manager/media/browser/mcpuk/lib/.htaccess
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<Files *>
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
9 changes: 7 additions & 2 deletions manager/media/browser/mcpuk/tpl/.htaccess
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<Files *>
Order allow,deny
Deny from all
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</Files>
Loading