4 Feb 2020

How to Secure, Harden and Improve Performance of Nginx Web Server

#1: Keep Nginx up to date

#2: Remove unnecessary modules in Nginx

To explicitly remove modules from Nginx while installing from source:

# ./configure --without-module1 --without-module2 --without-module3

Example:

# ./configure --without-http_dav_module --without-http_spdy_module

Removing modules from a previous Nginx installation requires recompilation.

Caution: configuration directives come from modules — verify you’re not disabling a module containing directives you still need. Check the Nginx documentation before disabling modules.

#3: Disable the server_tokens directive

The server_tokens directive displays the Nginx version on error pages. Hide this to prevent attacks that target known vulnerabilities in a specific version.

server {
    listen       192.168.0.25:80;
    server_tokens        off;
    server_name  example.com www.example.com;
    access_log  /var/www/logs/example.access.log;
    error_log  /var/www/logs/example.error.log error;
    root   /var/www/example.com/public_html;
    index  index.html index.htm;
}

#4: Deny bad HTTP user agents

Malware bots and crawlers waste resources. Create /etc/nginx/blockuseragents.rules:

map $http_user_agent $blockedagent {
        default         0;
        ~*malicious     1;
        ~*bot           1;
        ~*backdoor      1;
        ~*crawler       1;
        ~*bandit        1;
}

Include it before the server block, then return 403 for blocked agents:

include /etc/nginx/blockuseragents.rules;
server {
    listen       192.168.0.25:80;
    server_tokens        off;
    server_name  example.com www.example.com;
    access_log  /var/www/logs/example.access.log;
    error_log  /var/www/logs/example.error.log error;
    root   /var/www/example.com/public_html;
    index  index.html index.htm;
}

Test with:

# wget http://192.168.0.25/index.html
# wget --user-agent "I am a bandit haha" http://192.168.0.25/index.html

#5: Disable unwanted HTTP methods

Allow only GET, POST, and HEAD; return 444 for everything else (which fools malware into thinking there’s nothing there):

if ($request_method !~ ^(GET|HEAD|POST)$) {
   return 444;
}

Test with:

# curl -X DELETE http://192.168.0.25/index.html
# curl -X POST http://192.168.0.25/index.html

#6: Set buffer size limitations

Prevent buffer overflow attacks. Create /etc/nginx/conf.d/buffer.conf:

client_body_buffer_size  1k;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;

And include it:

include /etc/nginx/conf.d/*.conf;

#7: Limit connections by IP

limit_conn_zone $binary_remote_addr zone=addr:5m;
limit_conn addr 1;

#8: Set up monitoring for logs

#9: Prevent image hotlinking

location /img/ {
  valid_referers none blocked 192.168.0.25;
   if ($invalid_referer) {
     return   403;
   }
}

#10: Disable SSL, only enable TLS

ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

#11: Create certificates

# openssl genrsa -aes256 -out example.key 1024
# openssl req -new -key example.key -out example.csr
# cp example.key example.key.org
# openssl rsa -in example.key.org -out example.key
# openssl x509 -req -days 365 -in example.csr -signkey example.key -out example.crt

Add a separate server block for SSL:

server {
    listen 192.168.0.25:443 ssl;
    server_tokens off;
    server_name  example.com www.example.com;
    root   /var/www/example.com/public_html;
    ssl_certificate /etc/nginx/sites-enabled/certs/example.crt;
    ssl_certificate_key /etc/nginx/sites-enabled/certs/example.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
}

#12: Redirect HTTP traffic to HTTPS

Add this to your port-80 server block:

return 301 https://$server_name$request_uri;

This returns a 301 (Moved Permanently) response, redirecting requests on port 80 to the HTTPS server block.