Wisdom
  • Welcome
  • core
    • Flyway
    • Bean Validation
    • Lombok
    • Webclient
      • Generic Webclient Api
      • SSL Certificate
    • Application Event Publisher
    • REST API's Design
      • Http Methods and Status Codes
      • Resource Naming and URL Structure
      • Request / Response Design
      • Versioning Strategies
      • Filtering and Searching In API
    • Spring Boot Mail Integration
      • Sendgrid
    • Template Engines
      • Java Template Engine [JTE]
  • security
    • Complete Guide to URL Matchers in Spring Security: Types, Examples, Pros, Cons, and Best Use Cases
    • Passwordless Login With Spring Security 6
      • Spring Security OneTimeToken
        • One Time Token with default configuration
        • One Time Token with custom configuration
        • One Time Token With Jwt
  • others
    • How to Integrate WhatsApp for Sending Messages in Your Application
  • java
    • Interview Questions
      • Constructor
      • Serialization
      • Abstract Class
    • GSON
      • Type Token
      • Joda Datetime Custom Serializer and Deserializer
  • Nginx
    • Core Concepts and Basics
    • Deep Dive on NGINX Configuration Blocks
    • Deep Dive on NGINX Directives
    • Deep Dive into Nginx Variables
    • Nginx as a Reverse Proxy and Load Balancer
    • Security Hardening in NGINX
    • Performance Optimization & Tuning in NGINX
    • Dynamic DNS Resolution in Nginx
    • Advanced Configuration & Use Cases in NGINX
    • Streaming & Media Delivery in NGINX
    • Final Configuration
  • Angular
    • How to Open a PDF or an Image in Angular Without Dependencies
    • Displaying Colored Logs with Search Highlighting in Angular 6
    • Implementing Auto-Suggestion in Input Field in Angular Template-Driven Forms
    • Creating an Angular Project Using npx Without Installing It Globally
    • Skip SCSS and Test Files in Angular with ng generate
  • Javascript
    • When JavaScript's Set Falls Short for Ensuring Uniqueness in Arrays of Objects
    • Demonstrating a Function to Get the Last N Months in JavaScript
    • How to Convert Numbers to Words in the Indian Numbering System Using JavaScript
    • Sorting Based on Multiple Criteria
  • TYPESCRIPT
    • Using Omit in TypeScript
Powered by GitBook
On this page
  • 1. Base Configuration & Global Security Settings
  • 2. SSL/TLS Setup & HTTP to HTTPS Redirection
  • 3. Security Headers & Content Security Policy (CSP)
  • 4. DDoS Protection & Rate Limiting
  • 5. Access Restrictions by IP, GeoIP, and User-Agent
  • 6. Blocking Sensitive Directories and Files
  • 7. Blocking Bots & Bad Traffic with ModSecurity
  • 8. Proxying to Backend & Handling Subdomains
  • 9. Mitigating OWASP Top 10 Vulnerabilities
  • 10. Final Consolidated Configuration
  1. Nginx

Security Hardening in NGINX

1. Base Configuration & Global Security Settings

Start by tuning global settings for performance and security. That are-

  • Disable server tokens to prevent attackers from learning details about your NGINX version.

  • Resolver for DNS lookups.

  • Define a mapping to generate a CSP nonce based on the request ID.

  • A second map filters out suspicious User-Agent strings to block common malicious bots.

nginx.conf (Global Part):

# Global settings
worker_processes auto;
events {
    worker_connections 1024;
    multi_accept on;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    client_max_body_size 20M;

    # Use a public DNS resolver for upstream lookups
    resolver 8.8.8.8 valid=10s;

    # Disable NGINX version info in error pages and headers
    server_tokens off;

    keepalive_timeout 65;  # Keeps connections open for reuse up to 65 seconds
    sendfile on;           # Enable zero-copy file transmission
    tcp_nodelay on;        # Disable Nagle's algorithm to reduce latency
    tcp_nopush on;         # Optimize packet transmission (commonly used with sendfile)

    gzip on;
    gzip_comp_level 5;            # Compression level (1-9)
    gzip_min_length 256;          # Only compress responses larger than 256 bytes
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss;
    gzip_vary on;

    # Map to generate a nonce for CSP headers based on $request_id
    map $request_id $csp_nonce {
        "~*" $request_id;
    }

    # Map to match suspicious or malicious User-Agent strings
    # use this carefully as it will block crawlers also
    map $http_user_agent $bad_user_agent {
        default 0;
        ~*^$ 1;               # Empty User Agent
        ~*bot 1;              # Bots (use caution, some legitimate bots may be blocked)
        ~*spider 1;
        ~*crawl 1;
        ~*[<>]script 1;       # Potential XSS attempts
        ~*(nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) 1;
    }

    # Include additional project configuration
    include app.conf;
}

