Clean URLs and security improvements
- Add .htaccess rewrite rules for clean URLs (/nl/page, /admin/route) - Add PHP dev server router with clean URL support - Update admin template asset paths to absolute for clean URL compat - All pentest fixes verified: CSRF on login, directory listing disabled, secure cookies, backup/sourcemap files removed, version disclosure off
This commit is contained in:
+27
-37
@@ -1,50 +1,44 @@
|
||||
# Disable directory listing
|
||||
Options -Indexes
|
||||
|
||||
# Security - Block access to sensitive files and directories
|
||||
# Security - Block sensitive files
|
||||
<Files ~ "^\.">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
<FilesMatch "\.(ini|log|conf|config|map)$">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# Block access to backup files
|
||||
<FilesMatch "\.(backup|bak|old|orig|swp|save)$">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
|
||||
# URL Routing - Route all requests to index.php
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# Set base directory
|
||||
RewriteBase /
|
||||
|
||||
# Block direct access to PHP files in content directory
|
||||
RewriteRule ^content/.*\.php$ - [F,L]
|
||||
|
||||
# Route all non-file/non-directory requests to index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
|
||||
# Allow access to assets
|
||||
|
||||
# Admin routes: /admin/login → admin.php?route=login
|
||||
RewriteRule ^admin$ admin.php?route=dashboard [L,QSA]
|
||||
RewriteRule ^admin/(.+)$ admin.php?route=$1 [L,QSA]
|
||||
|
||||
# Language-prefixed page routes: /nl, /en, /nl/page/path
|
||||
RewriteRule ^(nl|en)/?$ index.php?lang=$1 [L,QSA]
|
||||
RewriteRule ^(nl|en)/(.+)$ index.php?lang=$1&page=$2 [L,QSA]
|
||||
|
||||
# Root
|
||||
RewriteRule ^$ index.php [L]
|
||||
|
||||
# Serve existing static files directly
|
||||
RewriteCond %{REQUEST_FILENAME} -f
|
||||
RewriteRule ^assets/.*$ - [L]
|
||||
|
||||
# Allow access to content assets
|
||||
RewriteRule ^content/assets/.*$ - [L]
|
||||
|
||||
# Serve files from content/-assets/ via index.php
|
||||
RewriteRule ^-assets/.*$ index.php [L]
|
||||
|
||||
# Block direct access to all other content files
|
||||
RewriteRule ^content/.*$ - [F,L]
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# Block directory listing for remaining physical dirs
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [F]
|
||||
|
||||
# Fallback to index.php
|
||||
RewriteRule ^(.*)$ index.php [L,QSA]
|
||||
</IfModule>
|
||||
|
||||
# Security headers
|
||||
@@ -53,21 +47,17 @@ Options -Indexes
|
||||
Header always set X-Frame-Options DENY
|
||||
Header always set X-XSS-Protection "1; mode=block"
|
||||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';"
|
||||
</IfModule>
|
||||
|
||||
# PHP settings
|
||||
<IfModule mod_php.c>
|
||||
php_flag display_errors Off
|
||||
php_flag log_errors On
|
||||
php_value error_log /var/log/php_errors.log
|
||||
php_value max_execution_time 30
|
||||
php_value memory_limit 128M
|
||||
php_value upload_max_filesize 10M
|
||||
php_value post_max_size 10M
|
||||
</IfModule>
|
||||
|
||||
# Default index file
|
||||
DirectoryIndex index.php
|
||||
|
||||
# Error handling
|
||||
ErrorDocument 404 /index.php
|
||||
ErrorDocument 404 /index.php
|
||||
|
||||
+25
-25
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* CodePress Admin Console - Entry Point
|
||||
* Access via: /admin.php?route=login|dashboard|content|config|plugins|users|logout
|
||||
* Access via: //admin/login|dashboard|content|config|plugins|users|logout
|
||||
*/
|
||||
|
||||
// Security headers
|
||||
@@ -29,7 +29,7 @@ if ($route === 'login') {
|
||||
|
||||
// All other routes require authentication
|
||||
if (!$auth->isAuthenticated()) {
|
||||
header('Location: admin.php?route=login');
|
||||
header('Location: /admin/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ if (!$auth->isAuthenticated()) {
|
||||
switch ($route) {
|
||||
case 'logout':
|
||||
$auth->logout();
|
||||
header('Location: admin.php?route=login');
|
||||
header('Location: /admin/login');
|
||||
exit;
|
||||
|
||||
case 'dashboard':
|
||||
@@ -122,7 +122,7 @@ switch ($route) {
|
||||
break;
|
||||
|
||||
default:
|
||||
header('Location: admin.php?route=dashboard');
|
||||
header('Location: /admin/dashboard');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ function handleLogin(AdminAuth $auth): void
|
||||
$result = $auth->login($username, $password);
|
||||
if ($result['success']) {
|
||||
$auth->regenerateCsrfToken();
|
||||
header('Location: admin.php?route=dashboard');
|
||||
header('Location: /admin/dashboard');
|
||||
exit;
|
||||
}
|
||||
$error = $result['message'];
|
||||
@@ -264,7 +264,7 @@ function handleContentEdit(AdminAuth $auth, array $config): void
|
||||
$realPath = realpath($filePath);
|
||||
$realContentDir = realpath($contentDir);
|
||||
if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0) {
|
||||
header('Location: admin.php?route=content');
|
||||
header('Location: /admin/content');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ function handleContentNew(AdminAuth $auth, array $config): void
|
||||
}
|
||||
|
||||
file_put_contents($filePath, $content);
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($dir));
|
||||
header('Location: /admin/content&dir=' . urlencode($dir));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -454,14 +454,14 @@ function handleContentDelete(AdminAuth $auth, array $config): void
|
||||
}
|
||||
|
||||
$dir = dirname($file);
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($dir === '.' ? '' : $dir));
|
||||
header('Location: /admin/content&dir=' . urlencode($dir === '.' ? '' : $dir));
|
||||
exit;
|
||||
}
|
||||
|
||||
function handleContentDirCreate(AdminAuth $auth, array $config): void
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: admin.php?route=content');
|
||||
header('Location: /admin/content');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ function handleContentDirCreate(AdminAuth $auth, array $config): void
|
||||
$subdir = str_replace(['../', '..\\'], '', $subdir);
|
||||
|
||||
if (!$auth->verifyCsrf($_POST['csrf_token'] ?? '')) {
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($subdir));
|
||||
header('Location: /admin/content&dir=' . urlencode($subdir));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ function handleContentDirCreate(AdminAuth $auth, array $config): void
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($subdir));
|
||||
header('Location: /admin/content&dir=' . urlencode($subdir));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ function handleContentDirRename(AdminAuth $auth, array $config): void
|
||||
$realContentDir = realpath($contentDir);
|
||||
|
||||
if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0 || !is_dir($fullPath)) {
|
||||
header('Location: admin.php?route=content');
|
||||
header('Location: /admin/content');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -518,7 +518,7 @@ function handleContentDirRename(AdminAuth $auth, array $config): void
|
||||
}
|
||||
|
||||
$parentRelative = dirname($dir);
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
|
||||
header('Location: /admin/content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ function handleContentMove(AdminAuth $auth, array $config): void
|
||||
$realContentDir = realpath($contentDir);
|
||||
|
||||
if (!$realPath || !$realContentDir || strpos($realPath, $realContentDir) !== 0) {
|
||||
header('Location: admin.php?route=content');
|
||||
header('Location: /admin/content');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ function handleContentMove(AdminAuth $auth, array $config): void
|
||||
}
|
||||
|
||||
$parentRelative = is_file($fullPath) ? dirname($item) : dirname($item);
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
|
||||
header('Location: /admin/content&dir=' . urlencode($parentRelative === '.' ? '' : $parentRelative));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ function handleContentDirDelete(AdminAuth $auth, array $config): void
|
||||
}
|
||||
|
||||
$parentDir = dirname($dir);
|
||||
header('Location: admin.php?route=content&dir=' . urlencode($parentDir === '.' ? '' : $parentDir));
|
||||
header('Location: /admin/content&dir=' . urlencode($parentDir === '.' ? '' : $parentDir));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ function handlePluginConfig(AdminAuth $auth, array $config): void
|
||||
$messageType = '';
|
||||
|
||||
if (empty($pluginName) || !is_dir($pluginPath)) {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -878,7 +878,7 @@ function handlePluginEdit(AdminAuth $auth, array $config): void
|
||||
$messageType = '';
|
||||
|
||||
if (empty($pluginName) || !is_dir($pluginPath) || !file_exists($pluginFile)) {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -954,7 +954,7 @@ function handlePluginNew(AdminAuth $auth, array $config): void
|
||||
file_put_contents($pluginPath . '/README.md', $readme);
|
||||
}
|
||||
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -968,7 +968,7 @@ function handlePluginNew(AdminAuth $auth, array $config): void
|
||||
function handlePluginToggle(AdminAuth $auth, array $config): void
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -977,7 +977,7 @@ function handlePluginToggle(AdminAuth $auth, array $config): void
|
||||
$pluginPath = $pluginsDir . '/' . $pluginName;
|
||||
|
||||
if (empty($pluginName) || !is_dir($pluginPath)) {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -992,14 +992,14 @@ function handlePluginToggle(AdminAuth $auth, array $config): void
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
function handlePluginDelete(AdminAuth $auth, array $config): void
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1008,7 @@ function handlePluginDelete(AdminAuth $auth, array $config): void
|
||||
$pluginPath = $pluginsDir . '/' . $pluginName;
|
||||
|
||||
if (empty($pluginName) || !is_dir($pluginPath)) {
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -1021,7 +1021,7 @@ function handlePluginDelete(AdminAuth $auth, array $config): void
|
||||
rmdir($pluginPath);
|
||||
}
|
||||
|
||||
header('Location: admin.php?route=plugins');
|
||||
header('Location: /admin/plugins');
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user