How to secure Nginx web server

Nam Huy Linux is using Nginx because Nginx is a great lightweight and high performace web server/reverse proxy server. Nginx is the second most popular webserver just after Apache web server, there are many popular sites using Nginx like Wikipedia, Sina, Weibo, Yahoo, Reddit, Tumblr, Worpress… Nginx can easily handle thousands inactive HTTP connections with very low memory consumption. In this article, I will show you how to secure/harden nginx web server to improve Nginx server security.

Disable nginx server_tokens

Nginx displays its version number by default in HTTP response header and automatically generated error pages. This could give out unnecessary server information to anyone. For better security practice, we should disable nginx server_tokens parameter in Nginx configuration file by setting on (by default) to off.

# nano /etc/nginx/nginx.conf

add server_tokens off; inside http { your nginx configurations here }.

http
{
server_tokens off;
}

Restart Nginx to take effect

# service nginx restart

Disable Directory Listing

Most web servers allow users to browse the directories (folder) if there is no index file is available. Disabling directory listing isn’t really a security measure but it’s a great security through obscurity. By not listing files from view, visitors won’t know the file names or extensions, there for they can’t access those things.

To disable directory listing on Nginx, you just need to add the following lines to your site’s configuration

location / {
autoindex off;
}

Remove PHP version from HTTP header

Unless is disabled, Nginx & PHP give away their version. I have shown you how to hide Nginx version above in “Disable nginx server_tokens” section, yet PHP (PHP: Hypertext Preprocessor) still shows its version in HTTP header. For security purposes, it’s a good idea to prevent those information from being shown to the public. Similar to disable directory listing, security through obscurity is no real security, but the less information your web server gives out, the better.

Edit php’s configuration file:

# nano /etc/php.ini

Look for expose_php and set its value to Off

; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header). It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php = Off

Restart php-fpm

# service nginx restart

Disable unused/unwanted nginx modules

Nginx will install loads of nginx modules automatically compiled directly into the nginx binary. Disabling unused/unwanted modules on Nginx you can minimize the risks of potential attacks by limiting the capabilities allowed by Nginx web server. The catch is to do this, you would need to re-compile your nginx setup if you have one running. If you don’t have nginx installed yet, you can disable unused/unwanted nginx modules with the configure option during installation.

For the full list of Nginx’s modules, you can visit http://wiki.nginx.org/Modules, for our article purpose I’m going to disable autoindex, ssi, and scgi modules.

# ./configure --without-http_autoindex_module --without-http_ssi_module --without-http_scgi_module
# make
# make install

Controlling Buffer Overflow Attacks

One of the most frequent attack types is the buffer overflow attack. A buffer overflow is an exploit that takes advantage of a program that is waiting on a user’s input that is longer than the implementor intended. Nginx is likely to suffer from two main type of buffer overflow attacks: stack based and heap based.

  • Heap based attacks flood the memory space reserved for Nginx, but it’s difficult to perform such an attack makes heap based attacks rare.
  • Stack based buffer overrun take advantage of nginx exploits/vulnerabilities (attacker places the address of the top of the stack with horrible lines of assembly code, such as a call to another tool, the program writes a return memory address to the stack and then the user’s input is placed on top of it). 

To prevent buffer overflow attacks in Nginx we can set buffer size limitations for all clients through the Nginx configuration file using the following directive

  • client_body_buffer_size Directive sets specify the buffer size for reading client request. Default value is 8k on x86/32-bit platforms, or 16k on x86-64/64-bit platforms. It is recommended to set this directive as low as 1k.
  • client_header_buffer_size Directive sets specify the header buffer size for reading client request header. In most case, 1k header buffer size is enough, but if you have a custom header, long cookies, WAP client, you can increase the header buffer size value.
  • client_max_body_size Directive sets specify the maximum allowed client request’s body size, indicated in the Content-Length request header field. Usually the value of 1k is enough, however if the client gets 413 error (Request Entity Too Large) means the size of in a request is greater than given one (please note that some browsers don’t display this error correctly). You will need to increase client_max_body_size value greater than 1k if you get file uploads via the POST method. You can disable checking of client request body size by setting the value to 0.
  • large_client_header_buffers Directive sets the maximum number and size of buffers to be used for reading large client request header. By default the buffer size is 8k bytes or equal to the size of page depends on platform. There are two values for large_client_header_buffers directive, “number” and “size”. The first number is the maximum number of buffer, the second number is buffer size. For example large_client_header_buffers 2 1k sets maximum number of buffer to 2, and each with 1k size (2 x 1k = 2kB data URI). Clients will get 414 (Request-URI Too Large) error if a request line exceed the size of one buffer. Clients will get 400 (Bad Request) error if a request header field can not exceed the size of one buffer.

