Last active
March 3, 2024 23:27
-
-
Save posixpascal/aca919dc76b2bcc3078e870ef9f50853 to your computer and use it in GitHub Desktop.
wordpress hardening snippet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// 1. Please read this snippet carefully before continuing. [It may break your site if you are not careful] | |
// 2. Paste this snippet into ./wp-content/mu-plugins/security.php | |
// 3. Log-in to WP Dashboard once to apply the .htaccess changes | |
/** | |
* Copyright 2024 xupx - <[email protected]> & more | |
* | |
* =================================== | |
* Make Wordpress Safe Again | |
* | |
* This snippet is meant as a drop-in "must-use" plugin to make various safe wordpress adjustments | |
* | |
* @php >= 7.4 | |
* @wordpress >= 6.0 | |
*/ | |
// -> Configuration | |
const DISABLE_XMLRPC = true; // Disables WP XML RPC Endpoint which is often targeted for attacks | |
const DISABLE_PINGBACKS = true; // Disables WP Pingback Feature where a blog notifies another. | |
const DISABLE_FILE_EDITOR = true; // Disables File editor (malicous actors can gain RW access to all files using this) | |
const DISABLE_REST_ENDPOINTS = true; // Disables WP-JSON Authors Listing | |
const DISABLE_PLUGIN_UPDATE_MAIL = true; // Disables E-mail Notification when updating plugins | |
const DISABLE_THEME_UPDATE_MAIL = true; // Disables E-mail Notficiation when updating themes | |
const DISABLE_FEEDS = true; // Disable RSS feeds | |
const DISABLE_DEBUG = true; // Enforces removal of debug mode (except on localhost, see below) | |
const DISABLE_WP_AUTO_UPDATE = true; // Never automatically try to update wordpress | |
const REMOVE_WP_VERSION_FROM_SNIPPETS = true; // Remove ?ver= hint from scripts and snippets | |
const REMOVE_HEAD_DATA = true; // Remove unnecessary <head> meta tags | |
const REMOVE_MULTISITE_SIGNUP = true; // Removes redirect from multisite to signup.php | |
const REMOVE_WP_EMBED = true; // Removes oembed.js from loading in footer | |
const FORCE_SSL_ADMIN = true; // Forces SSL connection in admin menu | |
// -> .htaccess (this may break your site, if it does, edit this file and remove | |
// all of lines between the ### <Section> START ### & ### <Section> END ### block | |
// then disable some htaccess rules here and re-login into the backend. | |
const HTA_DISABLE_SERVER_SIGNATURE = true; // Removes X-Powered-By and other http header hints | |
const HTA_DISABLE_PHP_ERRORS = true; // Disables php error log | |
const HTA_DISABLE_INDEX = true; // Disables directory listing | |
const HTA_DISABLE_WP_FOLDERS = true; // Disables access to critical files (see below) | |
const HTA_DISABLE_AUTHORS = true; // Disables authors rest endpoint via htaccess | |
const HTA_DISABLE_XMLRPC = true; // Removes xmlrpc access via htaccess | |
const HTA_SECURE_PHP_SESSION = false; // Uses secure php session cookies | |
const HTA_DISABLE_EXPOSE_PHP = false; // Removes php powered-by header | |
// -> Helper Methods | |
function hta_edit($section, $rules){ | |
$sectionHead = "### $section START ###"; | |
$sectionFoot = "### $section END ###"; | |
$src = file_get_contents(ABSPATH . ".htaccess"); | |
$pattern = "/(" . preg_quote($sectionHead, '/') . ")(.*?)" . preg_quote($sectionFoot, '/') . "/s"; | |
// Check if the section already exists | |
if (preg_match($pattern, $src)) { | |
// Update the existing section with new rules | |
$newSrc = preg_replace_callback($pattern, function($matches) use ($sectionHead, $rules, $sectionFoot) { | |
// Construct replacement string manually, escaping $ as needed (some htaccess snippets use $1) | |
return $sectionHead . "\n" . str_replace('$', '\$', $rules) . "\n" . $sectionFoot; | |
}, $src); | |
} else { | |
// Append new section at the end if it doesn't exist | |
$newSrc = $src . "\n" . $sectionHead . "\n" . $rules . "\n" . $sectionFoot . "\n"; | |
} | |
file_put_contents(".htaccess", $newSrc); | |
} | |
function nope() | |
{ | |
return false; | |
} | |
function nothing() | |
{ | |
return ""; | |
} | |
function no($script) | |
{ | |
return function () use ($script) { | |
wp_deregister_script($script); | |
}; | |
} | |
function disable_pingbacks($headers) | |
{ | |
unset($headers['X-Pingback']); | |
return $headers; | |
} | |
function remove_wp_version($src) | |
{ | |
return remove_query_arg("ver", $src); | |
} | |
function remove_multisite_signup() | |
{ | |
if (!is_user_logged_in()) { | |
wp_redirect(site_url()); | |
exit; | |
} | |
} | |
// -> Perform security measures | |
if (DISABLE_XMLRPC) | |
add_filter('wp_xmlrpc_server_class', 'nope') && add_filter('xmlrpc_enabled', 'nope'); | |
if (DISABLE_PINGBACKS) | |
add_filter('wp_headers', 'disable_pingbacks') && add_filter('pings_open', 'nope'); | |
if (DISABLE_REST_ENDPOINTS) { | |
add_filter('rest_endpoints', function ($endpoints) { | |
if (is_user_logged_in()) { | |
return $endpoints; | |
} | |
foreach ($endpoints as $route => $endpoint) { | |
if (strpos($route, '/wp/v2/') === 0) { | |
unset($endpoints[$route]); | |
} | |
} | |
return $endpoints; | |
}); | |
} | |
if (DISABLE_PLUGIN_UPDATE_MAIL) { | |
add_filter('auto_plugin_update_send_email', 'nope'); | |
} | |
if (DISABLE_THEME_UPDATE_MAIL) { | |
add_filter('auto_theme_update_send_email', 'nope'); | |
} | |
if (DISABLE_FEEDS) { | |
/** | |
* List of feeds to disable | |
*/ | |
$feeds = [ | |
'do_feed', | |
'do_feed_rdf', | |
'do_feed_rss', | |
'do_feed_rss2', | |
'do_feed_atom', | |
'do_feed_rss2_comments', | |
'do_feed_atom_comments', | |
]; | |
foreach ($feeds as $feed) { | |
add_action($feed, function () { | |
wp_die('Feed has been disabled.', '', ['response' => 403]); | |
}, 1); | |
} | |
} | |
if (REMOVE_HEAD_DATA) { | |
// Remove post and comment feed link | |
remove_action('wp_head', 'feed_links', 2); | |
// Remove post category links | |
remove_action('wp_head', 'feed_links_extra', 3); | |
// Remove link to the Really Simple Discovery service endpoint | |
remove_action('wp_head', 'rsd_link'); | |
// Remove the link to the Windows Live Writer manifest file | |
remove_action('wp_head', 'wlwmanifest_link'); | |
// Remove the XHTML generator that is generated on the wp_head hook, WP version | |
remove_action('wp_head', 'wp_generator'); | |
// Remove start link | |
remove_action('wp_head', 'start_post_rel_link'); | |
// Remove index link | |
remove_action('wp_head', 'index_rel_link'); | |
// Remove previous link | |
remove_action('wp_head', 'parent_post_rel_link', 10, 0); | |
// Remove relational links for the posts adjacent to the current post | |
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0); | |
// Remove relational links for the posts adjacent to the current post | |
remove_action('wp_head', 'wp_oembed_add_discovery_links'); | |
// Remove REST API links | |
remove_action('wp_head', 'rest_output_link_wp_head'); | |
// Remove Link header for REST API | |
remove_action('template_redirect', 'rest_output_link_header', 11, 0); | |
// Remove Link header for shortlink | |
remove_action('template_redirect', 'wp_shortlink_header', 11, 0); | |
} | |
if (REMOVE_MULTISITE_SIGNUP) { | |
add_action('signup_header', 'remove_multisite_signup'); | |
} | |
if (REMOVE_WP_VERSION_FROM_SNIPPETS) { | |
add_filter('the_generator', 'nothing'); | |
add_filter('style_loader_src', 'remove_wp_version', PHP_INT_MAX); | |
add_filter('script_loader_src', 'remove_wp_version', PHP_INT_MAX); | |
} | |
if (REMOVE_WP_EMBED) { | |
add_action('wp_footer', no('wp-embed')); | |
} | |
if (DISABLE_FILE_EDITOR) { | |
define('DISALLOW_FILE_EDIT', true); | |
} | |
if (DISABLE_DEBUG && !in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1', 'localhost'])) { | |
define('WP_DEBUG', false); | |
define('WP_DEBUG_DISPLAY', false); | |
} | |
if (DISABLE_WP_AUTO_UPDATE) { | |
define('WP_AUTO_UPDATE_CORE', false); | |
} | |
if (FORCE_SSL_ADMIN){ | |
define('FORCE_SSL_ADMIN', true); | |
} | |
function hta_changes(){ | |
if (HTA_DISABLE_SERVER_SIGNATURE){ | |
hta_edit("Disable Server Signature", <<<EOT | |
ServerSignature Off | |
Header unset SERVER | |
Header unset X-Pingback | |
Header unset X-Powered-By | |
EOT); | |
} | |
if (HTA_DISABLE_PHP_ERRORS){ | |
hta_edit("PHP Errors Off", <<<EOT | |
php_flag display_errors off | |
EOT); | |
} | |
if (HTA_DISABLE_INDEX){ | |
hta_edit("Disable Directory Listing", <<<EOT | |
Options All -Indexes | |
EOT); | |
} | |
if (HTA_DISABLE_WP_FOLDERS){ | |
hta_edit("Disable File Access", <<<EOT | |
<IfModule mod_rewrite.c> | |
RewriteEngine On | |
RewriteBase / | |
RewriteRule ^(wp-content/uploads/.+.php)$ $1 [H=text/plain] | |
</IfModule> | |
<IfModule mod_rewrite.c> | |
RewriteEngine On | |
RewriteBase / | |
RewriteRule ^wp-admin/includes/ - [F,L] | |
RewriteRule !^wp-includes/ - [S=3] | |
RewriteCond %{SCRIPT_FILENAME} !^(.*)wp-includes/ms-files.php | |
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L] | |
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] | |
RewriteRule ^wp-includes/theme-compat/ - [F,L] | |
</IfModule> | |
<Files xmlrpc.php> | |
Order Allow,Deny | |
Deny from all | |
</Files> | |
<Files install.php> | |
Order allow,deny | |
Deny from all | |
Satisfy All | |
</Files> | |
<FilesMatch "(wp-config.php|wp-config-sample.php|liesmich.html|readme.html)"> | |
Order allow,deny | |
Deny from all | |
Satisfy All | |
</FilesMatch> | |
<Files ~ "^[\._]ht"> | |
Order Allow,Deny | |
Deny from all | |
Satisfy All | |
</Files> | |
<FilesMatch "(\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist|txt)|~)$"> | |
Order allow,deny | |
Deny from all | |
Satisfy All | |
</FilesMatch> | |
<Files robots.txt> | |
Order allow,deny | |
Allow from all | |
</Files> | |
EOT); | |
} | |
if (HTA_DISABLE_AUTHORS){ | |
hta_edit("Disable Authors Endpoint", <<<EOT | |
<IfModule mod_rewrite.c> | |
RewriteEngine On | |
RewriteBase / | |
RewriteCond %{REQUEST_URI} ^/$ | |
RewriteCond %{QUERY_STRING} ^/?author=([0-9]*) [NC] | |
RewriteRule ^(.*)$ http://%{HTTP_HOST}/? [L,R=301,NC] | |
</IfModule> | |
EOT); | |
} | |
if (HTA_DISABLE_XMLRPC){ | |
hta_edit("Disable XMLRPC", <<<EOT | |
<Files "xmlrpc.php"> | |
Order Deny,Allow | |
Deny from All | |
</Files> | |
EOT); | |
} | |
if (HTA_SECURE_PHP_SESSION){ | |
hta_edit("Secure PHP Session", <<<EOT | |
php_value session.cookie_httponly On | |
php_value session.cookie_secure On | |
php_flag session.use_only_cookies On | |
php_flag session.use_strict_mode On | |
EOT); | |
} | |
if (HTA_DISABLE_EXPOSE_PHP){ | |
hta_edit("Stop PHP Expose", <<<EOT | |
php_value expose_php Off | |
EOT); | |
} | |
} | |
add_action('wp_login', 'hta_changes', 10, 2); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment