Let’s imagine we’ve just launched our new e-commerce store, dogtreats.com. We’ve noticed that we’re already getting a lot of requests from locations all around the world, including some that can’t even buy the product. Even worse, there are a lot of failed login requests, which could indicate an attacker trying to brute-force access to accounts.
In this scenario, dogtreats.com only ships products to the EU, so we’ve made the decision that we can block requests from any other country, but how can we do that?
Use a cloud-managed service
As we’re concerned about security, we should offload as much of the blocking as possible onto a third party or managed service. This is important as unless you’re an expert in responding to DDoS attacks, handling it yourself could result in very high costs and user-impact.
Ideally, we’ll block requests using the first resource which requests will hit. If we allow potentially malicious requests to pass through multiple resources, or even hit our application servers, our service will be far more vulnerable to attacks, such as Denial-of-Service.
The first resource is usually a CDN, so let’s look at how we can block requests using 4 leading CDNs - CloudFront, CloudFlare, Fastly, and Akamai. All major CDN providers should have some kind of in-built geo blocking functionality – if yours is missing it then it’d be worth considering an alternative.
AWS CloudFront
With CloudFront, there’s a geo restriction feature which can be used to whitelist or blacklist certain countries. It’s quite basic, only allowing us to block or allow a list of country codes.
CloudFlare
CloudFlare has an advanced Firewall feature built-in. It’s able to block or allow requests based on multiple different parameters, such as request path, request method, IP address, IP location, and many more.
Fastly allows us to restrict access using Varnish configuration. It provides a large number of geolocation variables, allowing more than just blocking based on country code. To block all non-EU requests, it’s just a few lines of code:
if ( geoip.continent_code != "EU" ) {
error 403 "Forbidden";
}
Akamai offers a similar solution to CloudFront, named Geo Protection, which also supports regional blocking and US Designated Market Areas.
Web Application Firewalls
A Web Application Firewall (WAF) monitors and blocks web traffic according to rules. CloudFlare’s Firewall is actually a fully-featured Web Application Firewall (WAF), and most hosting providers offer WAF functionality as a core part of their security features. A common use of WAF is to automatically block many common web exploits, such as XSS or SQLi, so it’s often useful regardless of any geo restrictions.
Most WAF solutions can be used with more than just CDNs – for example, AWS’ WAF can be attached to CloudFront distributions, Application Load Balancers and API Gateways – making them far more versatile than the in-built CDN geo blocking. However, they usually cost more than the in-built solutions and they’re more complex to set up.
To set up AWS WAF, first head to the “WAF & Shield” console, then click “Create web ACL”. You’ll then see a wizard which allows you to create a web ACL, associate it with your resources and configure rules.
If you’re using Google Cloud, Google Cloud Shield now supports WAF with geo blocking support. For Azure users, Azure WAF has geomatch rules, currently available in preview. But what can we do if we aren’t using a major cloud provider?
NGINX
NGINX is a very popular load balancer with both a free and a premium (named Plus) offering. Unfortunately, only NGINX Plus supports the GeoIP module to allow geolocation-based blocking. It’s reasonably complex to set up, but the NGINX docs have a great guide.
ModSecurity
ModSecurity is an open-source Web Application Firewall. It was originally a module for use with the Apache HTTP server, but it can now be integrated with NGINX, IIS, and many other solutions. It’s often available when using cPanel’s WebHost Manager (WHM) so, for some services, it’s the only feasible method of blocking traffic. There are two major versions of ModSecurity.
v2 entered support-mode and hasn’t been released since 2018. It only supports geolocation blocking using the MaxMind legacy databases. GeoLite Legacy has been discontinued and is not available for download, and GeoIP legacy is no longer available to new customers. Unfortunately, v2 is still commonly bundled with WHM, rendering it effectively useless for geo blocking.
v3 now supports the GeoIP2 and GeoLite2 databases, allowing us to write a simple rule to block all traffic which doesn’t originate from the EU:
SecGeoLookupDb /path/to/geoip/geolite2-country.mmdb
SecRule REMOTE_ADDR "@geoLookup" "phase:1,t:none,pass,nolog"
SecRule GEO:COUNTRY_CONTINENT "!@streq EU" "phase:1,t:none,log,deny,msg:'Blocking non-EU IP address'"
Application
If security isn’t the main reason for blocking traffic to your site, or you just want to provide additional location-based functionality, you may wish to do a location lookup within your application code. This could be done using the GeoIP databases mentioned above or a managed service, like ipdata. An Express middleware to only allow EU traffic would look as follows:
app.use(async (req, res, next) => {
const response = await fetch('https://api.ipdata.co?api-key=test');
const ipdata = await response.json();
if (ipdata.continent_code !== 'EU') {
res.status(403).send('Only users from the EU are allowed');
return;
}
// set ipdata on the request object so we can automatically tailor
// user preferences such as currency
req.ipdata = ipdata;
next()
})
Conclusions
If you’re using a popular CDN or cloud provider, one of their in-built solutions will allow blocking website visitors from specific countries. Choosing one of these in-built solutions will be far safer than using a self-hosted or self-managed option, but we’ve also covered geo blocking for NGINX and ModSecurity, as well as within our application code itself. Having geographic information within your application can help to provide advanced features, such as automatically selecting the correct location or for calculating a delivery estimate.