Nginx Hardening for Security Implications

NGINX

The web server has a crucial role in web-based applications. Since most of us leave it to the default configuration, it can leak sensitive data regarding the web server.

By applying numerous configuration tweaks, we can make NGINX more resilient against malicious attacks. Following are some NGINX hardening tips that you can incorporate to improve security.

1. Hide NGINX Version

Hiding the NGINX version number can prevent attackers from using known vulnerabilities specific to your NGINX version. Exposing the Nginx version can provide attackers with useful information for targeting known vulnerabilities. To hide the version:

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

server_tokens off;

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The server_tokens off; directive disables the display of the NGINX version in HTTP headers and error pages.

2. Disable Unnecessary HTTP Methods

Limiting HTTP methods helps to prevent attacks that exploit certain HTTP methods.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

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

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

This configuration will return a 444 No Response status code for requests using HTTP methods other than GET, POST, and HEAD, which helps mitigate risks..

3. Disable Directory Listing

Directory listing should be disabled to prevent exposure of sensitive files and directory contents.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

location / {
    autoindex off;
}

For Faveo Helpdesk, we can skip this step as we have already enabled it in default configuration file as below:

location / {
            try_files $uri $uri/ /index.php?$query_string;
        }

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The autoindex off; directive disables directory listing.

4. Clickjacking Defense with X-Frame-Options

To prevent your content from being used in a frame, which could be exploited for clickjacking attacks:

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

add_header X-Frame-Options "SAMEORIGIN";

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header X-Frame-Options "SAMEORIGIN"; directive ensures that your content can only be framed by pages from the same origin.

5. Basic XSS Protection

Enabling XSS protection in modern browsers helps prevent cross-site scripting attacks.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

add_header X-XSS-Protection "1; mode=block";

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header X-XSS-Protection "1; mode=block"; directive enables the XSS filter built into modern browsers.

6. Enable HttpOnly and Secure Flags for Cookies

Setting HttpOnly and Secure flags for cookies helps protect them from theft via client-side scripts and ensures they are only sent over HTTPS.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

http {
    ...
    add_header Set-Cookie "HttpOnly;Secure";
    ...
}

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header Set-Cookie "HttpOnly;Secure"; directive ensures that cookies are marked as HttpOnly and Secure.

7. Disable ETag

Disabling ETags helps prevent leaking information about the server. Disabling them can enhance privacy.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

server {
    ...
    add_header ETag "";
    ...
}

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header ETag ""; directive disables the generation of ETag headers.

8. Enable HTTP/2 Protocol

Enabling HTTP/2 can improve performance and security over HTTP/1.1..

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

listen 443 ssl http2;

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The listen 443 ssl http2; directive enables HTTP/2 for improved performance.

9. Set Referrer Policy

A referrer policy can prevent sensitive information from being leaked in HTTP referrer headers. Setting a strict referrer policy enhances privacy and security.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

add_header Referrer-Policy "strict-origin";

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header Referrer-Policy "strict-origin"; directive sets a referrer policy that restricts referrer information sent with requests.

10. Set Permissions Policy

Implementing a permissions policy can enhance the security of your site by controlling the use of certain features. Permissions policies (formerly known as Feature Policy) control which features and APIs can be used in your web application.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header Permissions-Policy "geolocation=(), microphone=(), camera=()"; directive restricts access to certain features.

11. Enforce HSTS-HTTP Strict Transport Security

Enforcing HSTS ensures that browsers only connect to your server over HTTPS.

Edit the NGINX configuration file:

  • RHEL Based Servers
    sudo nano /etc/nginx/nginx.conf
    
  • Debian Based Servers
    sudo nano /etc/nginx/sites-available/faveo.conf
    

Find the http block and add the following line:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

The add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; directive enforces HSTS for one year and includes all subdomains.

Nginx Virtual Host Configuration for Security Hardening

