Security tips for WordPress

WordPress is much used and vulnerable CMS targetted by hackers from all over the world. There are some basic thing you can do to make WordPress safer.

Remove WordPress login errors description

function remove_wordpress__login_errors(){
  return 'Something is wrong!';
}
add_filter( 'login_errors', 'remove_wordpress__login_errors' );

Disable login by e-mail

Since e-mail addresses are predictable and much traded on the dark web. It is advised to remove login by e-mail so users can only login with a custom login name.

remove_filter( 'authenticate', 'wp_authenticate_email_password', 20 );

Remove unused users

The human factor in security is always a tricky one. Who in your organization adds and removes users from the WordPress CMS? Is it HR when a contract is terminated? Or is it the admin who keeps an eye on this? Have a procedure in place to make sure users are removed in an orderly fashion and on time.

Use SSL https for front end of website

SSL is common nowadays so make sure your WordPress website uses the SSL certificate.

## Force SSL for website. Add code to top of htaccess file
# redirect http traffic to https
RewriteEngine On
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.com/$1 [R,L]

Use SSL https for wp-admin

// Force SSL for admin. Add code to wp-config.php just above "That’s all, stop editing! Happy blogging."
// Source: https://www.wordfence.com/learn/how-to-harden-wordpress-sites/
define('FORCE_SSL_ADMIN', true);

Use SFTP

Use secure hosting

One of the most important security aspects for a secure website is your hosting provider. It is the gatekeeper for traffic to and from your website. Budget hosting companies often rely on yourself as the manager of security measures. But are you capable of doing that? A good hosting company provides good security.

Add two factor authentication

