Solving the WordPress AJAX protection challenge: Using Cloudflare Tunnel to "distribute" normal website access traffic from attack traffic.
This article was last updated 149 days ago. The information in it may have developed or changed. If it is invalid, please leave a message in the comment section.

Preface

I have been facing continuous attacks since a few weeks ago. The key point is that these attacks are not the previous HTTP flood attacks with no technical content, but are based on a weak point in the defense of WordPress: the ajax function entrance (/wp-admin/admin-ajax.php) launched a slow rate attack (several to more than ten per second):

image.png

It just happened to circumvent the global rate limit set on my cloudflare, and the range of the attacking IPs has changed a lot. Previously, it was from abroad, mainly Russia, but in recent days it has changed to domestic IPs:

image.png

Among them, the biggest threat is the attack from Russia, because it seems to be able to disguise itself as a spider of the Yandex search engine, thereby passing the screening of "known automatic programs" in the Cloudflare WAF rules and directly hitting my intranet (it even seems to be able to penetrate Cloudflare's "5-second shield", because I tried to set up a hosting query specifically for this path for a while, but the attack from Russia still hit the intranet WAF, while others were blocked, but I don't have time to verify this, so I can't confirm it completely), which made me confused when I saw the huge amount of blog access data for a few days. The requests in the red box in the figure below are just the blog request data seen on the intranet WAF from 0:00 to 8:00 on the 26th:

image.png

Therefore, I was forced to change the skip strategy of cloudflare WAF:
image.png

Due to the robustness of my three-layer defense system (outer layer: Free version of cloudflare WAF, middle layer: community version of Changting Lei Chi WAF, inner layer: free version of wordpress security plug-in wordfence), these attacks did not have a substantial impact on my blog visits, but they seriously affected the accuracy of my statistical data (people with obsessive-compulsive disorder feel really uncomfortable looking at the huge number of requests for blog sites on Changting Lei Chi WAF). I didn’t originally plan to write about this in my article this week, but because I was annoyed by this kind of attack, it was like a fly always flying around my ears, so I decided to tackle this problem!

Additional knowledge: admin-ajax.php of wordpress

First, let's talk about WordPress's admin-ajax.php (the full path is/wp-admin/admin-ajax.php).

admin-ajax.php is the AJAX function entry point of WordPress: many dynamic functions in WordPress (including comment verification, likes, reply loading, etc.) are implemented using AJAX requests, and these requests are usually completed through the /wp-admin/admin-ajax.php path (for popular science knowledge about AJAX technology, please refer to my other article:Home Data Center Series: A review of the core concepts of current Web development starting with the Ajax cross-domain issue (from a non-programmer's perspective)).


Taking the behavior of users posting comments on WordPress as an example, let’s briefly introduce the workflow of AJAX requests.

  1. User actions trigger AJAX requests:

After the user enters a comment in the comment form and clicks the submit button, the front-end JavaScript will initiate a background request via XMLHttpRequest or fetch, usually pointing to /wp-admin/admin-ajax.php.

  1. The browser initiates an HTTP request:

The browser will follow the JavaScript instructions, encapsulate the comment data into an HTTP request and send it to the server (admin-ajax.php in this case).

  1. Request Features:

• The request header will carry necessary information (such as Cookies, Referer, etc.) so that the server can identify the user status.

• They are usually POST requests, used to submit data (such as comment content).


Originally, if you don't manage WordPress directly through the domain name provided for external access (this is what I think is the way to safely operate WordPress: separate the address provided for external access from the address used for normal management of WordPress, so as to facilitate the setting of WAF policies), and you don't need the comment function, then the simplest and crudest way is to directly set all the domains with the domain name of/wp-adminThe access request at the beginning is directly blocked, but although I do not manage through this domain name, the comment function is needed, so it cannot be solved so roughly.

So, if we don't block it, can we use cloudflare's WAF to handle the access request to admin-ajax.php by means of a challenge? No, because Cloudflare will perform additional verification on each request (such as browser fingerprint analysis, behavior verification, etc.). If the verification fails, the request will be blocked, which is fatal for ajax requests, because these requests are usually automatically initiated by front-end scripts, not complete browser interaction behaviors, so they will not respond to challenges (I didn't pay attention to it at first, but after setting it up, a friend told me that I couldn't post comments, and I realized this problem, and then I found that the translation function was gone~).

Generally speaking, there are two ways to attack admin-ajax.php: direct and indirect. The direct way is to attack admin-ajax.php directly. This is easier to deal with and can usually be solved by the Changting Leichi WAF in the intranet. The indirect way is to use other applications based on ajax technology to attack indirectly, such as the application with the path "/wp-content/plugins/translatepress-multilingual/includes/trp-ajax.php". This is the path of the translation plug-in TranslatePress I use. The high-frequency attack on admin-ajax.php can be indirectly achieved by calling the translation plug-in at a high frequency (after all, any ajax call on WordPress must go through the admin-ajax.php entrance):

image.png

This is more difficult to deal with because it is actually a normal call, but the call frequency is very high and it continues (it can also be considered a targeted DDoS attack). At the same time, by scanning WordPress, you can know which plug-ins are installed. As long as the plug-in uses ajax technology, it can become an accomplice of indirect attack.

Solution ideas

Outer layer: cloudflare layer

From the perspective of Cloudflare free users, the most effective script detection mechanism is usually the WAF hosted challenge. Unfortunately, as mentioned earlier, Cloudflare's challenge mechanism is designed to verify user behavior, but AJAX requests cannot automatically pass the challenge because it lacks the ability for user interaction.

So can we start from the User-Agent aspect? Actually, it is not impossible. For a while, I used WAF rules to block abnormal traffic based on UA. Unfortunately, UA can be changed at will. The key is that the attackers are not a group of people. The UAs are different and keep changing. I gave up this stupid method after blocking for a while.

So in theory, the rate limiting function can be used, because rate limiting can set a threshold for requests to access a certain path, which can at least greatly limit the number of attacks:

image.png

Unfortunately, although the rate limit can add multiple URI paths through the "or" method, the final request rate threshold can only be set to one. Generally speaking, when used for full-site DDos protection, this set rate value needs to be able to ensure normal access, so it cannot be set too low. Therefore, there is nothing to do for attacks that do not reach this rate threshold but are higher than the normal access rate (this is what I encountered this time). From the perspective of maximizing value, this single rate value is best used to protect against full-site DDos attacks:

image.png

Of course, it is possible to temporarily use rate limiting rules to mitigate attacks against admin-ajax.php (if you want to do this, it is best to directly limit the path of the application that calls admin-ajax.php, such as the path of TranslatePress in my case). However, in this case, the threshold of the rate limit setting must be set small, so that the DDos attack protection of the entire site cannot be taken into account (a small setting will affect the normal access of the website).

To sum up, from the perspective of cloudflare (Free users), there is no way to solve the problem of admin-ajax.php attack traffic only through WAF-related configuration (it is impossible to block it, and it is impossible to question it, and using rate limiting is like using a cannon to kill a mosquito). Although in theory the best solution is to be able to solve this problem at the cloudflare level, ideals are full and reality is very skinny, so the strategy of keeping the enemy out of the country seems to be unfeasible.

In addition, this part involves cloudflare's WAF knowledge. Friends who are not familiar with it can refer to my cloudflare series of tutorials on WAF:Home Data Center Series CloudFlare Tutorial (IV) CF WAF Function Introduction and Detailed Configuration Tutorial.


If you are not limited to Free users, Cloudflare actually has many more advanced features to deal with scripts, such as:

1. Super Bot Fight Mode (Advanced Robot Protection Mode)

Basic functions: Suitable for users who do not have complex configuration needs and can distinguish between known good robots (such as Googlebot) and known malicious robots.

advantage: No CAPTCHA verification is required to determine whether the traffic is from a malicious robot; a built-in malicious robot database is used to automatically block known malicious sources.

shortcoming: Limited recognition effect on complex attacks (such as robots disguised as legitimate traffic); relatively simple configuration, no support for custom rules.

2. Bot Management

Advanced Features: Suitable for users who need fine-grained control and analysis, and can be used in conjunction with WAF rules.

Core Technology:1. Browser fingerprinting: By analyzing the request header information (such as User-Agent, Accept-Language) and the browser's behavior characteristics (such as mouse and keyboard events);2. Traffic Analysis: Combining request patterns, IP address reputation, and geographic location;3. Machine Learning Model: Identify malicious robots through real-time analysis of traffic.

advantage:Can accurately identify robot traffic disguised as normal browsers; provide detailed analysis data to facilitate administrators to optimize rules. Support customized operations, such as direct blocking, allowing, and challenging (such as JS Challenge or CAPTCHA).

shortcoming: It is a high-end feature of Cloudflare and requires Business or Enterprise plan support; configuration requires a certain technical background.

The above two technologies are more advanced in identifying and protecting against malicious robot traffic. Their mechanisms are different from traditional managed challenges and can reduce interference with user experience to a certain extent without always triggering human-machine verification (such as CAPTCHA). However, I don’t know whether they work or not. Practice makes perfect. Unfortunately, I don’t have money and can’t afford them.


Middle layer: Changting Leichi WAF

Changting Leichi WAF itself can protect against conventional attacks that launch admin-ajax.php (the direct method mentioned above), after all, there are so many protection modules:

image.png

So I don't really care about this type of attack. What I care about is the attack caused by indirect means. The characteristic of this type of attack is that it usually appears in pairs with an application that calls ajax. An example is as follows:

image.png

This is actually normal behavior, but when the number is large, it becomes an attack. Note: The attack type in the above picture is "command injection", which is not accurate. In fact, it is because the content of the article to be translated contains command codes, which causes WAF to misidentify. However, this is not important. I just want to explain what "appearing in pairs" means.

Compared with the tight quotas for various functions for Cloudflare Free users, the functions available on Changting Leichi WAF (Community Edition) are much more relaxed.

• Global frequency limit:

image.png

• CC protection (site-level frequency limitation):

image.png

image.png

• BOT protection (human-machine verification)

image.png

image.png

• Authentication

image.png

image.png

Among the above functions, identity authentication is not used here; Bot protection and cloudflare WAF's query are similar and cannot be used for admin-ajax.php; and although frequency limit can be configured globally and at the site level, the problem is that according to the normal configuration logic, it is impossible to separate the normal access to the site from the access to admin-ajax.php and TranslatePress translation function (in fact, the reason is the same as the threshold of rate limit setting on cloudflare is difficult to take into account normal access and attack traffic): if the frequency limit is set too low, it will affect the normal access of the blog, and if it is set too high, it will not be effective in protecting against such attacks.

It seems that there is no way to solve the problem of admin-ajax.php attack traffic only from the perspective of Changting Lei Chi (Community Edition), so the conventional strategy of luring the enemy deep into the territory and then encircling and annihilating them will not work.

Inner layer: Wordfence

The Wordfence plugin is a very popular security tool for WordPress, and its free version provides a range of basic protection features. Although the features are somewhat limited compared to the professional version, the free version is basically enough for personal bloggers. Here are some of the main features of the Wordfence free version and its protection against admin-ajax.php attacks:

1. Firewall protection (Web Application Firewall, WAF)

Basic rule set: The free version provides a basic set of WAF rules to detect and block common attacks such as SQL injection, cross-site scripting (XSS), abuse of admin-ajax.php, etc. For example, compared with the real-time rule updates of the premium version, the rule updates of the free version are usually delayed by 30 days.

Request rate limiting: You can configure rules to limit the access frequency of admin-ajax.php, for example: limit the number of requests from a single IP in a short period of time; set a threshold and directly deny access if it exceeds the threshold:

image.png

For admin-ajax.php attack,"If the crawler has more page views than" It is the most direct protection option because it can effectively limit frequent requests. Adjusting to an appropriate frequency limit (for example, try adjusting it to 20-50 times per minute when under attack) combined with Wordfence's IP blocking function can significantly reduce the attack surface.

2. Malware Scanning

File integrity check: Compare the contents of the website’s core files, plugins, and themes to check if they have been tampered with.

Backdoor Detection: Scans websites for backdoors, malicious scripts, or code snippets.

Known vulnerability detection: Checks installed plugins and themes for known vulnerabilities.

SEO spam scanning: Detect whether the website has been inserted with SEO spam links or hidden content.

The scan will warn you if admin-ajax.php has been tampered with or contains malicious code.

3. Login Security

Limit login attempts: Prevent brute force attacks and limit the number of IP login failures.

Block IP addresses that are attacked by brute force: Automatically block attack sources that attempt to use common usernames (such as "admin").

Login verification code (reCAPTCHA): The free version supports protecting login forms via Google reCAPTCHA.

If the attack involves login attempts via admin-ajax.php, Wordfence Free's Login Security feature can limit the number of incorrect attempts and temporarily block the associated IP.

4. IP Blocking

Manual blocking: Allows administrators to manually block suspicious IP addresses or entire IP ranges.

Basic IP blocking: Blocking based on known attacker IP addresses, but updated less frequently.

Once the attack source is identified through traffic monitoring, these IPs can be added in the "Block" function.

Through the rational use of the above functions, Wordfence can alleviate the attack traffic of admin-ajax.php to a certain extent. However, I strongly disagree with directly fighting an offensive and defensive battle in the capital: even if Wordfence can intercept all of them, it will still consume the performance of WP, not to mention that it may not be able to intercept all of them.

Theoretically the best solution

After some consideration, I think the best solution at the moment is to combine cloudflare and Changting Lei Chi's WAF, and use the high-frequency protection function of Changting Lei Chi WAF to terminate the attack on Changting Lei Chi's WAF.

However, in this case, a key problem needs to be solved: how to divert the access to wordpress and separate the normal blog access requests, the attack requests to admin-ajax.php, and the high-frequency call requests to the path "/wp-content/plugins/translatepress-multilingual/includes/trp-ajax.php". If this can be achieved, the access requests of the three can be sent to three different sites on Changting Lei Chi WAF respectively, and different CC protection thresholds can be set for these three sites respectively. In this way, the normal access to the blog will not be affected, and the attacks on admin-ajax.php and the high-frequency calls to TranslatePress can be blocked without affecting the comment function and translation function.

Cloudflare tunnel + Changting Leichi WAF creates the best solution

In a previous article about Cloudflare Zero Trust, I detailed how Cloudflare Tunnel can be used in a variety of scenarios (see article:Cloudflare tutorial series for home data centers (Part 9) Introduction to common Zero Trust functions and multi-scenario usage tutorials), there is a detail that always interests me, that is, when creating a public hostname in the tunnel, you can specify the URI path, as follows:

image.png

I have never encountered such a scenario before, so I did not delve into it. However, I wondered if the public hostname without path and the public hostname with path could coexist in the same subdomain. If so, wouldn't it solve the problem of separating the access of different URIs of the same domain? So I tried it quickly, and it really worked (the smaller the serial number, the higher the priority):

image.png

So just put the specific URI first,"*"Put it at the bottom, and you can achieve 3 access paths pointing to different sites on the intranet Changting Leichi WAF:

image.png

image.png

image.png

In this way, as long as you use different ports to create three sites on Changting Leichi WAF (I use ports 50080, 50081, and 50082) and set different high-frequency access restriction thresholds, you can limit the call frequency of admin-ajax.php and TranslatePress's translation function without affecting normal blog access traffic:

image.png

As can be seen from the above figure, the number of ajax call requests corresponding to the TranslatePress translation function is the largest, because indirect attacks are initiated through it. In the CC protection of its corresponding site, if the request quantity threshold or attack quantity threshold is exceeded within a period of time, it will be directly banned for 1 day (within 1 day, any visit from this IP will be directly returned 404):

image.png

Actual effect:
image.png

Because TranslatePress's ajax calls were blocked in a timely manner, the calls to admin-ajax.php were basically normal, and the normal access statistics of the blog were completely normal.

As of the time of writing this article, the attacks are still going on, but I don’t feel anything anymore:

image.png

Note 1: Changting Lei Chi WAF is not necessary to solve this problem, because the rate limiting function for different sites is used, and Nginx itself also supports site access rate limiting. Therefore, for the current scenario, you can use general Nginx or Nginx installed on the Baota panel or other reverse proxies that support rate limiting.

Note 2: Regarding the specific high-frequency protection threshold (mainly this), high-frequency attack threshold, and high-frequency error limit on Changting Lei Chi WAF (if there are a large number of 404s generated by scanning, you can use this), please set an appropriate value according to your actual situation.

Afterword

Finally I solved this annoying problem. It was also my fault that I didn't take it seriously at the beginning and directly questioned the issue on Cloudflare's WAF without studying the issue in depth, which led to abnormal comments and translation functions for up to 1-2 weeks.

However, through this incident, I discovered a new way to use cloudflare tunnel to divert different access paths of the blog, which makes me very happy. The most important thing is that when I encounter other scenarios that require "diversion" in the future, I can easily complete it through the method of this article, which is another practical way to deal with problems.

📌 Content Structure Hints:
This content belongs to "Cloudflare Learning MapThis is part of the document; you can view the full content path here: Cloudflare Learning Map .
Share this article
All blog content is original; please indicate the source when reprinting! The blog's RSS address is:https://blog.tangwudi.com/feed, welcome to subscribe; if necessary, you can joinTelegram GroupDiscuss the problem together.

Comments

  1. 404
    Windows Chrome 131.0.0.0
    1 year ago
    2024-12-08 19:45:49

    Although it is unethical, Wordfence only needs a few lines of code to use the advanced version. Bloggers can go to search, but they need to reinsert the code each time they upgrade.
    (It's OK for the blogger to see it himself, no need to make it public hehehehe)

    • Owner
      404
      Macintosh Chrome 131.0.0.0
      1 year ago
      2024-12-08 21:57:25

      Is it that simple? I can do it when I need it :)

Send Comment Edit Comment


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠(ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ°Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
Emoticons
Emoji
Little Dinosaur
flower!
Previous
Next