How To Protect WordPress with CloudFlare Firewall Rules

OVERVIEW

CloudFlare recently added Firewall Rules to all accounts, including free accounts. With these rules, you can help protect your WordPress installation against common attacks for both known and undisclosed vulnerabilities.

PROCEDURE

First, log in to your CloudFlare account, then click the Firewall button in the toolbar at the top of the page.

CloudFlare Toolbar

Next, in the Firewall Rules section at the top of the page content, click the Create a Firewall Rule button.

CloudFlare Firewall Rules Section


LOGIN PROTECTION FIREWALL RULE

The first firewall rule we will add will help protect the WordPress login page.

  • Give your rule a name, such as “Login Protection.”
  • Select URI Path as the Field, contains as the Operator, /wp-login.php as the Value.
    Note: The reason we don’t use equals as the Operator is because it could be easily circumvented if the attacker used a URI Path of //wp-login.php (a double slash in a URI Path is interpreted as a single slash on most web servers).
  • Set the rule Action to Challenge (Captcha).

Your rule form should look like this:

CloudFlare WordPress Login Protection Firewall Rule

 

Click the Save and Deploy button. Test your new firewall rule by trying to log in to your WordPress web site. You should be presented with a CAPTCHA which you must answer correctly before being given access to the WordPress login page.  If not, check your firewall rule for typos and selection errors.

This firewall rule will stop all automated attacks on your login page. However, it will not stop login attacks against the REST or XMLRPC interfaces of your WordPress site. There are many security plugins available for WordPress that can help protect those interfaces from attacks on your web site.

VULNERABLE PLUGIN AND ROGUE PHP FILE BLOCKING RULE

The next firewall rule we will add will help protect your WordPress web site from attacks on vulnerable plugins. It will also stop access to PHP files a hacker might have managed to upload to your site.

  • Give your rule a name, such as “Content Protection.”
  • Select URI Path as the Field, contains as the Operator, and /wp-content/ as the Value.
  • Click the And button to add another match specification.
  • For the added match specification, select URI Path as the Field, contains as the Operator, and .php as the Value.
    Note: If you have a paid CloudFlare account, this is unnecessary. You can use a single match operator and specify a regular expression to detect access to PHP files in the /wp-content/ folder.
  • Set the rule Action to Block.

Your rule form should look like this:

CloudFlare Content Rule

Click the Save and Deploy button. Test your new firewall rule by trying to directly access any PHP file in the /wp-content/ folder. For example, if you have the WP Super Cache plugin installed, try accessing https://<my-web-site.com>/wp-content/plugins/wp-super-cache/wp-cache.php and you should get an Access Denied response from CloudFlare.

Note: Properly designed themes and plugins should never require direct access to PHP files in the /wp-content/ folder. If your theme or one of your plugins stops working after adding this firewall rule, be sure to complain to the maker of that theme or plugin. Better yet, uninstall it, because the developer can not be trusted if they made such a basic design mistake.

THE EXPRESSION EDITOR

Under the inputs for the Firewall Rule form you will notice the Expression Preview text box. This provides an easy way to copy and share Firewall Rules. Click the Edit Expression link to switch into the Expression Editor mode.

Cloudflare Expression Editor

Try adding these new blocking rules in the Expression Editor mode:

  • Block attempts to download the WordPress configuration file…
    (http.request.full_uri contains "wp-config.")
  • Block all attempts to access WordPress REST API (do not use if you have JetJack or other plugins installed that rely on REST)…
    (http.request.uri.path contains "/wp-json/")
  • Block author name scanning (sometimes a precursor to brute-force login attacks, but could also be search engines trying to discover content)…
    (http.request.uri.query contains "author_name=") or 
    (http.request.uri.query contains "author=" and not http.request.uri.path contains "/wp-admin/export.php")
  • Block attempts to access phpMyAdmin through your public web site. Hopefully you aren’t allowing that anyway, but why waste your web server’s time processing attempts to access it?
    (http.request.uri.path contains "phpmyadmin")
  • Block attempts to access the filesystem outside of the web site’s directory.  Be careful with this one since some poorly written CSS or HTML might make use of this to access assets in directories above it in the file system.  Also check for the URL encoded value of / which is %2F since this is often used  in scripting attacks.
    (http.request.full_uri contains "../") or (http.request.uri contains "..%2F")
  • Block attempts to access the password file by exploiting buggy plugins or themes.
    (http.request.uri contains "passwd")
  • Block bad bots and unwanted user agents with a substring search to handle user agent string changes (such as version number changes).
    (http.user_agent contains "Nimbostratus")
  • Block spammers, bots and other bad actors (as calculated by CloudFlare’s AI systems).
    (cf.threat_score gt 20)

COMBINE FIREWALL RULES

