Improving WordPress Security with Cloudflare Teams/Zero-Trust/Access
This very page that you are reading is one of the 40% of all pages that are powered by WordPress. It’s an incredibly powerful and flexible open source application, used by millions of sites to serve content to their users. I love it for it’s simplicity and customisability with thousands of useful plugins. I am not a web developer, nor do I have the slightest ability to make something look pretty – so I will leave that for somebody else to do. WordPress is however a victim of it’s own success. It’s popularity makes it a prime target for hackers trying to find the next exploit against the platform, knowing it can likely be used on millions of sites. WordPress is also very consistent out of the box, using very well known paths for the different functions e.g wp-admin.php and wp-login.php. This also makes WordPress instances very easy to launch brute-force attacks against.
Looking at the server logs, I can see hundreds of requests a day to /wp-admin.php – many coming from IP addresses already associated with spam and brute force attacks.
While I can implement a number of tools on the server and NGINX proxy – it seems silly to even let this traffic pass to the host itself – instead we can block it at the CDN level using Cloudflare Zero Trust.
The Architecture
All traffic to my public-facing infrastructure is already routed via Cloudflare’s proxy service and therefore can be intercepted and blocked by their Zero Trust service – forcing authentication to occur prior to allowing the traffic to pass to the host. Using this technology we can block access to entire subdomains or specific routes without authentication. In my case, authentication takes place using ayloNet SSO which uses a combination of Azure AD (Primary) and Google Cloud (Secondary) as the golden source(s) of identity data.
The Architecture can be seen below:
We can see that if traffic is being directed to /wp-admin, Cloudflare will automatically route the traffic via Cloudflare Zero Trust and authentication will be required via the relevant IDPs. If traffic is not directed to /wp-admin then the traffic is allowed to continue through Cloudflare’s standard proxy service to the host. Once at the host, it follows our typical architecture for web-apps (through a dedicated NGINX and DB network) to enforce zero-trust between the various high-risk solution components.
This same pattern is used for any other high-risk WordPress routes wp-admin is purely an example of one such case.
The Setup
The setup is really simple given that we already use Cloudflare’s proxy service. . I will cover the setup of Cloudflare Zero Trust in a separate article some time in the future. Therefore the only additional configuration was on the Zero-Trust side of things. This involves:
- Creating an access group in the IDPs for approved users.
- Creating a new “Application” in Cloudflare Zero Trust for each controlled route.
- Mapping the access groups to the relevant include/approve policies in Cloudflare.
Maintaining SSO for User Login
While users will be required to authenticate when accessing the admin console URL, we also need to authenticate them against WordPress itself to ensure that they are logged in using their SSO credentials. While Cloudflare supports a number of ways to do this, the approach I have taken is to have WordPress independently verify credentials with Azure AD via OAuth. The major reason for this is the additional attributes that WordPress can harvest from the OAuth claim via Microsoft Graph.
This is why WordPress has a direct HTTPS connection to Azure AD in the architecture above.
The User Experience
The user experience is pretty good, particularly as most of our internal users will already have a Cloudflare access token (from using other apps) and therefore will have a fully transparent journey though to WordPress. I’ve outlined the three journeys below:
Guest (No Auth Required) | Admin (Existing Token) | Admin (No Token) |
---|---|---|
Access directly provided to guest pages e.g. https://furber.tech | 1. User accesses /wp-admin 2. User presented at the login screen 3. User hits "Login with SSO" button 4. User Authenticated | 1. User accesses /wp-admin 2. User presented at Cloudflare ZT Login Screen 3. User selects an IDP and authenticates 4. User presented at the WordPress login screen. 5. User hits "Login with SSO" 6. User Authenticated |
We can see just how simple the journey is from the end user perspective by looking at Fig 5 and 6. For most users, this is all they will see before they are logged in with the applications. Two simple button presses and they are in with their standard SSO credentials.