htacces configuration to secure/harden WordPress

#1
Hi there,

The htaccess directives below are intended to secure/harden WordPress.

Whilst implementing the directives, it occurred to me that a number of these may be unsupported e.g. <if></if> based on the knowledge article at https://www.litespeedtech.com/suppo...iki:config:unsupported_apache_modules_by_lsws . It isn't sufficiently clear as to if this is a complete. I have assumed it is.

I would appreciate it if the wider community and/or Litespeed team can provide feedback on the directives below and whether the construct below achieves the following.
  • Full support for Litespeed whilst maintaining compatability with Apache
  • Secure and if there are any additional improvements
  • Performant
  • Alternatives if there are particular directives that are not supported by Litespeed.

Apache config:
# BEGIN WordPress
# Instatiate “mod_rewrite” module for Apache
<IfModule mod_rewrite.c>
# Enable Rewrite module
RewriteEngine On
# Declare Rewrite base
RewriteBase /
# Ignore Rewrite condition if index.php is requested
RewriteRule ^index\.php$ - [L]
# Rewrite request if file and/or directory is not present
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

# BEGIN custom directives
# Disable directory listing
Options All -Indexes

# Limit file upload size. If you do not accept file uploads you can configure this at a minimum as per the below. The below is configured for 1MB.
# LimitRequestBody 1024000

# Configure custom error pages
# ErrorDocument 404 /notfound.php
# ErrorDocument 403 /forbidden.php
# ErrorDocument 500 /error.php

# Configure default error pages
# ErrorDocument 401 default
# ErrorDocument 403 default

# Enable Security Headers
<IfModule mod_headers.c>
    # X-Frame-Options
    Header always set X-Frame-Options "SAMEORIGIN"
    # X-XSS-Protection
    Header always set X-XSS-Protection "1; mode=block"
    # X-Content-Type-Options
    Header always set X-Content-Type-Options nosniff
    # Referrer-Policy
    Header always set Referrer-Policy "strict-origin"
    # Content-Security-Policy
    # Header set Content-Security-Policy-Report-Only default-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' https://maps.googleapis.com; frame-ancestors 'self'; block-all-mixed-content; form-action 'self'; font-src 'self' [URL]https://fonts.gstatic.com[/URL] data:; img-src 'self' https://secure.gravatar.com data:;
    Header set Content-Security-Policy default-src 'self' 'unsafe-inline' https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' https://maps.googleapis.com; frame-ancestors 'self'; block-all-mixed-content; form-action 'self'; font-src 'self' [URL]https://fonts.gstatic.com[/URL] data:; img-src 'self' https://secure.gravatar.com data:;
    # Feature Policy
    Header set Feature-Policy "geolocation 'none'; midi 'none'; camera 'none'; usb 'none'; magnetometer 'none'; accelerometer 'none'; vr 'none'; speaker 'none'; ambient-light-sensor 'none'; gyroscope 'none'; microphone 'none'"
    # Unset headers revealing versions strings
    Header unset X-Powered-By
    Header unset X-Pingback
    Header unset SERVER
    # Disable Entity Tag Header
    Header unset ETag
    FileETag None
    #Set secure cookies
    Header always edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=Strict
    # Set Keep Alive Header
    Header set Connection keep-alive
    # Instructs the proxy to store both a compressed and uncompressed version of the resource
    <FilesMatch ".(js|css|xml|gz|html|woff|woff2|ttf)$">
      Header append Vary: Accept-Encoding
    </FilesMatch>
</IfModule>

# Protect number of important WordPress and web server files such as config, ini and log files (this also includes wp-config.php etc)
<FilesMatch "^(wp-config\.php|wp-mail\.php|repair\.php|xmlrpc\.php|php\.ini|php5\.ini|install\.php|php\.info|readme\.html|bb-config\.php|\.htaccess|\.htpasswd|error_log|error\.log|PHP_errors\.log|\.svn|\.log|\.bak|wlwmanifest.xml|\..*)">
  Require all denied
</FilesMatch>

# Prevent WordPress usernames from enumerating
RewriteCond %{REQUEST_URI} !^/wp-admin [NC]
RewriteCond %{QUERY_STRING} ^author=\d+ [NC,OR]
RewriteCond %{QUERY_STRING} ^author=\{num [NC]
RewriteRule ^(.*)$ - [F,L]

# Block access to WordPress login page from malformed requests
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} ^(.*)?wp-login\.php(.*)$ [NC,OR]
RewriteCond %{REQUEST_URI} ^(.*)?wp-admin?(/)$ [NC]
RewriteCond %{HTTP_REFERER} !(.*)domain\.co(.*) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^-?$ [OR]
RewriteCond %{HTTP_USER_AGENT} ^.*(jorgee|morfeusfirefox\/34.0|mozilla\/4.0|firefox\/40.1).* [NC]
RewriteRule ^(.*)$ - [F,L]

# Restrict access to WordPress PHP files from plugin and theme directories
RewriteCond %{REQUEST_URI} ^(.*)?wp-content/plugins/(.*\.php)$ [NC]
RewriteRule ^(.*)$ - [F,L]
RewriteCond %{REQUEST_URI} ^(.*)?wp-content/themes/(.*\.php)$ [NC]
RewriteRule ^(.*)$ - [F,L]