Force strong passwords (source: https://www.webtipblog.com/force-password-complexity-requirements-wordpress/ )

// functions.php

add_action( 'user_profile_update_errors', 'validateProfileUpdate', 10, 3 );
add_filter( 'registration_errors', 'validateRegistration', 10, 3 );
add_action( 'validate_password_reset', 'validatePasswordReset', 10, 2 );



/**
 * validate profile update
 *
 * @author  Joe Sexton <joe.@webtipblog.com>
 * @param   WP_Error $errors
 * @param   boolean $update
 * @param   object $user raw user object not a WP_User
 */
public function validateProfileUpdate( WP_Error &$errors, $update, &$user ) {

	return validateComplexPassword( $errors );
}


/**
 * validate registration
 *
 * @author  Joe Sexton <joe.@webtipblog.com>
 * @param   WP_Error $errors
 * @param   string $sanitized_user_login
 * @param   string $user_email
 * @return  WP_Error
 */
function validateRegistration( WP_Error &$errors, $sanitized_user_login, $user_email ) {

	return validateComplexPassword( $errors );
}

/**
 * validate password reset
 *
 * @author  Joe Sexton <joe.@webtipblog.com>
 * @param   WP_Error $errors
 * @param   stdClass $userData
 * @return  WP_Error
 */
function validatePasswordReset( WP_Error &$errors, $userData ) {

	return validateComplexPassword( $errors );
}

/**
 * validate complex password
 *
 * @author  Joe Sexton <joe.@webtipblog.com>
 * @param   WP_Error $errors
 * @param   stdClass $userData
 * @return  WP_Error
 */
function validateComplexPassword( $errors ) {

	$password = ( isset( $_POST[ 'pass1' ] ) && trim( $_POST[ 'pass1' ] ) ) ? $_POST[ 'pass1' ] : null;

	// no password or already has password error
	if ( empty( $password ) || ( $errors->get_error_data( 'pass' ) ) )
		return $errors;

	// validate
	if ( ! isStrongPassword( $password ) )
		$errors->add( 'pass', '<strong>ERROR</strong>: Your password must contain at least 8 characters.' ); // your complex password error message

	return $errors;
}

/**
 * isStrongPassword
 *
 * @author  Joe Sexton <joe.@webtipblog.com>
 * @param   string $password
 * @return  boolean
 */
function isStrongPassword( $password ) {

	return strlen( $password ) >= 8; // your complex password algorithm
}

Remove inactive themes, files, plugins, scripts

Protect login page

<Files wp-login.php>
	Order Deny,Allow
	Deny from all
	Allow from 123.456.789
</Files>

Protect wp-config.php

<Files wp-config.php>
	Order Allow,Deny
	Deny from all
</Files>

Disable file editing

define('DISALLOW_FILE_EDIT', true);

Disable password reset

Although a password reset is useful in most use cases but sometimes you can do without the password reset functionality in WordPress. Here is code to disable it for all users. You can adapt the code and disable the password reset for specific users.

// Disable password reset
// Source: https://mythemeshop.com/blog/remove-password-reset-wordpress/
function disable_password_reset() { 
   return false; 
}
add_filter ( 'allow_password_reset', 'disable_password_reset' );

Limit login attempts

Brute force attacks are common and can be

/**
 * Code to limit login attempts 1/4
 * source: https://phppot.com/wordpress/how-to-limit-login-attempts-in-wordpress/
 */
 // Check how many times the user tried to login unsuccessfully
function check_attempted_login( $user, $username, $password ) {
    // Get logged attempts
    if ( get_transient( 'attempted_login' ) ) {
        $datas = get_transient( 'attempted_login' );
	// Check if user tried before
        if ( $datas['tried'] >= 3 ) {
            $until = get_option( '_transient_timeout_' . 'attempted_login' );
            $time = time_to_go( $until );
	    // Return error to user
            return new WP_Error( 'too_many_tried',  sprintf( __( '<strong>ERROR</strong>: You have reached authentication limit, you will be able to try again in %1$s.' ) , $time ) );
        }
    }
    return $user;
}
add_filter( 'authenticate', 'check_attempted_login', 30, 3 ); 

/**
 * Code to limit login attempts 2/4
 * source: https://phppot.com/wordpress/how-to-limit-login-attempts-in-wordpress/
 */
// If login failed update number of attempted logins
function login_failed( $username ) {
    if ( get_transient( 'attempted_login' ) ) {
        $datas = get_transient( 'attempted_login' );
		// Increment attempts
		// Remark: one login updates this three times! So it goes from 0->3
        $datas['tried']++;
        if ( $datas['tried'] <= 3 ) {
            set_transient( 'attempted_login', $datas , 300 );
		}
	// If no former attempts set first one
    } else {
        $datas = array(
            'tried'     => 1
        );
        set_transient( 'attempted_login', $datas , 300 );
    }
}
add_action( 'wp_login_failed', 'login_failed', 10, 1 ); 

/**
 * Code to limit login attempts 3/4
 * source: https://phppot.com/wordpress/how-to-limit-login-attempts-in-wordpress/
 */
// If login successfull reset attempted logins to one
function login_succesfull( $username ) {
	// Check if there are logged attempts
    if ( get_transient( 'attempted_login' ) ) {
        $datas = get_transient( 'attempted_login' );
		// Check if there are more than three attempts
        if ( $datas['tried'] >= 3 ) {
			// Reset to zero
			//$datas['tried'] = 0;
            //set_transient( 'attempted_login', $datas , 300 );
			delete_transient('attempted_login');
			delete_transient('timeout_attempted_login');
		}
    } 
}
add_action( 'wp_login', 'login_succesfull', 99, 1 ); 

/**
 * Code to limit login attempts 4/4
 * source: https://phppot.com/wordpress/how-to-limit-login-attempts-in-wordpress/
 */
function time_to_go($timestamp) {
    // converting the mysql timestamp to php time
    $periods = array(
        "second",
        "minute",
        "hour",
        "day",
        "week",
        "month",
        "year"
    );
    $lengths = array(
        "60",
        "60",
        "24",
        "7",
        "4.35",
        "12"
    );
    $current_timestamp = time();
    $difference = abs($current_timestamp - $timestamp);
    for ($i = 0; $difference >= $lengths[$i] && $i < count($lengths) - 1; $i ++) {
        $difference /= $lengths[$i];
    }
    $difference = round($difference);
    if (isset($difference)) {
        if ($difference != 1)
            $periods[$i] .= "s";
            $output = "$difference $periods[$i]";
            return $output;
    }
}

Don’t use admin or administror username

If you used admin or administrator as username then rename it to a random name so it cannot be used for brute force attacks. I prefer going to phpmyadmin and rename the user_login name there. Also edit the name that is public to a name that doesn’t resemble the login name.

Hide WordPress version

If you view your WordPress website source there is a meta tag that shows your WordPress version: <meta name=”generator” content=”WordPress 5.4″ />. To hide this information from hackers, just remove it via the following code in your functions.php.

// Hide WordPress version in meta data
// source: https://www.malcare.com/blog/wordpress-security/
function remove_wordpress_version() {
	return '';
}
add_filter('the_generator', 'remove_wordpress_version');

Stel je vraag

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *