Secure WordPress on a VPS

Image of server racks

This guide will provide the user with the proper steps needed to secure their WordPress installation using a web application firewall, SSL certificate, and/or using the websites .htaccess file. Securing your WordPress installation on a VPS improves Search Engine Optimization, encrypts traffic, and can make it harder for malicious attackers to compromise your website and/or your web server.

Why Secure WordPress and Your VPS

WordPress is the single most used application on the internet which provides non-technical people the opportunity to simply install a theme and add plugins to customize the behavior of the website/blog. This helps small business owners and sole-proprietors get a website up in a matter or minutes without having to pay a developer to create a site from scratch. The downside to the popularity of WordPress is it is one of the most attacked platforms on the internet.

While WordPress itself is fairly secure and updated regularly, most attackers hope the owner of the site doesn’t update the software or any plugins that go with it. There is no possible way to make any electronic system 100 percent secure and stop all attacks. However, proper steps will secure the site and installation to keep it from being ‘low hanging fruit’.

Secure WordPress with a Web Application Firewall

Installing a Web Application Firewall is one of the easiest and quickest ways to secure your WordPress application. If you’re running a WordPress installation on your own VPS, be sure to read my post on how to setup and secure a Linux web server and apply server level firewall rules. The web application firewall (WAF) of choice for me is Wordfence. Wordfence is a WordPress plugin located in the plugins menu option on the left panel of the WordPress dashboard. Select Add New and search for Wordfence to install.

Once installation is complete a license key is required to use the plugin. Wordfence requires an email address for registration. Once installed, Wordfence will need to go through a minor setup, optimization, and a learning period. A couple of pop-up options may show, so just click allow on any pop-ups from Wordfence. The learning and optimization will help Wordfence gather data about the traffic of your site, and it will apply the analytics already known from malicious attackers and attack vectors. This could take a few weeks.

Setup Two-Factor Authentication – 2FA

Setting up two-factor authentication is one of the best way to secure any online account. Two-factor authentication provides layer of security that can prevent someone from accessing your account if they manage to get an account username and password. Securing WordPress site(s) with 2FA will ensure an attacker can’t get to the management console of a the WordPress installation, and modify the site or write malicious blog post on the owners behalf.

Setting up 2FA within Wordfence is relatively easy. The user will need a Temporary One Time Passcode (TOTP) from a secondary app. There a numerous apps available, but the one most in use is Authy. Authy allows the user to download a phone or desktop app to use with any online account that supports it. Once Authy is setup, all the user needs to do is scan the QR code with phones camera or type in the key provided. To locate the QR code/key, navigate to the Wordfence menu on the left menu bar of the WordPress management console and select Login Security.

Once the QR code appears, open the Authy (or similar app) and point the camera at the QR code. This will provide the user with the opportunity to accept the name and image, or modify them for better distinction. Once the account is added, there will be a six digit code that rotates every 30 seconds. After the code is displayed, download the backup codes to the right of the QR Code from within Wordfence. Once the backup codes are saved, type the six digit code from the Authy app into the text box below the backup codes and click Activate. Two-factor authentication is now setup with the WordPress site. If this has not been completed on other sites, please take the time to set it up now.

Secure WordPress and VPS With an SSL/TLS Certificate

An SSL certificate is a Secure Socket Layer certificate used to create a secure, encrypted connection between the web server and the client machine. SSL certificates are found across almost every website and web server hosting applications. An SSL certificate operates under a web of trust from a certificate authority and can be purchased from any hosting or domain registrar company. To learn more about SSL certificates, check out this article written by DigiCert.

Step 1: Create a Certificate Signing Request

A certificate signing request, CSR, is completed on the web server itself, which is like a digital signature for that server to be used for issuing an SSL certificate. This must be done from the command line and hosting providers typically provided instructions on how to do this. I will be describing how to do this with an Apache server as part of a LAMP stack.

