Add security hardening for shared hosting environments
Improve session cookie security with HttpOnly and SameSite attributes Add security headers via .htaccess Block direct access to sensitive files Restrict allowed HTTP methods Document cPanel-specific security configuration Add container hardening for ServerTokens and ServerSignature
This commit is contained in:
parent
f2dc4ec647
commit
3b04a3d78c
6 changed files with 121 additions and 2 deletions
|
|
@ -1,11 +1,15 @@
|
|||
FROM php:8.4.14-apache
|
||||
|
||||
# Enable Apache modules and custom config as root during build
|
||||
RUN a2enmod rewrite
|
||||
RUN a2enmod rewrite headers
|
||||
|
||||
COPY apache.conf /etc/apache2/conf-available/custom.conf
|
||||
RUN a2enconf custom
|
||||
|
||||
# Override default security.conf settings
|
||||
RUN sed -i 's/^ServerTokens OS/ServerTokens Prod/' /etc/apache2/conf-available/security.conf \
|
||||
&& sed -i 's/^ServerSignature On/ServerSignature Off/' /etc/apache2/conf-available/security.conf
|
||||
|
||||
# Log to /proc/self/fd for container output
|
||||
RUN sed -i 's|ErrorLog.*|ErrorLog /proc/self/fd/2|' /etc/apache2/sites-available/000-default.conf \
|
||||
&& sed -i 's|CustomLog.*|CustomLog /proc/self/fd/1 combined|' /etc/apache2/sites-available/000-default.conf \
|
||||
|
|
|
|||
10
apache.conf
10
apache.conf
|
|
@ -1,5 +1,13 @@
|
|||
# Minimize server version disclosure
|
||||
ServerTokens Prod
|
||||
|
||||
# Disable PHP version header and error display
|
||||
php_flag expose_php Off
|
||||
php_flag display_errors Off
|
||||
php_flag log_errors On
|
||||
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks
|
||||
Options FollowSymLinks
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,31 @@
|
|||
DirectorySlash Off
|
||||
|
||||
# Block direct access to content source files
|
||||
<FilesMatch "\.(ini|md|html|php)$">
|
||||
# Allow only the entry point
|
||||
<If "%{REQUEST_URI} != '/index.php'">
|
||||
Require all denied
|
||||
</If>
|
||||
</FilesMatch>
|
||||
|
||||
# Security headers
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "DENY"
|
||||
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
||||
Header unset X-Powered-By
|
||||
Header always unset X-Powered-By
|
||||
</IfModule>
|
||||
|
||||
# Restrict HTTP methods to GET, POST, HEAD
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$ [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
# Route /app requests to index.php
|
||||
RewriteCond %{REQUEST_URI} ^/app/
|
||||
RewriteRule ^(.*)$ /index.php [L,QSA]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,31 @@
|
|||
DirectorySlash Off
|
||||
|
||||
# Block direct access to content source files
|
||||
<FilesMatch "\.(ini|md|html|php)$">
|
||||
# Allow only the entry point
|
||||
<If "%{REQUEST_URI} != '/index.php'">
|
||||
Require all denied
|
||||
</If>
|
||||
</FilesMatch>
|
||||
|
||||
# Security headers
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "DENY"
|
||||
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header set Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
||||
Header unset X-Powered-By
|
||||
Header always unset X-Powered-By
|
||||
</IfModule>
|
||||
|
||||
# Restrict HTTP methods to GET, POST, HEAD
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$ [NC]
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
# Route /app requests to index.php
|
||||
RewriteCond %{REQUEST_URI} ^/app/
|
||||
RewriteRule ^(.*)$ /index.php [L,QSA]
|
||||
|
|
|
|||
|
|
@ -5,5 +5,12 @@ if (str_starts_with($_SERVER['REQUEST_URI'], '/app/')) {
|
|||
exit;
|
||||
}
|
||||
|
||||
// Harden session cookie before any session starts
|
||||
ini_set('session.cookie_httponly', '1');
|
||||
ini_set('session.cookie_samesite', 'Lax');
|
||||
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
|
||||
ini_set('session.cookie_secure', '1');
|
||||
}
|
||||
|
||||
// All other requests go to router
|
||||
require __DIR__ . '/../app/router.php';
|
||||
|
|
|
|||
56
docs/security-cpanel.md
Normal file
56
docs/security-cpanel.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Security Hardening — cPanel Shared Hosting
|
||||
|
||||
The container dev environment (Containerfile + apache.conf) handles most hardening automatically. On cPanel shared hosting, some settings must be configured manually since you don't control the Apache or PHP config directly.
|
||||
|
||||
## What's handled by .htaccess (works everywhere)
|
||||
|
||||
These are applied automatically via `content/.htaccess` (synced from `.htaccess.base`):
|
||||
|
||||
- Block direct access to `.ini`, `.md`, `.html`, `.php` content files
|
||||
- Security headers: `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`
|
||||
- Strip `X-Powered-By` header
|
||||
- Restrict HTTP methods to GET/POST/HEAD
|
||||
- Rewrite rules routing all requests through `index.php`
|
||||
|
||||
The `custom/.htaccess` and `custom/data/.htaccess` files also deploy automatically and block direct access to config files and data.
|
||||
|
||||
## What needs manual cPanel configuration
|
||||
|
||||
### 1. Disable display_errors
|
||||
|
||||
Go to **MultiPHP INI Editor** (Home > Software > MultiPHP INI Editor):
|
||||
|
||||
- Select the domain
|
||||
- Set `display_errors` = **Off**
|
||||
- Set `log_errors` = **On**
|
||||
- Set `expose_php` = **Off**
|
||||
|
||||
This prevents PHP errors from leaking server paths and internal details to visitors.
|
||||
|
||||
### 2. PHP version
|
||||
|
||||
Use **MultiPHP Manager** to ensure PHP 8.4+ is selected for the domain.
|
||||
|
||||
### 3. Session cookie hardening
|
||||
|
||||
Handled in `content/index.php` via `ini_set()` calls — no cPanel action needed. The entry point sets `HttpOnly`, `SameSite=Lax`, and `Secure` (when on HTTPS) before any session starts.
|
||||
|
||||
### 4. Server version header
|
||||
|
||||
On shared hosting you typically cannot change `ServerTokens` (it's a server-level directive). The `X-Powered-By` header is stripped by `.htaccess`, but the `Server: Apache/2.4.x` header may still show the full version. This is a low-risk issue on shared hosting since the Apache version is the hosting provider's responsibility.
|
||||
|
||||
### 5. SSL/TLS
|
||||
|
||||
Use **SSL/TLS** (Home > Security > SSL/TLS) or **AutoSSL** to ensure HTTPS is active. The session cookie `Secure` flag only activates over HTTPS.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `.htaccess` deployed (copy `.htaccess.base` if needed, preserve cPanel-generated blocks)
|
||||
- [ ] `display_errors` = Off in MultiPHP INI Editor
|
||||
- [ ] `expose_php` = Off in MultiPHP INI Editor
|
||||
- [ ] `log_errors` = On in MultiPHP INI Editor
|
||||
- [ ] SSL certificate active
|
||||
- [ ] `custom/smtp-config.php` exists but is NOT in git (check `.gitignore`)
|
||||
- [ ] `custom/listmonk-config.php` exists but is NOT in git (check `.gitignore`)
|
||||
- [ ] `custom/data/` directory writable by web server (`chmod 755` or `775`)
|
||||
- [ ] `custom/data/.htaccess` present with `Require all denied`
|
||||
Loading…
Add table
Add a link
Reference in a new issue