Edit the Nginx configuration file:

# nano /etc/nginx/nginx.conf

Put these lines in HTTP server directives

Start limit buffer size ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;
## END limit buffer size ##

Limit available HTTP methods

There are two commonly used HTTP/1.1 request methods between client-server on the Internet are GET and POST.

  • GET (along with HEAD) requests are used to read data only or retrieve information from the given server using a given URI. and not change it.
  • POST method is used to send data to the server (file upload, customer information, etc…).
  • Head method is same as GET bur returns HTTP headers without document body.

There are other HTTP request methods (DELETE, CONNECT, OPTIONS, TRACE) which are not often used. There for we should disable any HTTP methods which are not going to be used on the web server. We can only allow GET, HEAD, and POST methods, and filter out other methods by giving a 444 No Response status code (the server returns no information to the client and closes the connection).

Edit Nginx Virtual Hosts (Server Blocks)

# nano /etc/nginx/conf.d/yourdomain.conf

Insert these codes inside your server directive. server { your codes go here }

only accept GET, HEAD, and POST HTTP methods ##
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
## Limit available HTTP methods ##

Blacklist bad User-Agents

The modern Internet is full of malicious robots, crawlers, bots, content scrapers, scanners, etc… Some of them are constantly scanning your web server for vulnerabilities, harvest email addresses, or steal your website’s contents. You can blacklist those bad bots by blocking their “user-agent” signature string.

Edit the Nginx configuration file of your website

# nano /etc/nginx/conf.d/yourdomain.conf

Please copy and paste these lines within server directive

# case-insensitive matching
if ($http_user_agent ~* (wget|libwww|lwp-trivial|Jakarta|User-Agent|java|curl|Snoopy)) {
return 444;
}

This is a small list of bad user-agent, please google search yourself for a longer/better list.

Configure Nginx Security Headers

Nginx security headers or additional headers can make your websites more secure by passing security policies, set configuration options, and disable features of the browsers back to the clients (web browsers), hosts can give a much safer browsing experience to their visitors and reduce the risk for both parties. These additional headers give the browser more information about how hosts want to act with regards to your websites. You can add these security headers to your main nginx’s configuration file or your Virtual Hosts (Server Blocks) files

# nano /etc/nginx/nginx.conf

The headers go to HTTP server directive

http {
add_header ...
[...]
}

or add to separate Virtual Hosts (Server Blocks) files

# nano /etc/nginx/conf.d/yourdomain.conf

The headers go to server directive

server {
[...]
add_header ...
[...]
}

Let’s add some Nginx security headers

add_header X-Content-Type-Options nosniff;

Supported by IE (Internet Explorer) and Google Chrome to prevent them from MIME-sniffing a response from the declared content-type. The only defined value “nosniff” for “X-Content-Type-Options” HTTP header will prevent attakcs based on MIME-type confusion. This also improve the security for Nginx web server and clients (your web server and web users) against drive-by download attacks.

add_header X-Frame-Options SAMEORIGIN;

Helps your sites against ClickJacking, supported by all browsers to provide protection from iframing the content of your web sites into others. There are three defined value “deny”, “sameorigin”, and “allow-from”.

  • DENY” will not allow rendering/displaying content within any frame.
  • SAMEORIGIN” will not allow rendering/displaying content in any frame from a page if it’s different origin than the content itself. Some web browsers or plugins can not reliably determine the origin of the content and the frame have the same origin. If you want to have better security measurement, always use “DENY” rather than “SAMEORIGIN”.
  • ALLOW-FROM” will allow rendering/displaying content if framed loaded from a trusted URI (origin/DOMAIN). This can be risky if the trusted URI is malware infected, use this value wisely if you need to use content from other domains. For example: X-Frame-Options: ALLOW-FROM https://www.domain.com/