To ensure the SSL certificate files are not lost during creation and installation, create a new folder in the home director for SSL

sudo mkdir SSL

Change directories into the SSL folder

cd SSL

Once in the SSL folder the CSR process can start. Using the code below copy this into the command line prompt, but be sure to change the server.key and the server.csr names. A best practice is to use the domain name they are associated with (i.e. mydomain_com.key and mydomain_com.csr). If more than one website is hosted on the same server, the generic names could be confusing.

openssl req -new -newkey rsa:2048 -nodes -keyout mydomain_com.key -out mydomain_com.csr

Once the key is created there will be a series of questions associated with the CSR. If this is for a personal domain/website, then any data can be put in here or nothing at all. However, you must put your fully qualified domain name in the FQDN spot, which is the second to last data input. This will tell your domain registrar (GoDaddy, Namecheap, etc) what domain the certificate is protecting.

Step 2: Activate the SSL Certificate

Once the CSR has been generated, you can use the code to activate the SSL certificate. To show a list of files and subfolders, run the following list command.

ls

After the files and subfolders are listed, there should be a file with a .csr extention with the mydomain_com.csr file name. To see the contents of the .csr file type

cat mydomain_com.csr

The certificate request should look something like the following: (This certificate request data is bogus and randomly made up)

-----BEGIN CERTIFICATE REQUEST-----
MIIC3DCCAcQCAQAwgZYxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVTdGF0ZTENMAsG
xt54mf47sXYa7rwZCdtNXyzpzf68jUkVQyFn4kPyODe+qARlgv0MBvs64msW7Ilk
A1UEBwwEQ2l0eTEVMBMGA1UECgwMQ29tcGFueSBOYW1lMRcwFQYDVQQLDA5Pcmdh
bml6YXRpb25hbDEVMBMGA1UEAwwMbXlkb21haW4uY29tMSEwHwYJKoZIhvcNAQkB
FhJhZG1pbkBteWRvbWFpbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDLsPIIrdfZLgg334Z5lDlIoP4BoSISVGWz2+kgZQ4dJmRcTpGO7dA15nQX
sngQNvpJEKx4MIoYLgSeOpDmliEPSo9A86njryZRI3/vNz5L/pm1m/vvWy0mXXAd
woUKn0J487ScGiNLQ27ksMW2HI8bZfzuHrijujiHNjUrvhKqN+tYAXvCFRUcwJka
B7glyP3CpRJteKW225VS/Y4zB+xFlC2ym4cg6cKS+O53iONeyklYlH4fFa7e88wM
pkvo+xD/wqY0y6nxFLCB2b6vc/7jAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEA
LIGN9y8mUftYsqhKrhNmEEOgFsB/tM7OHMZrFg1Vx9NOe1ZadEBm8Ow6CiKFz7Y4
GAokz28gYMi6gB0aYSg+AGMhVJpO1EG0JgfELabAjbb1A2tD/TrA4kSlGuztDHCU
D+05nfecDtNxNfshtt8ddEv83LIxF9lTm8x5zh48hPVxsdDN83d/T49sFBRiQukb
uktyO7dCcVKuunZ1uFrD742BeS2zniGhHXy3wYZz2aBmawntu/U8gmKKWv+945nw
aJ/zxJg2J0Zvc0G/jcihtQ259rpJpKxod0J6PNYsF9TCsrNFQR0Gxay1w0yZQ0ma
GTYNcNJHE4wxsuy03ynM2Q==
-----END CERTIFICATE REQUEST-----

Be sure to copy all text, including the —–BEGIN CERTIFICATE REQUEST—– and —–END CERTIFICATE REQUEST—–

Every domain registrar is different with their dashboards and how CSR’s are handled, but the Activiate Certificate option will be on the user dashboard under the SSL section. Your domain registar will have instructions on how to activate on their site.