Here is the combined configuration for Debian-based systems, summarized from all 11 points, to be placed in the virtual host configuration files. Here we are directly making changing in a single virtual host file for a particular website.

Enabling and Applying Configuration:

Edit the NGINX configuration file:

sudo nano /etc/nginx/sites-available/faveo.conf

Edit or create the site-specific configuration file:

server {
    root /var/www/faveo/public;
    index index.php index.html index.htm;
    server_name helpdesk.example.com;

    client_max_body_size 100M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;       

#1. Hide NGINX Version
        server_tokens off;
#2. Disable Unnecessary HTTP Methods
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
    return 444;
}


# Security Headers
#4. Clickjacking Defense with X-Frame-Options
add_header X-Frame-Options "SAMEORIGIN";
#5. Basic XSS Protection
add_header X-XSS-Protection "1; mode=block";
#6. Enable HttpOnly and Secure Flags for Cookies
add_header Set-Cookie "HttpOnly;Secure";
#7. Disable ETag 
#add_header ETag "";
etag off;
#9. Set Referrer Policy
add_header Referrer-Policy "strict-origin";
#10. Set Permissions Policy
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";
#11. Enforce HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

#8. Enable HTTP/2 Protocol
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/helpdesk.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/helpdesk.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = helpdesk.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;
    server_name helpdesk.example.com;
    return 404; # managed by Certbot


}

Please make sure that the server name/domain is changed at all locations and ssl paths are correct.

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

Nginx Virtual Host Configuration for Security Hardening

Here is the combined configuration for Debian-based systems, summarized from all 11 points, to be placed in the virtual host configuration files. Here we are directly making changing in a single virtual host file for a particular website.

Enabling and Applying Configuration:

Edit the NGINX configuration file:

sudo nano /etc/nginx/nginx.conf

Edit or create the site-specific configuration file:


user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

#Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
#6. Enable HttpOnly and Secure Flags for Cookies
    add_header Set-Cookie "HttpOnly;Secure";

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        server_name  helpdesk.example.com;
        root         /var/www/faveo/public/;
        index index.php index.html index.htm;

#1. Hide NGINX Version
        server_tokens off; # Disable server tokens for security
#2. Disable Unnecessary HTTP Methods
	if ($request_method !~ ^(GET|POST|HEAD)$ ) {
	    return 444;
	}


        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        # This is for user friendly URL 
        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

        location / {
            try_files $uri $uri/ /index.php?$query_string;
#3. Disable Directory listing
	    autoindex off;
        }

# Security Headers
#4. Clickjacking Defense with X-Frame-Options
add_header X-Frame-Options "SAMEORIGIN";
#5. Basic XSS Protection
add_header X-XSS-Protection "1; mode=block";
#7. Disable ETag 
#add_header ETag "";
etag off;
#9. Set Referrer Policy
add_header Referrer-Policy "strict-origin";
#10. Set Permissions Policy
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";
#11. Enforce HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";



        location ~* \.html$ {
            expires -1;
        }

        location ~* \.(css|gif|jpe?g|png)$ {
            expires 1M;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        }

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    
#8. Enable HTTP/2 Protocol
    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/helpdesk.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/helpdesk.example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

    gzip on;
    gzip_http_version 1.1;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_types application/atom+xml
               application/javascript
               application/json
               application/vnd.ms-fontobject
               application/x-font-ttf
               application/x-web-app-manifest+json
               application/xhtml+xml
               application/xml
               font/opentype
               image/svg+xml
               image/x-icon
               text/css
               text/plain
               text/xml;
    gzip_buffers 16 8k;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";



    server {
    if ($host = helpdesk.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        server_name  helpdesk.example.com;
    listen 80;
    return 404; # managed by Certbot


}}

Please make sure that the server name/domain is changed at all locations and ssl paths are correct.

Save and exit the file.

ctrl + x

Restart NGINX:

sudo systemctl restart nginx

Updated: