FeaturesWP CloudPricingEE CLIContactMy Account

Features

Team Access & User Permissions

Server Setup & Management

Site Creation & Management

Deployments & Developer Tools

Firewall Protection

Speed & Caching

Backups & Recovery

Logs & Monitoring

Create WP Cloud Sites

All Features

Read Documentation

Install

Installation Guide

FAQ

Roadmap

Explore EE CLI

Resources

Commands

Handbook

Blog

Community

Forum

EasyExperts

Tutorials

Subscribe

Github

CLEAR

Start For Free

separator Tutorials separator Nginx separator Block wp-login.php bruteforce attack

Topics

On this page

Nginx Settings

Global Settings

Per Site Setting

Protecting other areas

Other Changes

Test

Logs

Simulate Attack

Other Notes

Whitelisting IP addresses

Block wp-login.php bruteforce attack

Last night, a site in our managed-hosting network was under attack.

Following config thwarted the bruteforce attack successfully using Nginx’s Limit Req Module.

Nginx Settings

Global Settings

In /etc/nginx/nginx.conf file under http{..} block, add following


			limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
		

10m is size of zone. 1MB can hold 16000 states. I think this means 16000 unique IP addresses. In case you have way too many sites or very high traffic sites, you may want to increase it to 20MB or 100MB.

1r/s means 1 request per second is allowed. You cannot specify fractions. If you want to slowdown further, means less requests per second try 30r/m which means 30 requests per min, effectively 1 request per 2 second.

Per Site Setting

You can add something like below to server{..} block:


			location = /wp-login.php {
    limit_req   zone=one  burst=1 nodelay;
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9000;
}
		

OR


			location ~ \.php$ {
    location ~* wp\-login\.php {
        limit_req   zone=one  burst=1 nodelay;
        include fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;
    }
    #other rules
}
		

nodelay makes sure as soon as request limit exceeds, HTTP status code 503 (Service Unavailable) is returned.

Protecting other areas

On same lines you can protect other area as well. e.g. your contact form.

If you have contact form on every page, then may be you can add location of form’s action handler URL.

Other Changes

In case you want to return 444 (recommended) or any other code use following in http {..} block for global effect or with location {..} block for local-effect:


			limit_req_status 444;
		

You can server custom error page using error_page directive.

Test

Logs

