Note: Rules on this page are updated from time to time. Last update: January 8, 2023.
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.
Next, click the Firewall Rules tab. Then click the Create a Firewall Rule button.
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). Double slashes are not an issue if you enable CloudFlare’s URL Normalization. But we still want to use the contains operator instead of the equals operator to prevent other obfuscation of the URI. - Set the rule Action to Challenge (Captcha).
Your rule form should look like this:
Click the 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.
- Set the rule Action to Block.
Your rule form should look like this:
Click the 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.
EXPRESSION EDITOR EXAMPLES
Here are just a few examples of what you can do with the Expression Editor:
- Block attempts to download the WordPress configuration or setup files…
(http.request.uri contains "wp-config.") or (http.request.uri contains "setup-config.")
- Block all attempts to access WordPress REST API…
(http.request.uri.path contains "/wp-json/")
Do not use this rule if you use the WordPress Block Editor, or have JetPack or other plugins installed that rely on REST.
- 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") or (http.request.uri contains "/wp-json/wp/v2/users/") or
Note: Blocking /wp-json/wp/v2/users/ might result in a lost of features in WordPress’ Block Editor.
- Block attempts to access phpMyAdmin through your public web site…
(lower(http.request.uri.path) contains "phpmyadmin")
Hopefully you aren’t allowing that anyway, but why waste your web server’s time processing attempts to access it?
- Block attempts to access the filesystem outside of the web site’s directory…
(raw.http.request.uri contains "../") or (raw.http.request.uri contains "..%2F")
%2F is the URL encoded value of / which is often used in scripting attacks. The raw. prefix allows us to examine the URI contents before CloudFlare’s URL Normalization is performed. URL Normalization prevents attempts to access the filesystem outside the web site’s directory. But it also prevents us from catching and blocking these attacks. Add this rule if you would like to see these attacks and where they are coming from in CloudFlare’s firewall log.
- 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 of the user agent string…
(http.user_agent contains "Nimbostratus")
Searching for a substring keeps this rule working if there are minor changes to the user agent string, such as a version number change.
- Block spammers, bots and other bad actors (as calculated by CloudFlare’s AI systems)…
(cf.threat_score gt 15)
- Block access to javascript source map files…
(http.request.uri.path contains ".js.map")
A source map is used by developer tools to transform compressed code back into human readable code. Web browsers have developer tools built-in to make use of source maps. Inspecting source on a web page will automatically trigger downloading a source map for each local javascript file found on a web page. Source map files should not be on a production web server, but themes and plug-ins might have them anyway. Requests like these mean someone is peeking at your code, so let’s make that more difficult by adding this rule.
- Block access to theme and plugin folders if an invalid referer header was sent…
There is never a legitimate case for direct access to the /wp-content/themes/ or /wp-content/plugins/ folders with no referer header. Simple vulnerability scanners look for known insecure files by making direct requests to your themes and plugins folders, which means that no referer header is sent. You can prevent this with the following firewall rule.((http.request.uri.path contains "/wp-content/themes/" or http.request.uri.path contains "/wp-content/plugins") and http.referer eq "")
We can get even more protection by checking for the existence of the web site’s base URL in the referer header. This will stop simple vulnerability scanners, and also bad actors hot-linking to your resources and stealing your bandwidth. Replace the host name in the URL in the following firewall rule with your own.
((http.request.uri.path contains "/wp-content/themes/" or http.request.uri.path contains "/wp-content/plugins") and (not http.referer contains "https://www.mydomain.com/"))
Notes:
- A slightly more sophisticated vulnerability scanner could send a valid referer header, negating the effectiveness of this firewall rule.
- Some users might not be able to access your site if they use a web browser plugin that prevents the sending of referer headers. This is actually not a violation of internet standards since the referer header is defined as optional.
- Some VPNs may strip out referer headers in an effort to increase user privacy. This will cause access problems.
- It is tempting to just treat the whole /wp-content/ folder and all its subfolders the same, instead of just the themes and plugins folders. But if you serve PDF files uploaded to your WordPress Media Library, you will soon see search engines, like Google, being denied access to those files. It is better to use CloudFlare’s Hotlink Protection to protect your media library.
- If you use a service that legitimately makes direct access to resources in theme or plugin folders, this rule will break that service. For example, using a logo in your theme folder as your domain’s BIMI logo.
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:
This set of rules is not absolutely necessary since CloudFlare has added its free “Bot Fight Mode”. But you can still use it to block specific user agents, or hackers sending invalid user agents.
Action: Block
Rule:(http.user_agent contains "?%00") or (http.user_agent contains "$[") or (http.user_agent contains "${") or (http.user_agent contains "absinthe") or (http.user_agent contains "AhrefsBot") or (http.user_agent contains "ALittle") or (http.user_agent contains "baidu") or (http.user_agent contains "BFAC") or (http.user_agent contains "/bin/bash") or (http.user_agent contains "brutus") or (http.user_agent contains "bsqlbf") or (lower(http.user_agent) contains "curl") or (http.user_agent contains "coccocbot") or (http.user_agent contains "commix") or (http.user_agent contains "crimscanner") or (http.user_agent contains "DavClnt") or (http.user_agent contains "datacha0s") or (http.user_agent contains "dirbuster") or (http.user_agent contains "DnyzBot") or (http.user_agent contains "domino") or (http.user_agent contains "DotBot") or (http.user_agent contains "dotdotpwn") or (http.user_agent contains "dragostea") or (http.user_agent contains "eval(") or (http.user_agent contains "env:") or (http.user_agent contains "fhscan") or (http.user_agent contains "floodgate") or (lower(http.user_agent) contains "fuzz") or (http.user_agent contains "get-minimal") or (http.user_agent contains "gobuster") or (http.user_agent contains "gootkit") or (http.user_agent contains "grabber") or (http.user_agent contains "grendel") or (http.user_agent contains "GRequest") or (http.user_agent contains "eadless") or (http.user_agent contains "havij") or (http.user_agent contains "Hello") or (http.user_agent contains "http-client") or (http.user_agent contains "hydra") or (http.user_agent contains "jdni") or (http.user_agent contains "Jorgee") or (http.user_agent contains "nowledge") or (http.user_agent contains "ldap") or (http.user_agent contains "lobster") or (http.user_agent contains "Lua") or (http.user_agent contains "lx71") or (http.user_agent contains "masscan") or (http.user_agent contains "mail.ru") or (http.user_agent contains "mea pentru") or (http.user_agent contains "metis") or (http.user_agent contains "Protocol Discovery") or (http.user_agent contains "morfeus") or (http.user_agent contains "mysqloit") or (http.user_agent contains "My User Agent") or (http.user_agent contains "nasl") or (http.user_agent contains "NetSystemsResearch") or (http.user_agent contains "Nikto") or (http.user_agent contains "Nimbostratus") or (http.user_agent contains "nmap") or (http.user_agent contains "Nuclei") or (http.user_agent contains "openvas") or (http.user_agent contains "pangolin") or (http.user_agent contains "PetalBot") or (http.user_agent contains "plesk") or (lower(http.user_agent) contains "python") or (http.user_agent contains "QQGameHall") or (http.user_agent contains "ReactorNetty") or (http.user_agent contains "RestSharp") or (http.user_agent contains "revolt") or (http.user_agent contains "Scrapy") or (http.user_agent contains "SeznamBot") or (http.user_agent contains "Sogou") or (http.user_agent contains "spbot") or (http.user_agent contains "springenwerk") or (http.user_agent contains "sqlmap") or (http.user_agent contains "sqlninja") or (http.user_agent contains "Uptimebot") or (http.user_agent contains "vega/") or (http.user_agent contains "w3af") or (http.user_agent contains "WebDAV-MiniRedir") or (http.user_agent contains "webshag") or (http.user_agent contains "WinHttp.WinHttpRequest") or (http.user_agent contains "wp_is_mobile") or (http.user_agent contains "YandexBot") or (lower(http.user_agent) contains "zmeu")
- Content Protection:
Action: Block
Rule:(cf.threat_score gt 14) or (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.uri contains "/wp-json/wp/v2/users/") or (http.request.uri contains "wp-config.") or (http.request.uri contains "setup-config.") or (http.request.uri.path contains "/wp-content/" and http.request.uri.path contains ".php") or (http.request.uri.path contains ".js.map") or (lower(http.request.uri.path) contains "phpmyadmin") or (lower(http.request.uri.path) contains "thinkphp") or (lower(http.request.uri.path) contains "/phpunit") or (raw.http.request.uri contains "../") or (raw.http.request.uri contains "..%2F") or (http.request.uri contains "passwd") or (http.request.uri contains "/var/log/") or (http.request.uri contains "/dfs/") or (http.request.uri contains "/autodiscover/") or (http.request.uri contains "/wpad.") or (http.request.uri contains "wallet.dat") or (http.request.uri contains "webconfig") or (http.request.uri contains "vuln.") or (http.request.uri contains ".sql") or (http.request.uri contains ".bak") or (http.request.uri contains ".cfg") or (http.request.uri contains ".env") or (http.request.uri contains ".ini") or (http.request.uri contains ".log") or (http.request.uri.query contains "bin.com/") or (http.request.uri.query contains "bin.net/") or (raw.http.request.uri.query contains "?%00") or (http.request.uri.query contains "eval(") or (http.request.uri.query contains "base64") or (http.request.uri.query contains "var_dump") or (http.request.uri.query contains "<script") or (raw.http.request.uri.query contains "%3Cscript") or (http.request.uri contains "<?php") or (http.cookie contains "<?php") or (http.cookie contains "<script") or (http.referer contains "%3Cscript") or (http.cookie contains"() {") or (http.cookie contains "base64") or (http.cookie contains "var_dump") or (http.request.method == "POST" and not (any(http.request.headers.names[*] == "content-type"))) or (upper(http.request.uri.query) contains "$_GLOBALS[") or (upper(http.request.uri.query) contains "$_REQUEST[") or (upper(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 your current IP address is listed in the events, then one of your firewall rules is being triggered by your actions on your web site.
- 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). Then, 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 re-enabling each one. 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 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 what caused the problem, try to make a more specific filter to protect your web site without causing problems for normal traffic.
- Searching for signs of SQL Injection attacks like UNION ALL and SELECT won’t catch much because it is easy to mix comments into those strings and still have them be valid SQL. A regular expression search would make it easier to detect SQL Injection attacks. CloudFlare provides the matches regex operator. However, even though the documentation does not state that the matches operator is restricted in any way, any attempt to save a firewall rule using a matches expression in a CloudFlare Free or Pro account will result in a message of…
not entitled to use regexp pattern in expression, at index: 0
If you have a paid CloudFlare account that allows for regex operators, you are better off using the included Cloudflare Web Application Firewall service to block SQL Injection attacks instead of rolling your own. - CloudFlare documentation mentions ((cf.bot_management.score lt 30) and not (cf.bot_management.verified_bot)) as a way to detect bad bots. Unfortunately this is not a free feature.
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 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. They most likely just want to add your web server to their army of ’bots for more nefarious purposes.
However, this is not 100% protection for your WordPress site. Some additional things you can do to protect your site are:
- Create a page rule on CloudFlare for */wp-admin/* where you add and set the Cache Level setting to Bypass. Add and set the Security Level setting to High. If your site does not force the use of HTTPS everywhere (in this day and age, it really should), add the Always Use HTTPS and Automatic HTTPS Rewrites settings to your rule and enabled them.
- In your CloudFlare account, go to Firewall >> Bots >> Bot Fight Mode, and enable it. Let CloudFlare’s AI do battle with ’bots before they touch your system.
- 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.
Share the post "How To Protect WordPress with CloudFlare Firewall Rules"