WordPress, Nginx and Security

The Issue

If you are using WordPress then you undoubtedly have noticed an ever increasing number of login attempts on wp-admin.php. There are botnets out there that do nothing else but try to login to (any) WordPress Backend site. The reason is simple and not what you think; they are not going to defacing your site but rather looking for mailservers in good standing.  Once “hacked” into your wp-admin, they most commonly infect a theme and call that theme to send tens of thousands of Spam mails to the point where your server can no longer send email due to the fact that it’s blocked on most rbl’s out here. This is in fact so common, that removing malware from WordPress themes is a (near)fixed task at work.

But even if you have chosen a good, strong password combined with a non-standard username there is still the matter of exploiting bugs and glitches which might even bypass authorization altogether.

A Solution

The solution is simple, place a htaccess-protection in front of it. A htaccess protection is plain stupid from a technical point of view – not much can break, not much can be bypassed. By adding another layer of authentication you effectively protect your wp-admin and all current and future bugs and exploits.

But this would mean having to log in twice. Yuck.

And don’t think about using the same username and password combination for both locks.

A better solution

How I am doing it and how I recommend it: Run nginx (it does work with apache, too!) and always serve tls pages which is a best practice and good for your seo on google. I am assuming you already have a working nginx setup and your site is already being served out of nginx. So edit your nginx configuration add add these lines to enable tls:

server {
 listen 443 ssl ;
 server_name alpha-labs.net www.alpha-labs.net;
 ssl_certificate /etc/ssl/combined/alpha-labs.net.crt;
 ssl_certificate_key /etc/ssl/keys/alpha-labs.net.key;
 ssl_dhparam /etc/ssl/dh/alpha-labs.net.dh;
 [...]
}

This will make apache listen on port 443, using and serving the certificates under the appropriate paths. If you do not have your own certificate yet, pay a visit to startssl — domain validated certificates free of charge.

By the way: For an A+ scoring on ssllabs use these tls/ssl configuration in nginx:

    #   
    # SSL
    #   
    add_header                Strict-Transport-Security "max-age=31536000";
    add_header                X-Content-Type-Options nosniff;
    add_header                X-Frame-Options DENY;
    ssl_prefer_server_ciphers on; 
    ssl_protocols             TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache         shared:SSL:10m;
    ssl_session_timeout       10m;
    ssl_ciphers               'AES256+EECDH:AES256+EDH';

This will break compatibility with (way) older clients like Internet Explorer 6, but meh.  I am using (in a different configuration file) a rewrite rule that forwards all non-https requests to their https counterparts:

server {
 listen 80;
 rewrite ^ https://$host$request_uri? permanent;
}

So now all requests for your site on http will be redirected to https, but the wp-admin backend is still unprotected. So we will now activate optional client certificate authorization, that is all clients will log in with their certificates if they have one. If they do not, well, that’s why it’s optional. Add these lines below the ssl lines from above:

server {
 listen 443 ssl ;
 server_name alpha-labs.net www.alpha-labs.net;
 ssl_certificate /etc/ssl/combined/alpha-labs.net.crt;
 ssl_certificate_key /etc/ssl/keys/alpha-labs.net.key;
 ssl_dhparam /etc/ssl/dh/alpha-labs.net.dh;
 ssl_client_certificate /etc/ssl/cas/alpha-labs.net-ca.crt;
 ssl_crl /etc/ssl/cas/alpha-labs.net-ca.crl;
 ssl_verify_client optional;
 [...]
}

Now of course you actually need your own (self-signed) certificate authority for that, but setting that up is a breeze and worth the effort. If you have not already, check out this howto.

Here comes the awesome part: If a client wants to access /wp-admin/ on the server, nginx now checks if it has validated the certificate from the client. If it has, you are logged in, hassle-free. If the client did not provide a certificate, the client if forwarded back to /, making it look like there is no wp-admin directory at all. Again, add these lines:

server {
 listen 443 ssl ;
 server_name alpha-labs.net www.alpha-labs.net;
 ssl_certificate /etc/ssl/combined/alpha-labs.net.crt;
 ssl_certificate_key /etc/ssl/keys/alpha-labs.net.key;
 ssl_dhparam /etc/ssl/dh/alpha-labs.net.dh;
 ssl_client_certificate /etc/ssl/cas/alpha-labs.net-ca.crt;
 ssl_crl /etc/ssl/cas/alpha-labs.net-ca.crl;
 ssl_verify_client optional;

 location /wp-admin/ {
   if ($ssl_client_verify != SUCCESS) {
     rewrite        ^ https://alpha-labs.net permanent;
     break;
   }
   [...]
  }
[...]
}

And reload nginx. If you set your browser to automatically select and send client certificates then you will not notice the second layer of security at all. Everyone else just gets redirected to the main site, no questions asked. Worried about certificates and mobile devices? Don’t! Most mobile browsers support importing and authenticating of/with client certificates.

Congratulations: You are now protected. \o/

Christian

Touched base with Linux back in 1995, got hooked up on it ever since. I am using Linux for both private and office for two decades. Working as a System Administrator at a medium sized hosting company I get in touch with all kinds of trouble. All of which can be solved with Linux. In my blog I am sharing solutions to problems that I had to search for myself in hope that someone else out there might find them useful.

Leave a Reply

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