You will have to take the CSR copied like above and paste it into a text box. This should bring up the domain name associated with the certificate. The registrar will require some kind of verification that you own the domain. This could be an email to the domain admin, by adding a CNAME entry in the DNS, or by uploading a validation file to the root directory of your website folder. Your registrar will have directions specifically for them.

Step 3: Install the SSL Certificate

The steps listed here are only for Apache running on a Debian/Ubuntu Linux server.

Step A: Installation check

Check the location of the configuration file for HTTP sites.

sudo apachectl -S

The default website configuration file is located at /etc/apache2/sites-enabled/000-default.conf. I have blocked out the configuration file names since I have mulitple sites hosted on this particular server.

In the next steps I’m going to show how to enable the HTTPS port 443 in the configuration file and start the Apache SSL service.

Step B: Enable SSL/TLS on Apache

Often, the SSL/TLS support is already enabled, but, if not, it can easily be enabled by typing the following command in:

sudo a2enmod ssl

If the module is already enabled, there will be a response “Module ssl already enabled”. If not, Apache will go through the steps to enable the module. Once enabled, type the following command to restart Apache so the changes can take effect.

sudo service apache2 restart

If the above command does not work, then try this one to achieve the same result.

sudo systemctl restart apache2

Step C: Configure the VPS to Use SSL For WordPress

Configuring the webserver can be tricky and I have had a number of headaches while trying to do this, especially if you are trying to run multiple websites and SSL certificates on a single server. First, we will look at where the configuration files are held, just like in Step A above.

sudo apachectl -S

The output should show the enabled sites located at /etc/apache2/sites-enabled/000-default.conf. This is the default system configuration file that will need to be changed. However, we are not going to change the default configuration file. I have made the mistake in the past by making edits to the default file and did not know how to revert back to the default. So, to save ourselves from any mistakes, we are going to create a new configuration file, but one with our domain name just in case we want to add mulitple websites and domains to the one server.

Navigate to the available sites folder by typing in the command

cd /etc/apache2/sites-available

Then list the contents of the folder using the ls command

ls

If there are no other sites on this server and it is a fresh install, then only the default configuration files will be there. You should at least see 000.default.conf and default-ssl.conf

Within the sites-available folder, create a copy of the 000-default.conf file.

cp 000-default.conf mydomain.conf

Before we start doing any modifications to the new file, create a copy of the default-ssl.conf file as well.

cp default-ssl.conf mydomain-ssl.conf

Some instructions found online say to use the 000-default.conf file and add the code into it, but I have found that does not always work and I always had to have two files, non-ssl and ssl.

Step D: Configure Non-SSL Traffic On VPS

Let’s update the configuration file for non-ssl web traffic. Open your configuration file using the following command

nano /etc/apache2/sites-available/mydomain.conf

Since this is a new configuration file, there will be minimal configurations in the file. We will have to make the necessary edits to make it work. If you want to host more than one website on a server, the ServerName option will require the “#” to be removed so Apache doesn’t see it as a comment. Items in BOLD are what you want to edit.

VirtualHost *:80>
        
        # Rewrite rules to direct all traffic the HTTPS version of the sites
        
        RewriteEngine On
        RewriteCond %{HTTPS} off
        RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        ServerName brandonlittle.com

        ServerAlias www.yourdomain.com
        Redirect permanent / https://www.yourdomain.com/

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

The BOLD portion at the top of the file is a rewrite condition that needs to be applied to redirect your non-encrypted traffic to the encrypted SSL traffic. This will redirect your traffic from http://mydomain.com to https://www.mydomain.com. You will always see the www before your domain name now.

The next file to edit will be the mydomain-ssl.conf file.

Step E: Configure the SSL Traffic File On VPS

This is the most important file to configure because it will encrypt the traffic to and from your website. Items in BOLD are what you want to edit. Navigate to