Explanation:

  • worker_processes/worker_connections: Set up to efficiently handle concurrent connections.

  • server_tokens off: Prevents disclosing NGINX version details.

  • Resolver: Ensures proper DNS lookups.

  • Maps ($csp_nonce, $bad_user_agent): Dynamically generate CSP nonces and block obvious bad bots, mitigating issues like injection (OWASP Injection) and reconnaissance (vulnerable components).


2. SSL/TLS Setup & HTTP to HTTPS Redirection

Securing connections with SSL/TLS is essential. We can use Let’s Encrypt (via Certbot) in production. For internal services or testing, self‑signed certificates may suffice.

A. Using Let’s Encrypt with Certbot

For “example.com” and subdomains (e.g., “app.example.com”), configure an HTTP server block to redirect all traffic to HTTPS, then configure an HTTPS server block.

HTTP to HTTPS Redirect:

server {
    listen 80;
    server_name example.com *.example.com;
    # Permanent redirect to force HTTPS
    return 301 https://$host$request_uri;
}

HTTPS Server Block (SSL/TLS with Let’s Encrypt):

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # SSL Certificates provided by Let’s Encrypt
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Enforce strong TLS protocols and ciphers
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers   HIGH:!aNULL:!MD5;

    # HSTS: instruct browsers to use HTTPS exclusively for one year, The HSTS header tells browsers to access your site securely, mitigating man-in-the-middle attacks and protocol downgrade attempts.
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # Additional security headers can be added here (see Section 4)

    location / {
        # Example content or proxy settings (see later sections)
        proxy_pass http://backend.com:8080;
    }
}

Explanation:

  • HTTP to HTTPS redirection: Ensures all connections are encrypted, mitigating data eavesdropping (OWASP Cryptographic Failures).

  • SSL protocols and HSTS: Force modern TLS standards and protect against downgrade attacks.

B. Self-Signed Certificates

For internal or development environments, self-signed certificates can be used.

server {
    listen 443 ssl;
    server_name internal.example.com;

    ssl_certificate /path/to/selfsigned.crt;
    ssl_certificate_key /path/to/selfsigned.key;

    ssl_protocols TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;

   # HSTS header: enforce HTTPS for 1 year, include subdomains, and allow preload
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location / {
        # Internal content settings
    }
}

Why? While not trusted by browsers, self-signed certificates provide encryption for testing or internal communications.


3. Security Headers & Content Security Policy (CSP)

Security headers help prevent many common attacks. We set up several headers such as X‑Frame‑Options, X‑XSS‑Protection, and a detailed CSP header that also accommodates trusted third‑party services (e.g., Google APIs, CDN providers).

Example Security Headers

# In the server block (see full configuration later)

# Prevent your site from being framed to prevent clickjacking
add_header X-Frame-Options 'deny, nosniff' always;
add_header X-Content-Type-Options nosniff always;

# Enables XSS filtering in supported browsers, this header instructs browsers to block pages when they detect reflected XSS.
add_header "X-XSS-Protection" "1; mode=block" always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;

# Content-Security-Policy allowing trusted services and applying a nonce for inline scripts
add_header Content-Security-Policy "default-src 'self'; \
  script-src 'self' 'unsafe-eval' 'nonce-$csp_nonce' https://maps.googleapis.com https://www.google.com https://www.gstatic.com https://cdnjs.cloudflare.com; \
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
  font-src 'self' data: https://fonts.gstatic.com; \
  frame-src 'self' https://www.google.com $allowed_origin; \
  connect-src 'self' data: https://*.googleapis.com wss://example.com; \
  object-src 'none'; \
  img-src 'self' data:; \
  frame-ancestors 'self' $allowed_origin; \
  worker-src 'self' data: blob:; \
  base-uri 'self'; \
  form-action 'self'; \
  upgrade-insecure-requests;" always;

