
Intoduction
Nginx is one of the most popular and fastest growing open source web servers in the world.
Compared to apache is more resource-friendly, it can be used as a web server, reverse proxy, load balancer, mail proxy and HTTP cache.
According to Gartner, misconfigurations is the reason for most breaches. Is critical to understand how to provision securely web servers as it will prevent misconfigurations and potential breaches or outages.
For this guide, we’ll go through several steps to improve Nginx security and performance.
Before we get started, I assume you have an Ununtu 16.0.4 server running with nginx installed.
If you do not, then follow this guide: Nginx getting started – Install
I will also provide an easier way to play around with nginx security and performance for the purpose of this guide with Docker (here).
For your convenience, the final configuration file is (here).
$ sudo apt-get -y update
$ sudo apt-get -y upgrade
$ sudo apt-get -y update nginx
If you are using Docker:
$ apt-get -y update && apt-get -y upgrade && $ apt-get -y update nginx
Syntax: server_tokens on | off | build | string;
Default: server_tokens on;
Context: http, server, location
This nginx directive enables or disables emitting nginx version on:
- error pages and
- in the “Server” response header field.
In order to prevent attacks on our web server caused by known vulnerabilities of our version, we should not share this information with the world.
So, since the default is on , we should set it to off in the http, server or location block.
http { include conf/mime.types; index index.html index.htm index.php; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; tcp_nopush on; # don't send the nginx version number in error pages and Server header server_tokens off; server { # php/fastcgi listen 80; server_name domain1.com www.domain1.com; access_log logs/domain1.access.log main; #server_tokens off; root html; location ~ \.php$ { fastcgi_pass 127.0.0.1:1025; #server_tokens off; } }
$ sudo nginx -s stop $ sudo nginx -s start or $ sudo nginx -s reload
## Deny certain User-Agents (case insensitive) ## The ~* makes it case insensitive as opposed to just a ~ if ($http_user_agent ~* LWP::Simple|BBBike|wget) { return 403; }
# Add/ Remove agents/bots from the list below depending your preference map $http_user_agent $limit_bots { default 0; ~*(google|Googlebot|bing|yandex|msnbot) 1; ~*(AltaVista|Slurp|BlackWidow|Bot|ChinaClaw|Custo|DISCo|Download|Demon|eCatch|EirGrabber|EmailSiphon|EmailWolf|SuperHTTP|Surfbot|WebWhacker) 1; ~*(Express|WebPictures|ExtractorPro|EyeNetIE|FlashGet|GetRight|GetWeb!|Go!Zilla|Go-Ahead-Got-It|GrabNet|Grafula|HMView|Go!Zilla|Go-Ahead-Got-It) 1; ~*(rafula|HMView|HTTrack|Stripper|Sucker|Indy|InterGET|Ninja|JetCar|Spider|larbin|LeechFTP|Downloader|tool|Navroad|NearSite|NetAnts|tAkeOut|WWWOFFLE) 1; ~*(GrabNet|NetSpider|Vampire|NetZIP|Octopus|Offline|PageGrabber|Foto|pavuk|pcBrowser|RealDownload|ReGet|SiteSnagger|SmartDownload|SuperBot|WebSpider) 1; ~*(Teleport|VoidEYE|Collector|WebAuto|WebCopier|WebFetch|WebGo|WebLeacher|WebReaper|WebSauger|eXtractor|Quester|WebStripper|WebZIP|Wget|Widow|Zeus) 1; ~*(Twengabot|htmlparser|libwww|Python|perl|urllib|scan|Curl|email|PycURL|Pyth|PyQ|WebCollector|WebCopy|webcraw) 1; } if ($limit_bots = 1) { return 403; }
# Format to use in log files # $remote_addr The remote host # $remote_user The authenticated user (if any) # $time_local The time of the access # $request The first line of the request # $status The status of the request # $body_bytes_sent The size of the server's response, in bytes # $http_referer The referrer URL, taken from the request's headers # $http_user_agent The user agent, taken from the request's headers log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; # Default log file # (this is only used when you don't override access_log on a server{} level) access_log /var/log/nginx/nginx.access.log main; # Default error log file # (this is only used when you don't override error_log on a server{} level) error_log /var/log/nginx/nginx.error.log warn;
# Prevent hotlinking - To prevent people hotlinking to your files. One aspect is security: # say you have sensitive material that you only want your direct visitors to see. location ~ .(gif|png|jpg|doc|xls|pdf|html|htm|xlsx|docx|mp4|mov)$ { valid_referers none blocked ~.google. ~.bing. ~.yahoo. yourdomain.com *.yourdomain.com; if ($invalid_referer) { return 403; }
# config to don't allow the browser to render the page inside an frame or iframe # and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking # if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri with ALLOW-FROM uri # https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options add_header X-Frame-Options SAMEORIGIN;
# This header enables the Cross-site scripting (XSS) filter built into most recent web browsers. # It's usually enabled by default anyway, so the role of this header is to re-enable the filter for # this particular website if it was disabled by the user. # https://www.owasp.org/index.php/List_of_useful_HTTP_headers add_header X-XSS-Protection "1; mode=block";
# When serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header, to disable content-type sniffing on some browsers. # https://www.owasp.org/index.php/List_of_useful_HTTP_headers # currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx # http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx # 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020 add_header X-Content-Type-Options nosniff;
## Reset lingering timed out connections, freeing ram. Deflect DDoS. reset_timedout_connection on; # Directive describes the zone, in which used to limit the number of connections per the # defined key,in particular, the number of connections from a single IP address # 1m can handle 32000 sessions with 32 bytes/session, set to 10m x 32000 session limit_conn_zone $binary_remote_addr zone=perip:10m; # Control maximum number of simultaneous connections for one session i.e. ### # restricts the amount of connections from a single ip address ### limit_conn perip 10;
## Start: Size Limits & Buffer Overflows ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 4g;
large_client_header_buffers 2 1k;
## END: Size Limits & Buffer Overflows ##
## Only requests to our Host are allowed i.e. yourdomain.com
if ($host !~ ^(www.yourdomain.com|api.yourdomain.com)$ ) {
return 444;
}
Deny or allow a single IP:
deny 1.2.3.4; or allow 1.2.3.4;
Deny or allow IP range:
deny 1.2.3.4/24; or allow 1.2.3.4/24;
Deny All:
deny all;
Combined for specific location(admin area):
location ^~ /admin { deny all; allow 1.2.3.4; # home ip address allow 9.8.7.6; # office ip address }
add_header Allow “GET, POST, HEAD” always;
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
return 405;
}
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# cipher suite for backwards compatibility (IE6/WinXP) ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; # means that the server will prefer to use ciphers specified in the ssl_ciphers # directive over the ciphers preferred by clients. ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m;
For backwards compatibility (IE6/WinXP) use:
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
# OCSP stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; ssl_trusted_certificate /etc/letsencrypt/domain/live/chain.pem
# config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";