nano /etc/apache2/sites-available/mydomain-ssl.conf
<IfModule mod_ssl.c>
NameVirtualHost *:443
        <VirtualHost *:443>
                ServerAdmin webmaster@localhost

                DocumentRoot /var/www/html

                # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
                # error, crit, alert, emerg.
                # It is also possible to configure the loglevel for particular
                # modules, e.g.
                #LogLevel info ssl:warn

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                # For most configuration files from conf-available/, which are
                # enabled or disabled at a global level, it is possible to
                # include a line for only one particular virtual host. For example the
                # following line enables the CGI configuration for this host only
                # after it has been globally disabled with "a2disconf".
                #Include conf-available/serve-cgi-bin.conf

                #   SSL Engine Switch:
                #   Enable/Disable SSL for this virtual host.
                SSLEngine on

                #   A self-signed (snakeoil) certificate can be created by installing
                #   the ssl-cert package. See
                #   /usr/share/doc/apache2/README.Debian.gz for more info.
                #   If both key and certificate are stored in the same file, only the
                #   SSLCertificateFile directive is needed.
                SSLCertificateFile      /etc/ssl/mydomain_com.crt
                SSLCertificateKeyFile /etc/ssl/private/mydomain.key
                SSLCertificateChainFile /etc/ssl/mydomain_com.ca-bundle

                #   Server Certificate Chain:
                #   Point SSLCertificateChainFile at a file containing the
                #   concatenation of PEM encoded CA certificates which form the
                #   certificate chain for the server certificate. Alternatively
                #   the referenced file can be the same as SSLCertificateFile
                #   when the CA certificates are directly appended to the server
                #   certificate for convinience.
                #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt

                #   Certificate Authority (CA):
                #   Set the CA certificate verification path where to find CA
                #   certificates for client authentication or alternatively one
                #   huge file containing all of them (file must be PEM encoded)
                #   Note: Inside SSLCACertificatePath you need hash symlinks
                #                to point to the certificate files. Use the provided
                #                Makefile to update the hash symlinks after changes.
                #SSLCACertificatePath /etc/ssl/certs/
                #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt

                #   Certificate Revocation Lists (CRL):
                #   Set the CA revocation path where to find CA CRLs for client
                #   authentication or alternatively one huge file containing all
                #   of them (file must be PEM encoded)
                #   Note: Inside SSLCARevocationPath you need hash symlinks
                #                to point to the certificate files. Use the provided
                #                Makefile to update the hash symlinks after changes.
                #SSLCARevocationPath /etc/apache2/ssl.crl/
                #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl

                #   Client Authentication (Type):
                #   Client certificate verification type and depth.  Types are
                #   none, optional, require and optional_no_ca.  Depth is a
                #   number which specifies how deeply to verify the certificate
                #   issuer chain before deciding the certificate is not valid.
                #SSLVerifyClient require
                #SSLVerifyDepth  10

                #   SSL Engine Options:
                #   Set various options for the SSL engine.
                #   o FakeBasicAuth:
                #        Translate the client X.509 into a Basic Authorisation.  This means that
                #        the standard Auth/DBMAuth methods can be used for access control.  The
                #        user name is the `one line' version of the client's X.509 certificate.
                #        Note that no password is obtained from the user. Every entry in the user
                #        file needs this password: `'.
                #   o ExportCertData:
                #        This exports two additional environment variables: SSL_CLIENT_CERT and
                #        SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
                #        server (always existing) and the client (only existing when client
                #        authentication is used). This can be used to import the certificates
                #        into CGI scripts.
                #   o StdEnvVars:
                #        This exports the standard SSL/TLS related `SSL_*' environment variables.
                #        Per default this exportation is switched off for performance reasons,
                #        because the extraction step is an expensive operation and is usually
                #        useless for serving static content. So one usually enables the
                #        exportation for CGI and SSI requests only.
                #   o OptRenegotiate:
                #        This enables optimized SSL connection renegotiation handling when SSL
                #        directives are used in per-directory context.
                #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
                <FilesMatch "\.(cgi|shtml|phtml|php)$">
                                SSLOptions +StdEnvVars
                </FilesMatch>
                <Directory /usr/lib/cgi-bin>
                                SSLOptions +StdEnvVars
                </Directory>

                #   SSL Protocol Adjustments:
                #   The safe and default but still SSL/TLS standard compliant shutdown
                #   approach is that mod_ssl sends the close notify alert but doesn't wait for
                #   the close notify alert from client. When you need a different shutdown
                #   approach you can use one of the following variables:
                #   o ssl-unclean-shutdown:
                #        This forces an unclean shutdown when the connection is closed, i.e. no
                #        SSL close notify alert is send or allowed to received.  This violates
                #        the SSL/TLS standard but is needed for some brain-dead browsers. Use
                #        this when you receive I/O errors because of the standard approach where
                #        mod_ssl sends the close notify alert.
                #   o ssl-accurate-shutdown:
                #        This forces an accurate shutdown when the connection is closed, i.e. a
                #        SSL close notify alert is send and mod_ssl waits for the close notify
                #        alert of the client. This is 100% SSL/TLS standard compliant, but in
                #        practice often causes hanging connections with brain-dead browsers. Use
                #        this only for browsers where you know that their SSL implementation
                #        works correctly.
                #   Notice: Most problems of broken clients are also related to the HTTP
                #   keep-alive facility, so you usually additionally want to disable
                #   keep-alive for those clients, too. Use variable "nokeepalive" for this.
                #   Similarly, one has to force some clients to use HTTP/1.0 to workaround
                #   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
                #   "force-response-1.0" for this.
                # BrowserMatch "MSIE [2-6]" \
                #               nokeepalive ssl-unclean-shutdown \
                #               downgrade-1.0 force-response-1.0

        </VirtualHost>
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Now that Apache has been configured to accept traffic and redirect it to the SSL encrypted HTTPS, let’s enable the sites within Apache.