add_header X-XSS-Protection "1; mode=block";

This header will help your site to against some types of Cross-site scripting (XSS) attacks. The header will enable XSS filter built into most popular web browsers like IE, Chrome, Firefox, etc… Normally it’s enabled by default by web browser, but this header will re-enable the filter for this particular website if it XSS filter was disabled by the user.

add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

This header is for whos websites accepts a connection through HTTP and redirects to HTTPS. HTTP Strict-Transport-Security (HSTS) enforces secure (HTTP over SSL/TLS) connections to the server to prevent man-in-the-middle attack by reducing session data leaking through cookies and external links. HSTS lets your websites inform end user’s browsers that is should never load the sites using HTTP, and convert all HTTP connections to HTTPS.

 Flood/DoS protection

I have manged websites for a while, there are bots & attackers scan for vulnerabilities, content scrapers, etc… Normally those bots connect to your web server with thousands of request and/or connections simultaneously, which will take a lot of resources from your server. You can limit the maximum number of requests from a user with Nginx’s limit_req module & limit the maximum connections with Nginx’s limit_conn to prevent potential DoS/Flood attacks against your server.

ngx_http_limit_req_module module is used to limit the request rate from a single IP address to the server. First let’s create a zone in Nginx’s http {…} block with 5 request per second, and the zone to hold up to 10MB of data. 10m (10 MB) is the size of data zone will keep. 1 MB can hold 16000 64-byte states, if you have high traffic sites or many sites on the same server, you may want to increase shared memory.(adjust the request value depends on your web applications)

# nano /etc/nginx/nginx.conf

Insert limit_reg_zone line as the following sample

# ---------------------------------------
# HTTP Core Module
# ---------------------------------------

http {
limit_req_zone $binary_remote_addr zone=limit:10m rate=5r/s;
[...]
}

After creating the zone “limit” (you can rename the zone name to whatever you want, in our example I use “limit”), we will have to enable it in each Virtual Hosts (Server Blocks). You can use the same zone for multiple Virtual Hosts (Server Blocks).

# nano /etc/nginx/conf.d/yourdomain.conf

Then inside the server block, set a limit_req

# ---------------------------------------
# vHost yourdomain.com
# ---------------------------------------

[...]

location / {
limit_req zone=limit burst=5 nodelay;
limit_req_status 444;
...
}

[...]
  • nodelay option enforces the request rate, when request limit exceeds, HTTP status code 503 (Service Unavailable) is returned.
  • burst it’s the number of request that allowed to burst for web application per a single IP address. Set maximum requests as rate * burst in burst seconds. For example, maximum value is 25(=5*5) requests in 1 seconds in our sample. Depends on your web application, adjust burst value accordingly. If you set the burst value too low, your web application won’t work correctly (blank page, error, etc…).
  • limit_req_status When the number of request exceeds, rather than return 503 error, you can change the return code to whatever you want. In our sample I want Nginx to return 444 error for requests exceeding burst number.

ngx_http_limit_conn_module limits the number of connections from a specific IP address, you can set this globally for your server or a single website.

Updates

As a part of your regular server maintain, make sure you keep your system & programs updated. Attackers exploit vulnerabilities in all system and program including nginx and it’s add-on code like cgi, perl, PHP, etc… Not only getting security vulnerabilities & bugs get fixed, you will be likely get new feature by upgrading Nginx and other programs to newer version.

To update on Ubuntu/Debian based distro

# apt-get update; apt-get dist-upgrade

To update on Fedora/Centos/RedHat based distro

# yum update

References

“Nginx Security Advisories.” Nginx Security Advisories. Web. 11 Aug. 2015. .

“Web Server Technologies Web Usage Statistics.” Web Server Technologies Web Usage Statistics. Builtwith.com. Web. 6 Aug. 2015. <http://trends.builtwith.com/web-server>. Nginx is the second most popular web server

“Usage Statistics and Market Share of Nginx for Websites.” Usage Statistics and Market Share of Nginx for Websites, August 2015. Web. 11 Aug. 2015. .

“Module Ngx_http_core_module.” Module Ngx_http_core_module. Nginx. Web. 6 Aug. 2015. <http://nginx.org/en/docs/http/ngx_http_core_module.html>.

Leave a Comment

Your email address will not be published. Required fields are marked *