add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header Feature-Policy "geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'self'; payment 'none'" always;

Explanation: These headers reduce the risk of clickjacking, XSS, MIME type confusion, and data leakage. The CSP header is tuned to allow scripts and styles from trusted sources (e.g., Google APIs, cdn.jsdelivr.net) and uses a nonce to allow inline scripts only when authorized.


4. Mitigating SQL Injection

Although preventing SQL injection is primarily an application-layer responsibility (using parameterized queries), NGINX can help filter out malicious requests.

server {
    listen 443 ssl;
    server_name example.com;

    location / {
        # Block suspicious SQL injection patterns in the query string
        if ($query_string ~* "union.*select.*\(") {
            return 403;
        }
        proxy_pass http://backend;
    }
}

Why? This simple pattern-based filter adds an extra layer of protection by rejecting obviously malicious requests, though backend validation remains essential.

4. DDoS Protection & Rate Limiting

Protect your server from abuse and DDoS attacks by limiting the number of requests from a single client.

Rate Limiting Example:

http {
    # Define a shared zone for rate limiting by client IP
    limit_req_zone $binary_remote_addr zone=ddos_zone:10m rate=10r/s;
    
    ...
    
    server {
        listen 443 ssl http2;
        server_name example.com www.example.com;
        
        location / {
            # Apply rate limiting with burst capacity to handle short spikes
            limit_req zone=ddos_zone burst=20 nodelay;
            proxy_pass http://backend.com:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

Explanation: This configuration limits clients to 10 requests per second (with bursts of up to 20), helping prevent abuse and service disruption (mitigating OWASP’s Insecure Design and Vulnerable Components).


5. Access Restrictions by IP, GeoIP, and User-Agent

Limit access to sensitive endpoints by IP, geographical region, or by filtering malicious user agents.

A. IP-based Restrictions

server {
    listen 443 ssl http2;
    server_name admin.example.com;
    
    location /admin/ {
        # Only allow trusted IP ranges to access the admin area
        allow 192.168.1.0/24;
        deny all;
        proxy_pass http://backend.com:8080/admin/;
    }
}

B. User-Agent Filtering

In our global configuration, we mapped suspicious User-Agents to a variable ($bad_user_agent). Then, within the server block, we block those requests:

if ($bad_user_agent) { # bad_user_agent defined in global
    return 403;
}
 if ($http_user_agent ~* "(?:maliciousBot|badCrawler)") {
            return 403;
}

Explanation: These restrictions help mitigate unauthorized access and reduce attack surfaces (addressing Broken Access Control and Identification Failures).

C. Restricting by GeoIP

(Requires GeoIP module and database.)

http {
    geo $allowed_country {
        default no;
        # Only allow traffic from the US and Canada
        US yes;
        CA yes;
    }

    server {
        listen 80;
        server_name geo.example.com;

        location / {
            if ($allowed_country = no) {
                return 403;
            }
            proxy_pass http://backend;
        }
    }
}

Why? GeoIP restrictions reduce malicious traffic from regions where you do not operate, improving overall security posture.


6. Blocking Sensitive Directories and Files

Prevent access to internal or version control directories that could expose sensitive data.

location ~* ^/(\.well-known/|\.git|\.svn|\.hg|\.bzr|\._darcs|BitKeeper) {
    deny all;
    return 404;
}

Explanation: Blocking these directories prevents attackers from accessing configuration files and repository data, reducing risk of information disclosure.


7. Blocking Bots & Bad Traffic with ModSecurity

ModSecurity is a robust web application firewall (WAF) that integrates with NGINX to block malicious traffic, bots, and a wide range of web attacks (from SQL injection to XSS), further hardening your server against the OWASP Top 10 vulnerabilities. [ We need to install this module ]

server {
    listen 80;
    server_name secure.example.com;

    # Enable ModSecurity with its configuration rules (e.g., OWASP CRS)
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://backend;
    }
}

Why? ModSecurity, when combined with the OWASP Core Rule Set (CRS), can automatically block many common vulnerabilities and malicious requests, enhancing your server’s security.

8. Proxying to Backend & Handling Subdomains

When using NGINX as a reverse proxy for dynamic content, you may host multiple subdomains. For example, a subdomain like “marine.example.com” can proxy traffic to your backend server.

Subdomain Proxy Example (Marine UI):

server {
    listen 443 ssl http2;
    server_name marine.example.com;
    
    # Inherit similar SSL and security header settings as the main domain
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers   HIGH:!aNULL:!MD5;
    
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options 'deny, nosniff' always;
    add_header X-Content-Type-Options nosniff always;
    add_header "X-XSS-Protection" "1; mode=block" always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
    add_header Cache-Control "no-cache, no-store, must-revalidate" always;

    # Define allowed_origin for CSP purposes
    set $allowed_origin https://example.com;
    
    # Block bad user agents early
    if ($bad_user_agent) {
        return 403;
    }
    
    location / {
        proxy_pass http://backend.com:8080/;
        proxy_hide_header Server;
        
        # Set a valid Origin header based on our allowed origin
        if ($http_origin = "") {
            set $origin_value "$allowed_origin";
        }
        proxy_set_header Origin $origin_value;
    }
}

Explanation:

  • Subdomains: This server block handles “marine.example.com” with similar security settings while proxying requests to “backend.com:8080.”

  • Allowed Origin: Ensures that CSP and CORS-related headers use a trusted origin.


9. Mitigating OWASP Top 10 Vulnerabilities

Here’s how our configuration helps mitigate common OWASP risks:

  1. Broken Access Control:

    • Use IP-based restrictions and enforce HTTPS with HSTS.

    • Backend must also implement authentication/authorization.

  2. Cryptographic Failures:

    • Enforce strong TLS protocols, secure ciphers, and HSTS.

  3. Injection (SQL, Command, etc.):

    • Simple query string filters help block obvious injection patterns.

    • Application-level sanitization remains critical.

  4. Insecure Design:

    • Layered security with proper headers, rate limiting, and access controls.

  5. Security Misconfiguration:

    • Regularly update configurations and disable server tokens.

  6. Vulnerable and Outdated Components:

    • Keep NGINX and modules up to date.

  7. Identification and Authentication Failures:

    • Enforce HTTPS and restrict sensitive endpoints.

  8. Software and Data Integrity Failures:

    • Use secure transmission (SSL/TLS) and signed updates.

  9. Security Logging and Monitoring Failures:

    • Ensure thorough logging and integrate ModSecurity.

  10. Server-Side Request Forgery (SSRF):

    • Validate upstream requests and restrict access via firewall rules.


10. Final Consolidated Configuration

Below is the final, consolidated configuration incorporating all the suggestions, modified to use “example.com” and “backend.com:8080.” It also considers subdomains and third-party services.

# nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
    multi_accept on;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    client_max_body_size 20M;
    resolver 8.8.8.8 valid=10s;
    server_tokens off;
    sendfile on;
    keepalive_timeout 65;

    # Generate CSP nonce
    map $request_id $csp_nonce {
        "~*" $request_id;
    }

    # Map for suspicious User Agents
    map $http_user_agent $bad_user_agent {
        default 0;
        ~*^$ 1;
        ~*bot 1;
        ~*spider 1;
        ~*crawl 1;
        ~*[<>]script 1;
        ~*(nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) 1;
    }

    # Rate Limiting Zone for DDoS protection
    limit_req_zone $binary_remote_addr zone=ddos_zone:10m rate=10r/s;

    # Include project-specific configuration
    include proj/tdis.conf;
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}
# app.conf
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # SSL/TLS Settings (Let’s Encrypt)
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers   HIGH:!aNULL:!MD5;

    # HSTS Header
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # Security Headers
    add_header X-Frame-Options 'deny, nosniff' always;
    add_header X-Content-Type-Options nosniff always;
    add_header "X-XSS-Protection" "1; mode=block" always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
    add_header Cache-Control "no-cache, no-store, must-revalidate" always;

    # Content Security Policy including trusted third-party URLs and nonce for inline scripts
    add_header Content-Security-Policy "default-src 'self'; \
      script-src 'self' 'unsafe-eval' 'nonce-$csp_nonce' https://maps.googleapis.com https://www.google.com https://www.gstatic.com https://cdnjs.cloudflare.com; \
      style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
      font-src 'self' data: https://fonts.gstatic.com; \
      frame-src 'self' https://www.google.com https://example.com; \
      connect-src 'self' data: https://*.googleapis.com wss://example.com; \
      object-src 'none'; \
      img-src 'self' data:; \
      frame-ancestors 'self' https://example.com; \
      worker-src 'self' data: blob:; \
      base-uri 'self'; \
      form-action 'self'; \
      upgrade-insecure-requests;" always;

    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header Feature-Policy "geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; fullscreen 'self'; payment 'none'" always;

    # Substitution for CSP nonce replacement in served files
    sub_filter_once off;
    sub_filter_types *;
    sub_filter "**CSP_NONCE**" "$csp_nonce";

    # Block sensitive paths
    location ~* ^/latest/meta-data/ { deny all; return 404; }
    location ~* ^/meta-data/ { deny all; return 404; }
    location ~* ^/(\.well-known/|\.git|\.svn|\.hg|\.bzr|\._darcs|BitKeeper) { deny all; return 404; }

    # Block requests with suspicious User Agents
    if ($bad_user_agent) {
        return 403;
    }

    # Main location serving static content
    location / {
        try_files $uri $uri/ /index.html;
        if ($request_uri ~* ".(ico|json|css|js|gif|jpe?g|png)$") {
            expires 30d;
            access_log off;
            add_header Pragma public;
            add_header Cache-Control "public";
            add_header X-Content-Type-Options nosniff always;
            break;
        }
        
        # Optional: Filter for basic SQL injection patterns
        if ($query_string ~* "union.*select.*\(") {
            return 403;
        }
    }

    # Proxy location for dynamic content 
    location /api/ {
        
        # Optional: Filter for basic SQL injection patterns
        if ($query_string ~* "union.*select.*\(") {
            return 403;
        }
    
        proxy_pass http://backend.com:8080/;
        proxy_hide_header Server;
        if ($http_origin = "") {
            set $origin_value "https://example.com";
        }
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Origin $origin_value;
        add_header X-Frame-Options 'deny, nosniff' always;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        add_header X-Content-Type-Options nosniff always;
        add_header "X-XSS-Protection" "1; mode=block" always;
        add_header Referrer-Policy strict-origin-when-cross-origin always;
        add_header Cache-Control "no-cache, no-store, must-revalidate" always;
        if ($bad_user_agent) { return 403; }
    }
    
    # Restricting access to sensitive areas (e.g., admin)
    location /admin/ {
        allow 192.168.1.0/24;
        deny all;
        proxy_pass http://backend_admin;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root html;
    }

 # GeoIP-based restrictions (requires GeoIP module and database)
    location /geo/ {
        if ($allowed_country = no) {
            return 403;
        }
        proxy_pass http://backend_geo;
    }
}

