Defending against WordPress brute-force attacks using built in features of IIS

Throughout the past week, a very large network of compromised computers has been pounding away at sites using the WordPress content management platform, attempting to access those sites by continually attempting logins using default WordPress usernames, along with a combination of passwords.

For these attacks to work, the attacking machines have to continually try various combinations of passwords, throwing request after request at your system, until they find a combination that works.

One of the best ways to defend against such an attack is to make sure that you’re not using default usernames.  Even if you’re doing that, though, it doesn’t prevent the attackers from throwing posts at your WordPress points of entry.  This forces WordPress to have to process these attempted logins before determining that the username is invalid.

While there’s no major security vulnerability, when attackers are throwing post after post at your installation, it can hamper performance dramatically.

There’s plenty of advice online for people who’ve installed WordPress using an Apache web server, but not as much for people using IIS. Fortunately, IIS has some features that can help mitigate the problem.

Dynamic IP Restrictions

A great first line of defense is available by installing the Dynamic IP Restrictions extension (link).

Once installed, you’ll can configure your site to deny requests from any IP address that is sending too many requests to your server too quickly, based on the criteria you define in the configuration.























Inbound Rewrite Rule

Another great idea is placing a rewrite rule on your wp-login.php file that checks for certain conditions prior to processing a user login.  When a bot attempts to login to your site using an automated routine, you will usually see a post of attempted credentials directly to your wp-login.php file.  This allows for rapid automation of multiple login attempts.

This looks different than someone actually visiting your WordPress admin, and clicking the “Login” button after supplying credentials.  Specifically, when a post is coming directly from a bot, the header sent along with the post will be usually be missing the HTTP_REFERER header.  Knowing this can help you construct a rewrite rule that will prevent these attempted logins from being processed.

In Features view, click on the “URL Rewrite” icon, and click “Add Rule” in the Actions pane on the right.

Give your rule a name, and in the Requested URL drop down, choose “Matches the Pattern”. Choose “Wildcard” under “Using”.

In the pattern box, enter *wp-login.php*.  This pattern allows you to also catch any redirects caused by someone visiting a site at the /wp-admin/ URL.

Under “Conditions” choose “Match All”, and add the following two conditions:

1. {HTTP_REFERER} Does Not Match the Pattern *wp-login.php*

2. {REQUEST_METHOD} Matches the Pattern POST

Under “Action”, choose “Abort Request”

This rule should catch any automated attempt to post directly to wp-login.php, and stop the request before WordPress actually attempts to process it, allowing for improved performance.


Working on the redesign of a college website

November 2011 – The desire for a site redesign is first mentioned, you knowingly sigh a deep sigh to yourself.

February 2012 – An administrative committee is formed to lay out the main categorizations and links for the new site.  You shake your head at the first inkling that there are to be changes to cutesy, vague category names like “Discover”.  “Global” is also included, which still makes you chuckle.

May 2012 – A wireframe design is complete, and example pages are shopped around campus.

June 2012 – After three campus demos, and sign-off from the various stakeholders, you put together a project plan to execute on the redesign.

July 2012 – The “categories” for the links are finalized, and guidance is given in what to include in each category.  You provide feedback that we need to be really certain these are the categories we want to go with, because you’re also rolling these menu items out to 150+ sites using the theme, so it’s not simple to go back and change link names later.

November 2012 – The plans to implement the redesign are finalized

December 2012 – The trigger is pulled over holiday break

January 2013

  • The first call comes in to change a category back to the way it was before.
  • You get the first  “I can’t find the calendar” call. You provide the three places that the calendar is directly linked from, along with the guidance that if you type “calendar” in the search box and click go it is the first result.
  • You get the first call from someone angry that their link is “missing”.
  • You get the first angry tweet from a student that things look different now.

January 2013, week two

  • The second call to change a category back to the way it was before the redesign
  • The first call from someone upset that they are no longer featured on the front page of the website
  • You get an email from someone who was in the group who created the new menu items, asking you why you chose the menu items you did.

January 2013, week three

  • The student newspaper asks you “why do you think the redesign has been criticized so much?”
  • This is also the first time you field a call from someone who is angry that you change something back for someone who was angry that it changed in the first place

January 2013, week four

  • The third call comes in to change a category back to the way it was before the redesign, you realize now that you’re 75% back to the menus and navigation as they were before the process began.  You put head on desk and close your eyes.
  • You touch up your LinkedIn Profile



Mobile Traffic to the Marshall Website for 2012

iOS-VS-AndroidThis morning I ran a report for a coworker on the mobile device traffic to the Marshall website over the past year (through yesterday).  The statistics break down like this:

72.92% iOS
22.38% Android
1.02% Blackberry
0.48% Windows Mobile

A couple of points of information to use when interpreting the data:  iOS includes all iPhone, iPad and iPod traffic.  While the percentage of Android devices is a lower percentage wise than you might see on the broader web, we’ve seen a significant increase in traffic from Android devices year over year, and each year I run the stats I see Android continue to chip away at iOS.  iOS used to hold an 85%+ advantage over others in terms of total traffic.

Blackberry continues its death spiral, down from a high of about 4.5% in 2008.


Not understanding the problem

There’s a cute little site here where a graphic designer rants a bit about the American Airlines website, and how if he had the opportunity to design it, he’d do it so much better.   That’s not the interesting part – people on the internet waiting to tell you how much better that they’d do things if they had your job are a dime a dozen.  I’ve had a least four different (highly detailed) emails from people since I’ve been at Marshall telling me why the work I did sucked, and how I should have done a, b or c if I had any clue about how things should work/look/behave.  Just in the past three months I’ve had two GAs who were hired to work in departments on campus deliver me detailed PDFs about everything that is wrong with a new design that hasn’t even launched yet.   You learn to let that stuff go or you quit coming in every day.

What is interesting about this site though, is that one of the UX Designers who worked on actually took the time to reach out to the guy (even after the guy had essentially called on AA to “fire your entire graphic design team”), and gave him a reasoned, well thought out run down of how the real world works when you put on your big boy pants.

The best part comes in this paragraph:

But—and I guess here’s the thing I most wanted to get across—simply doing a home page redesign is a piece of cake. You want a redesign? I’ve got six of them in my archives. It only takes a few hours to put together a really good-looking one, as you demonstrated in your post. But doing the design isn’t the hard part, and I think that’s what a lot of outsiders don’t really get, probably because many of them actually do belong to small, just-get-it-done organizations. But those of us who work in enterprise-level situations realize the momentum even a simple redesign must overcome, and not many, I’ll bet, are jumping on this same bandwagon. They know what it’s like.

And that, well, that really sums it up.   Doing site mockups, writing applications, developing new services, etc. – none of that is the hardest part of the job.  The hardest part of the job is navigating the competing needs of multiple areas of a large organization, each with their own wants, needs and agendas.   It’s understanding that while you may not agree, those wants, needs and agendas are still real and have to be considered that is part of the professional growth cycle that everyone eventually has to go through if they’re going to move from spec design or development in the spare bedroom of their parents house to working in a living, breathing environment.


Configuring WordPress with FORCE_SSL_ADMIN with shared SSL and hardware load balancing

While updating our WordPress configuration to deal with a semi-related problem, I wanted to move from the configuration we’d be using which required all administrative logins to WordPress to use SSL encryption to a more secure model that required the entire administrative session be encrypted.

These two choices are controlled via settings in the wp-config.php file.  They’re detailed here at if you’re looking for more information.

After modifying the wp-config.php by setting the FORCE_SSL_ADMIN instruction to “TRUE”, any attempt to access the WordPress administrator would result in a “Too many redirects” error, getting the administrative interface stuck in an infinite redirect loop as a result of the way the is_ssl() function is processed in the wp-includes/functions.php file and some “out of the box” incompatibilities with SSL certificates being provided as shared wild card certificates from a hardware load balancer.

Fortunately, WordPress includes information on how to resolve this issue in their “Administration Over SSL” article.  The relevant portion is included in this bit of information:

Using a Reverse Proxy

If WordPress is hosted behind a reverse proxy that provides SSL, but is hosted itself without SSL, these options will initially send any requests into an infinite redirect loop. To avoid this, you may configure WordPress to recognize the HTTP_X_FORWARDED_PROTO header (assuming you have properly configured the reverse proxy to set that header).


define('FORCE_SSL_ADMIN', true);
define('FORCE_SSL_LOGIN', true);
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')

What this bit of code is doing is first instructing WordPress to require that all interaction with the admin be done over SSL.  Then it’s expecting the hardware load balancer to pass an HTTP_X_FORWARDED_PROTO variable letting WordPress know that the request came in to the the load balancer as an HTTPS request.  From their, the local server variable that WordPress uses in its is_ssl() function can be set to match the variable provided by the load balancer so that the function will work properly and the infinite redirect loop can be avoided.

Unfortunately, this configuration did not work for us initially.   After a bit of troubleshooting, it became clear that our load balancer wasn’t passing an HTTP_X_FORWARDED_PROTO variable at all – which presents a bit of a problem if you’re attempting to use it later to set the value of another variable.

The missing piece of the puzzle was working with our great Systems guys to get an iRule added at the load balancer that would insert an appropriate value for HTTPS requests, and then pass that variable along to the web server so it could be used in the wp-config.php code.

The iRule itself looks like this:

HTTP::header insert X-Forwarded-Proto:“https”

More information on this iRule, including the context of the thread the final solution was found in is available at this link.

UPDATED: To make sure that an X-Forwarded-Proto was inserted correctly whether the request was HTTP or HTTPS, another iRule was added just like the first (leaving the s off of the ‘http’ portion).  Each iRule was then added to its respective Virtual Server Resources tab.

To accomplish X-Forward-Proto using iRules you must use 2 separate iRules if implementation is required for both http and https.  The rules are written as follows:


HTTP::header insert X-Forwarded-Proto http



HTTP::header insert X-Forwarded-Proto https


The first iRule (http) will be used on the Http Virtual Server within the F5.  The second, on the https Virtual Server.

This same thing can be accomplished without using iRules (which is preferred).  You do so by modifying the Virtual Server’s http & Virtual Server’s https profile.  On the http profile go to the “Request Header Insert” section and fill in the blank with “X-Forwarded-Proto:http”…do the same thing on the https profile filling in the blank with “X-Forwarded-Proto:https”.