wordpress
WordPress is among the most popular content management systems.
Apache
wordpress_headers
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Content-Type-Options "nosniff"
Header always append X-Frame-Options SAMEORIGIN
# Header always unset X-Content-Powered-By
Header always unset Link
Header always set Permissions-Policy "accelerometer=(), camera=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), usb=(), gamepad=(), serial=()"
Header always set X-XSS-Protection "1; mode=block"
Header always append Content-Security-Policy "frame-ancestors 'self'"
wordpress
# special MIME type for icons
AddType image/vnd.microsoft.icon .ico
AddType application/font-woff2 .woff2
# now we have icon MIME type, we can use it
# GZIP compression for text files: HTML, CSS, JS, Text, XML, fonts
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
</IfModule>
ExpiresActive On
ExpiresByType application/javascript "access plus 49 hours"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/x-javascript "access plus 49 hours"
ExpiresByType image/gif "access plus 5 weeks"
ExpiresByType image/jpeg "access plus 5 weeks"
ExpiresByType image/png "access plus 5 weeks"
ExpiresByType image/svg "access plus 5 weeks"
ExpiresByType text/css "access plus 1 weeks"
ExpiresByType text/javascript "access plus 1 weeks"
ExpiresByType text/plain "access plus 1 weeks"
# my favicon doesn't change much
ExpiresByType image/vnd.microsoft.icon "access plus 3 months"
ExpiresByType application/font-woff "access plus 7 days"
ExpiresByType application/font-woff2 "access plus 7 days"
<Directory {{ webmut_basedir }}/{{ item.name }}/public_html/>
{% if item.apache_whitelist is defined %}
<RequireAny>
{% for info in item.apache_whitelist_admin|default([]) + item.apache_whitelist %}
{% if info.name is defined %}
# {{ info.name }}
{% endif %}
Require ip {{ info.ip }}
{% endfor %}
</RequireAny>
{% endif %}
</Directory>
<Directory {{ webmut_basedir }}/{{ item.name }}/public_html/wp-admin>
{% if item.apache_whitelist_admin is defined %}
<RequireAny>
{% for info in item.apache_whitelist_admin %}
{% if info.name is defined %}
# {{ info.name }}
{% endif %}
Require ip {{ info.ip }}
{% endfor %}
</RequireAny>
{% endif %}
</Directory>
<Location /wp-admin/admin-ajax.php>
Order deny,allow
Allow from all
Satisfy any
</Location>
<Directory "{{ webmut_basedir }}/{{ item.name }}/public_html/wp-admin/css/>
Order deny,allow
Allow from all
Satisfy any
</Directory>
<Location "/xmlrpc.php">
<RequireAny>
{% for info in item.apache_whitelist_admin %}
{% if info.name is defined %}
# {{ info.name }}
{% endif %}
Require ip {{ info.ip }}
{% endfor %}
</RequireAny>
</Location>
{% if item.apache_whitelist_admin is defined %}
<Location /wp-login.php>
<RequireAny>
{% for info in item.apache_whitelist_admin %}
{% if info.name is defined %}
# {{ info.name }}
{% endif %}
Require ip {{ info.ip }}
{% endfor %}
</RequireAny>
</Location>
{% endif %}
RewriteEngine on
# CGR 10/1/2020
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{HTTP_USER_AGENT} "HeadlessChrome" [NC]
RewriteRule .* - [F]
#
RewriteRule ^/.*license.txt$ - [R=404,L]
RewriteRule ^/.*readme.(html|txt)$ - [R=404,L]
RewriteRule ^/.*FAQ$ - [R=404,L]
RewriteRule ^/.*README(.md)?$ - [R=404,L]
RewriteRule /.well-known - [L]
RewriteRule ^/phpMyAdmin - [L]
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} author=\d [NC,OR]
RewriteCond %{QUERY_STRING} author=\{num
RewriteRule ^ - [L,R=403]
RewriteRule ^(.*)wp-config(.*)\.php(.*)$ [R=404,L]
RewriteRule ^/wp-admin$ /wp-admin/ [R=301,L]
RewriteRule ^/wp-admin/includes/ - [F,L]
RewriteRule ^/wp-content/uploads/.+\.php - [F,L]
RewriteRule ^/wp-content/uploads/dlm_uploads/ - [F,L]
RewriteRule ^/wp-cron.php - [R=404,L]
RewriteRule ^/wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^/wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^/wp-includes/theme-compat/ - [F,L]
#
RewriteRule ^/wp-(content|admin|includes) - [L]
RewriteRule ^/.*\.php$ - [L]
RewriteRule ^/.*\.([a-zA-Z0-9]{1,3})$ - [L]
RewriteRule ^/.*\.(html)$ - [L]
RewriteRule ^/index\.php$ - [L]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
Audit
wordpress_check.py
#!/usr/bin/python3
import requests
import random
site = 'https://www.globalsp.com/'
s = requests.session()
headers = {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
for filename in [
'license.txt',
'readme.html',
'wp-content/uploads/dlm_uploads/index.html',
]:
url = site + filename
status_code = s.get(url, allow_redirects=False, headers=headers, params={ 'rand': random.randint(1,65535)}).status_code
print(filename.ljust(32), status_code, '✅' if status_code in (403, 404) else '❌')
for filename in [
'/wp-admin/admin-ajax.php',
]:
url = site + filename
status_code = s.get(url, allow_redirects=False, headers=headers, params={ 'rand': random.randint(1,65535)}).status_code
print(filename.ljust(32), status_code, '✅' if status_code == 200 else '❌')
for filename in [
'wp-content/uploads/foobar.php',
'wp-includes/class-feed.php',
'wp-includes/block-patterns.php',
]:
url = site + filename
status_code = s.get(url, allow_redirects=False, headers=headers, params={ 'rand': random.randint(1,65535)}).status_code
print(filename.ljust(32), status_code, '✅' if status_code == 403 else '❌')
wordpress_check.py output
license.txt 404 ✅
readme.html 404 ✅
wp-content/uploads/dlm_uploads/index.html 403 ✅
wp-content/uploads/foobar.php 403 ✅
wp-includes/class-feed.php 403 ✅
wp-includes/block-patterns.php 403 ✅