Enable Website In Apache

The final steps needed are to actually enable the sites on Apache so the server knows how to direct HTTP requests on port 80. This is a fairly simple process.

sudo a2ensite mydomain

This will enable both your mydomain.conf and your mydomain-ssl.conf configuration files. However, we want to ensure our due diligence. Navigate to the site-enabled folder under Apache2.

cd /etc/apache2/sites-enabled

Under this folder you should see both mydomain.conf and mydomain-ssl.conf. If there are any other sites enabled in this folder, then you may want to disable them. This would most likely be the default configuration files we started with. Input this command to disable them.

sudo a2dissite 000-default
sudo a2dissite default-ssl

The final step is to restart Apache so the configuration files can be loaded.

systemctl reload apache2

Final Testing Of WordPress Site

The final step is to test your site to ensure it works properly. Test this out in a couple different ways. First, navigate to mydomain.com to ensure it redirects to the https://www.mydomain.com. Finally, navigate to www.mydomain.com. If everything redirects to https://www.mydomain.com, then everything is working great!

Final WordPress Secure Configuration

One final setting in WordPress may need to be updated, if you haven’t done so already. Log into your WordPress site and click the Settings tab on the left menu bar. There will be two boxes showing the WordPress Address URL and the Site Address URL. Ensure both of these boxes list the HTTPS version of the site.

Screen capture of WordPress settings
Proper HTTPS settings in WordPress

Conclusion

Setting up and configuring your own server to host a website or blog can be a bit tedious at times. However, once you go through the configurations and understand them, it can give you much greater flexibility. Total control of a person Virtual Private Server allows you the opportunity to much more than using a piece of a server. Additionally, owning a full server will allow your site to grow and handle the increased traffic during peak load times. Otherwise, you may end up getting nasty letters from your hosting company. I’ve had a site go down in the past due to a large increase in traffic. Never again.

No Comments

Add Comment

Leave a Reply

Your email address will not be published. Required fields are marked *