# Optional: Additional server block for subdomain (e.g., marine.example.com)
server {
    listen 443 ssl http2;
    server_name example.com;
    include app.conf;  # Or override as needed with similar security headers and proxy_pass directives
}

# Optionally, a separate server block for ModSecurity-enabled endpoints
server {
    listen 443 ssl http2;
    server_name secure.example.com;
    
    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {
        proxy_pass http://backend.com:8080;
    }
}

Explanation of Final Config:

  • Global & HTTP Settings: Optimize performance and security globally (worker settings, DNS resolver, etc.).

  • SSL/TLS and HSTS: Ensure all connections are encrypted and browsers enforce HTTPS.

  • Security Headers & CSP: Prevent clickjacking, XSS, MIME sniffing, and control resource loading, including allowances for trusted third-party services.

  • Rate Limiting & Access Restrictions: Deter DDoS attacks and block malicious traffic (via IP/User-Agent/GeoIP as needed).

  • Subdomain Handling: Demonstrates how to configure subdomains (e.g., marine.example.com) to proxy to your backend.

  • OWASP Top 10 Mitigations: Address vulnerabilities (injection, broken access, cryptographic failures, etc.) via layered configuration.

  • ModSecurity Integration: Adds a WAF layer to further protect against web attacks.


PreviousNginx as a Reverse Proxy and Load BalancerNextPerformance Optimization & Tuning in NGINX

Last updated 3 months ago