Use following command to monitor logs. Replace 503 with any other error code if you are using limit_req_status


			tail -f /var/www/example.com/logs/*.log | egrep "login|503"
		

If your site is already under attack, you will see lines like below:


			2013/08/23 04:17:03 [error] 256554#0: *99927 limiting requests, excess: 1.852 by zone "one", client: 1.2.3.4, server: example.com, request: "GET /wp-login.php HTTP/1.0", host: "exmaple.com"
		

1.2.3.4  is blocked IP.

Simulate Attack

You can use apache-bench to attack server:


			ab -n 100 -c 10 example.com/wp-login.php
		

Other Notes

Whitelisting IP addresses

For wp-login example in this article you won’t need whitelisting as a real-human has no business to send multiple requests to wp-login.php at any moment.

Still, for some other purpose you may need to whitelist IP addresses.

In /etc/nginx/nginx.conf file under http{..} block, add a map{..} block like below:


			map $remote_addr $rt_filtered_ip {
        default $binary_remote_addr;
        1.2.3.4 "";
        4.4.4.4 "";
}
		

1.2.3.4 and 4.4.4.4 are example of whitelisted IP’s. You can have any number of them. Add every IP on separate line. Make sure you use “” in second column for every IP. rate limit module ignores empty values.

Then you need to use variable $rt_filtered_ip


			limit_req_zone $rt_filtered_ip zone=one:10m rate=1r/s;
		

Above simple method may not work if you want to whitelist a range of IP’s. For which you need to geo module.


			geo $rt_filtered_ip {
    default        $binary_remote_addr;

    127.0.0.1      "";
    192.168.1.0/24 "";
    10.1.0.0/16    "";

    ::1            "";
    2001:0db8::/32 "";

    1.2.3.4        ""
}
		

Important: We haven’t tested geo-module example. So use at your risk.

Amazon Elastic Load Balancer and Forwarding Real-IP Nginx

PREVIOUS

Configuring HTTP/2 Server Push

NEXT


Comments

  1. wcat Avatar

    wcat

    September 6, 2013

    Stimulate Attack = freudian slip
    🙂

    TY

    1. Rahul Bansal Avatar

      Rahul Bansal

      September 6, 2013

      Corrected. I hope I did not offend anybody 😉

  2. wcat Avatar

    wcat

    September 6, 2013

    also

    tail -f /var/www/example.com/*.log | egrep "login|503" 
    

    should be

    tail -f /var/www/example.com/logs/*.log | egrep "login|503"
    
    1. wcat Avatar

      wcat

      September 6, 2013

      Asterisk’s were removed….

      1. Rahul Bansal Avatar

        Rahul Bansal

        September 6, 2013

        Added back. Looks like it’s markdown plugin we are using.

        By the way, it would not have stripped asterisks if commands were surrounded by backticks (`). Just a guess!

    2. Rahul Bansal Avatar

      Rahul Bansal

      September 6, 2013

      Thanks for corrections. 🙂

  3. Guill Avatar

    Guill

    October 9, 2013

    Excellent, thanks.

  4. Chathu Avatar

    Chathu

    October 20, 2013

    After I added following code to nginx configuration file and reboot the VPS, I got this error.

    [emerg] 1577#0: “location” directive is not allowed here in /etc/nginx/nginx.conf:67

    Any solution?

    Thanks!

    1. Anti DDoS Avatar

      Anti DDoS

      October 22, 2013

      Look like you added a “location” directive inside the “http” block, while you can only add it inside the “server” block.

      1. Chathu Avatar

        Chathu

        October 26, 2013

        Thank you Anti DDoS and Rahul for your replies. I have an Apache Nginx installation (managed VPS). It uses back end Apache and Nginx for front end. When I navigate to Nginx admin>Configuration, I can’t see “Server” block, instead of that, it has http black. So in this situation how do I fix this issue?

        Thanks!

    2. Rahul Bansal Avatar

      Rahul Bansal

      October 23, 2013

      @Anti DDoS Thanks for answering.

      @Chathu Please follow above answer.

  5. Gaptek Update Avatar

    Gaptek Update

    January 1, 2014

    Just want to inform a typo with:

    geo $rt_filterd_ip {

    it should be:

    geo $rt_filtered_ip {

    1. Rahul Bansal Avatar

      Rahul Bansal

      January 1, 2014

      Thanks for catching that typo. Corrected it. 🙂

  6. Luka Kaspar Avatar

    Luka Kaspar

    February 18, 2014

    Thanks for this tip, however I can’t seem to get it working properly… Here’s what I used together with the global setting

    location ~ \.php$ { location ~* wp\-login\.php { limit_req zone=one burst=1 nodelay; try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

    include fastcgi_params; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/var/run/php5-fpm.sock; } try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

    include fastcgi_params; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/var/run/php5-fpm.sock;

    }

    It seems to have something to do with fastcgi_pass… If I set it to 127.0.0.1:9000 it seems to block, but then my website won’t load…. Any ideas?

    Cheers

    1. Luka Kaspar Avatar

      Luka Kaspar

      February 18, 2014

      Also, I just tried changing the sockets to 127.0.0.1:9000 in the http://www.conf file and then in the location and still no luck

      1. Luka Kaspar Avatar

        Luka Kaspar

        March 3, 2014

        Nevermind, seems like there was some error in my server configuration. Did a fresh install and everything works outside the box. Strange.

        Anyway, thanks for this article!

        1. Rahul Bansal Avatar

          Rahul Bansal

          March 3, 2014

          @Luka sorry for missing your earlier comment.

          Glad to know your issue is solved.

          On fresh setup, you can try https://rtcamp.com/easyengine (for wordpress-nginx sites)

          1. Luka Kaspar Avatar

            Luka Kaspar

            March 3, 2014

            Yeah I thought of using easyengine but I already did a fresh install on Debian before I noticed you added support for that distro 🙂

            Still, used a lot of tips from this website so in the end I came to the same outcome. great tutorials 😉

          2. Rahul Bansal Avatar

            Rahul Bansal

            March 7, 2014

            Glad to know that you found tutorials useful. I hope EasyEngine will make your next install “easy”. 🙂

Features

Useful Links

Product

Other Products

Subscribe for curated product updates, tips, and announcements.

"*" indicates required fields

Comments

This field is for validation purposes and should be left unchanged.

Email*

SIGN ME UP

Δ


© rtCamp, since 2009. All rights reserved.

Powered by