# Block access to WordPress includes folder
RewriteCond %{REQUEST_URI} ^(.*)?wp-admin/includes/(.*\.php)$ [NC, OR]
RewriteCond %{REQUEST_URI} ^(.*)?wp-includes/js/tinymce/langs/(.*\.php)$ [NC, OR]
RewriteCond %{REQUEST_URI} ^(.*)?wp-includes/theme-compat/(.*\.php)$ [NC]
RewriteRule ^(.*)$ - [F,L]

<If "%{REQUEST_URI} =~ m#^(.*)?/wp-content/uploads/(.*\.txt)#">
    Require all denied
</If>

# Block Nuisance Requests
# https://perishablepress.com/block-nuisance-requests
<IfModule mod_alias.c>
# RedirectMatch 403 (?i)\.php\.suspected
# RedirectMatch 403 (?i)\.(git|well-known)
# RedirectMatch 403 (?i)apple-app-site-association
# RedirectMatch 403 (?i)/autodiscover/autodiscover.xml
  RewriteCond %{REQUEST_URI} ^(.*)?(\.php\.suspected|\.well-known|\.git|apple|autodiscover)(.*)$ [NC]
  RewriteRule ^(.*)$ - [F,L]
</IfModule>

# Blocking the »ReallyLongRequest« Bandit
# https://perishablepress.com/blocking-reallylongrequest-bandit/
<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_METHOD} .* [NC]
    RewriteCond %{THE_REQUEST}  (YesThisIsAReallyLongRequest|ScanningForResearchPurpose) [NC,OR]
    RewriteCond %{QUERY_STRING} (YesThisIsAReallyLongRequest|ScanningForResearchPurpose) [NC]
    RewriteRule ^(.*)$ - [F,L]
</IfModule>

# 6G FIREWALL/BLACKLIST
# @ https://perishablepress.com/6g/

# 6G:[QUERY STRING]
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{QUERY_STRING} (eval\() [NC,OR]
    RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR]
    RewriteCond %{QUERY_STRING} ([a-z0-9]{2000,}) [NC,OR]
    RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR]
    RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR]
    RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR]
    RewriteCond %{QUERY_STRING} (<|%3C)(.*)script(.*)(>|%3) [NC,OR]
    RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`|<|>|\|) [NC,OR]
    RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR]
    RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR]
    RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC]
    RewriteRule .* - [F]
</IfModule>

# 6G:[REQUEST METHOD]
<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_METHOD} ^(connect|debug|move|put|trace|track) [NC]
    RewriteRule .* - [F]
</IfModule>

# 6G:[REFERRER]
<IfModule mod_rewrite.c>
    RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000,}) [NC,OR]
    RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC]
    RewriteRule .* - [F]
</IfModule>

# 6G:[REQUEST STRING]
<IfModule mod_alias.c>
    RedirectMatch 403 (?i)([a-z0-9]{2000,})
    RedirectMatch 403 (?i)(https?|ftp|php):/
    RedirectMatch 403 (?i)(base64_encode)(.*)(\()
    RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\.
    RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&amp;?)/?$
    RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")
    RedirectMatch 403 (?i)(~|`|<|>|:|;|,|%|\\|\{|\}|\[|\]|\|)
    RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|muieblack)
    RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)
    RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$
    RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php
</IfModule>

# 6G:[USER AGENT]
<IfModule mod_setenvif.c>
    SetEnvIfNoCase User-Agent ([a-z0-9]{2000,}) bad_bot
    SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot

# Block IPs with 6G Firewall
# https://perishablepress.com/block-ips-6g-firewall/
    # Apache < 2.3
    <IfModule !mod_authz_core.c>
        Order Allow,Deny
        Allow from all
        Deny from env=bad_bot
    </IfModule>

    # Apache >= 2.3
    <IfModule mod_authz_core.c>
        <RequireAll>
            Require all Granted
            Require not env bad_bot
        </RequireAll>
    </IfModule>
</IfModule>
# END custom directives
 
Last edited by a moderator:
#3
Thanks Pong. I have rewritten the rule

Code:
<If "%{REQUEST_URI} =~ m#^(.*)?/wp-content/uploads/(.*\.txt)#">
    Require all denied
</If>
as follows for example.

Code:
RewriteCond %{REQUEST_URI} ^(.*)?/wp-content/uploads/(.*\.php)$ [NC]
RewriteRule ^(.*)$ - [F,L]
.

It would be valuable if you can provide insight into the questions as to whether the construct achieves the following
  • Full support for Litespeed whilst maintaining compatability with Apache
  • Secure and if there are any additional improvements
  • Performant
  • Alternatives if there are particular directives that are not supported by Litespeed.
 

Pong

Administrator
Staff member
#4
Your questions seems too big and too broad to answer. In general, LSWS tries to match Apache behavior as much as it can but it doesn't has some slightly different implementation on on certainly area such such the <if></if>, the rest LiteSpeed should act the same as apache.

LiteSpeed should be much secure and has a lot of built in security function as well https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:waf .

Most of the time, when you switching over between apache and LiteSpeed, they should work the same but with much better performance. check here for performance.
https://www.litespeedtech.com/benchmarks/http2-litespeed-nginx
 
Last edited by a moderator:
Top