As we can see, the expression editor allows the use of parenthesis and the logical operators and and or. This means we can combine all rules that use the same Action into a single rule. To protect a WordPress site with all the rules we have made so far, we can reduce them down to two rules; one that combines all the Block rules, and another for the Challenge action when someone accesses the login page.

  • Unwanted Bots and User Agents:
    Action: Block
    Rule:
    (cf.threat_score gt 20) or 
    (http.user_agent contains "AhrefsBot/") or 
    (http.user_agent contains "BaiDuSpider") or 
    (http.user_agent contains "baidu.com") or 
    (http.user_agent contains "/bin/bash") or 
    (http.user_agent contains "[email protected]") or 
    (http.user_agent contains "DnyzBot/") or 
    (http.user_agent contains "DotBot/") or 
    (http.user_agent contains "eval(") or 
    (http.user_agent contains "Go-http-client/") or 
    (http.user_agent contains "Nikto") or 
    (http.user_agent contains "Nimbostratus") or 
    (http.user_agent contains "python-requests") or 
    (http.user_agent contains "Scrapy/") or 
    (http.user_agent contains "SeznamBot/") or 
    (http.user_agent contains "Sogou web spider/") or 
    (http.user_agent contains "spbot/") or 
    (http.user_agent contains "Uptimebot/") or 
    (http.user_agent contains "WebDAV-MiniRedir") or 
    (http.user_agent contains "WinHttp.WinHttpRequest") or 
    (http.user_agent contains "ZmEu")
    
  • Content Protection:
    Action: Block
    Rule:
    (http.request.uri.query contains "author_name=") or 
    (http.request.uri.query contains "author=" and not http.request.uri.path contains "/wp-admin/export.php") or 
    (http.request.full_uri contains "wp-config.") or 
    (http.request.uri.path contains "/wp-json/") or 
    (http.request.uri.path contains "/wp-content/" and http.request.uri.path contains ".php") or 
    (http.request.uri.path contains "phpmyadmin") or 
    (http.request.full_uri contains "<?php") or 
    (http.cookie contains "<?php") or 
    (http.request.full_uri contains "../") or (http.request.full_uri contains "..%2F") or 
    (http.request.full_uri contains "passwd") or 
    (http.request.uri contains "/dfs/") or 
    (http.request.uri contains "/autodiscover/") or 
    (http.request.uri contains "/wpad.") or 
    (http.request.full_uri contains "webconfig.txt") or 
    (http.request.full_uri contains "vuln.") or 
    (http.request.uri.query contains "base64") or 
    (http.request.uri.query contains "<script") or (http.request.uri.query contains "%3Cscript") or 
    (http.cookie contains "<script") or (http.referer contains "<script") or 
    (http.request.uri.query contains "$_GLOBALS[") or (http.request.uri.query contains "$_REQUEST[") or (http.request.uri.query contains "$_POST[")
    
  • Login Protection:
    Action: Challenge (Captcha)
    Rule:
    (http.request.uri.path contains "/wp-login.php")

TROUBLESHOOTING

If you see strange behavior with your web site after enabling any firewall rules, here are some steps to try to track down the problem.

  • Check the Events tab on the Firewall page. If you see your current IP address listed in the events, then one of your firewall rules is being triggered by your actions on your web site. Viewing the details of a firewall event does not give you a human readable indicator of which rule was triggered; it gives you a Filter ID number instead. However, you might be able to figure out exactly which part of which rule caused the trigger by examining the URL and User Agent in the events details.
  • Update: Click on any event with a Service column labeled "Firewall rule” to view details of the event.  This will show you which firewall rule triggered the event.  Hover your pointer over the Rule ID to reveal a Filter button.  Clicking the Filter button will take you directly to the editor for that Firewall Rule.  If you still can’t figure out what is triggering a firewall event, try the following steps.
  • Disable all your firewall rules (toggle the switch next to each rule) and see if the problems you are observing are fixed. Your IP address should not be showing up in any new firewall events listed in the Events tab.
  • Next, re-enable each firewall rule one at a time, testing your web site after each one is re-enabled. When the problem on your web site returns, you know you have found the rule causing the problem.
  • Next, use the Expression Editor to copy-and-paste the entire rule that is causing the problem to a temporary document. Then, use the Expression Builder to delete individual lines from the rule, and save the rule after each line you delete. Check your web site to see if the problem went away. If it did, you found the line causing the problem. If not, continue deleting lines, saving, and testing your web site until you find the line causing the problem.
  • Once you have isolated the problem, use the Expression Editor to restore the original rule from your temporary copy. Then delete the one line that was causing the problem. Save the edited rule and verify the problem has gone away. If not, more than one line may be causing a problem, most likely a line after the one you deleted. Repeat the previous procedure until you find the new line causing the problem.
  • Rather than just eliminating the line, or lines, that caused the problem, see if you can determine a way to make a more specific filter that will protect your web site without causing problems for normal web server traffic.

FURTHER READING

Check out the CloudFlare Firewall Rules documentation for more information and ideas on how to put this powerful feature to work.

MORE PROTECTION

These firewall rules will stop a lot of attacks on your web site. Just return to your CloudFlare account in a 24 hours and look at the Firewall Events log at the bottom of the Firewall page.  You will see at least a dozen attacks blocked by these rules.  Don’t take it personally, these attackers most likely do not know you.

However, this is not 100% protection for your WordPress site. Some additional things you can do to protect your site are:

  • Keep WordPress, your theme and plugins up-to-date. Read the Configuring Automatic Background Updates article to learn how to update your web site as soon as updates are available.
  • Review and follow the advice given in the Hardening WordPress article.
  • Add a WordPress security plugin like NinjaFirewall.
  • Install and configure server level protection, such as mod_security, Fail2Ban, and bad bot blockers. Note: If you aren’t running your own web server, your web hosting company should be taking care of server level protection for you.
  • Read more details on what fields and expressions are possible with CloudFlare’s firewall rules.
  • Cloudflare offers a paid Web Application Firewall service which is a like having the firewall rules outlined in this article, plus many more, all kept up-to-date by their engineers.