{"id":600,"date":"2018-02-05T15:21:21","date_gmt":"2018-02-05T07:21:21","guid":{"rendered":"https:\/\/blog.jsjs.org\/?p=600"},"modified":"2018-02-05T15:21:21","modified_gmt":"2018-02-05T07:21:21","slug":"mitigating-ddos-attacks-with-nginx-and-nginx-plus","status":"publish","type":"post","link":"https:\/\/blog.jsjs.org\/?p=600","title":{"rendered":"Mitigating DDoS Attacks with NGINX and NGINX Plus"},"content":{"rendered":"<p>A\u00a0<a href=\"https:\/\/www.nginx.com\/resources\/glossary\/what-is-distributed-denial-of-service\/\">Distributed Denial\u2011of\u2011Service (DDoS) attack<\/a>\u00a0is an attempt to make a service, usually a website, unavailable by bombarding it with so much traffic from multiple machines that the server providing the service is no longer able to function correctly because of resource exhaustion.<\/p>\n<p>Typically, the attacker tries to saturate a system with so many connections and requests that it is no longer able to accept new traffic, or becomes so slow that it is effectively unusable.<\/p>\n<h2>Application\u2011Layer DDoS Attack Characteristics<\/h2>\n<p>Application\u2011layer (Layer\u00a07\/HTTP) DDoS attacks are carried out by software programs (<em>bots<\/em>) that can be tailored to best exploit the vulnerabilities of specific systems. For example, for systems that don\u2019t handle large numbers of concurrent connections well, merely opening a large number of connections and keeping them active by periodically sending a small amount of traffic can exhaust the system\u2019s capacity for new connections. Other attacks can take the form of sending a large number of requests or very large requests. Because these attacks are carried out by bots rather than actual users, the attacker can easily open large numbers of connections and send large numbers of requests very rapidly.<\/p>\n<p>Characteristics of DDoS attacks that can be used to help mitigate against them include the following (this is not meant to be an exhaustive list):<\/p>\n<ul>\n<li>The traffic normally originates from a fixed set of IP\u00a0addresses, belonging to the machines used to carry out the attack. As a result, each IP\u00a0address is responsible for many more connections and requests than you would expect from a real user.\n<p><strong>Note:<\/strong>\u00a0It\u2019s important not to assume that this traffic pattern always represents a DDoS attack. The use of forward proxies can also create this pattern, because the forward proxy server\u2019s IP\u00a0address is used as the client address for requests from all the real clients it serves. However, the number of connections and requests from a forward proxy is typically much lower than in a DDoS attack.<\/li>\n<\/ul>\n<ul>\n<li>Because the traffic is generated by bots and is meant to overwhelm the server, the rate of traffic is much higher than a human user can generate.<\/li>\n<li>The\u00a0<code>User-Agent<\/code>\u00a0header is sometimes set to a non\u2011standard value.<\/li>\n<li>The\u00a0<code>Referer<\/code>\u00a0header is sometimes set to a value you can associate with the attack.<\/li>\n<\/ul>\n<h2>Using NGINX and NGINX\u00a0Plus to Fight DDoS Attacks<\/h2>\n<p>NGINX and NGINX\u00a0Plus have a number of features that\u00a0\u2013\u00a0in conjunction with the characteristics of a DDoS attack mentioned above\u00a0\u2013\u00a0can make them a valuable part of a DDoS attack mitigation solution. These features address a DDoS attack both by regulating the incoming traffic and by controlling the traffic as it is proxied to backend servers.<\/p>\n<h3>Inherent Protection of the NGINX Event\u2011Driven Architecture<\/h3>\n<p>NGINX is designed to be a \u201cshock absorber\u201d for your site or application. It has\u00a0<a href=\"http:\/\/www.aosabook.org\/en\/nginx.html\" target=\"_blank\" rel=\"noopener\">a non\u2011blocking, event\u2011driven architecture<\/a>\u00a0that copes with huge amounts of requests without a noticeable increase in resource utilization.<\/p>\n<p>New requests from the network do not interrupt NGINX from processing ongoing requests, which means that NGINX has capacity available to apply the techniques described below which protect your site or application from attack.<\/p>\n<p>More information about the underlying architecture is available at\u00a0<a href=\"https:\/\/www.nginx.com\/blog\/inside-nginx-how-we-designed-for-performance-scale\/\">Inside NGINX: How We Designed for Performance &amp; Scale<\/a>.<\/p>\n<h3>Limiting the Rate of Requests<\/h3>\n<p>You can limit the rate at which NGINX and NGINX\u00a0Plus accept incoming requests to a value typical for real users. For example, you might decide that a real user accessing a login page can only make a request every 2\u00a0seconds. You can configure NGINX and NGINX\u00a0Plus to allow a single client IP\u00a0address to attempt to login only every 2\u00a0seconds (equivalent to 30\u00a0requests per\u00a0minute):<\/p>\n<pre><code class=\"config\">limit_req_zone $binary_remote_addr zone=one:10m rate=30r\/m;<\/code><\/pre>\n<p>server { # &#8230; location \/login.html { limit_req zone=one; # &#8230; } }<\/p>\n<pre><code class=\"config\"><\/code><\/pre>\n<p>The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_limit_req_module.html#limit_req_zone\" target=\"_blank\" rel=\"noopener\">limit_req_zone<\/a><\/code>\u00a0directive configures a shared memory zone called\u00a0<strong>one<\/strong>\u00a0to store the state of requests for the specified key, in this case the client IP\u00a0address (<code>$binary_remote_addr<\/code>). The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_limit_req_module.html#limit_req\" target=\"_blank\" rel=\"noopener\">limit_req<\/a><\/code>\u00a0directive in the\u00a0<code>location<\/code>\u00a0block for\u00a0<strong>\/login.html<\/strong>\u00a0references the shared memory zone.<\/p>\n<p>For a detailed discussion of rate limiting, see\u00a0<a href=\"https:\/\/www.nginx.com\/blog\/rate-limiting-nginx\/\">Rate Limiting with NGINX and NGINX\u00a0Plus<\/a>\u00a0on our blog.<\/p>\n<h3>Limiting the Number of Connections<\/h3>\n<p>You can limit the number of connections that can be opened by a single client IP\u00a0address, again to a value appropriate for real users. For example, you can allow each client IP\u00a0address to open no more than 10\u00a0connections to the\u00a0<strong>\/store<\/strong>\u00a0area of your website:<\/p>\n<pre><code class=\"config\">limit_conn_zone $binary_remote_addr zone=addr:10m;<\/code><\/pre>\n<p>server { # &#8230; location \/store\/ { limit_conn addr 10; # &#8230; } }<\/p>\n<pre><code class=\"config\"><\/code><\/pre>\n<p>The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_limit_conn_module.html#limit_conn_zone\" target=\"_blank\" rel=\"noopener\">limit_conn_zone<\/a><\/code>\u00a0directive configures a shared memory zone called\u00a0<strong>addr<\/strong>\u00a0to store requests for the specified key, in this case (as in the previous example) the client IP\u00a0address,\u00a0<code>$binary_remote_addr<\/code>. The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_limit_conn_module.html#limit_conn\" target=\"_blank\" rel=\"noopener\">limit_conn<\/a><\/code>\u00a0directive in the\u00a0<code>location<\/code>\u00a0block for\u00a0<strong>\/store<\/strong>\u00a0references the shared memory zone and sets a maximum of 10\u00a0connections from each client IP\u00a0address.<\/p>\n<h3>Closing Slow Connections<\/h3>\n<p>You can close connections that are writing data too infrequently, which can represent an attempt to keep connections open as long as possible (thus reducing the server\u2019s ability to accept new connections). Slowloris is an example of this type of attack. The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#client_body_timeout\" target=\"_blank\" rel=\"noopener\">client_body_timeout<\/a><\/code>\u00a0directive controls how long NGINX waits between writes of the client body, and the\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#client_header_timeout\" target=\"_blank\" rel=\"noopener\">client_header_timeout<\/a><\/code>\u00a0directive controls how long NGINX waits between writes of client headers. The default for both directives is 60\u00a0seconds. This example configures NGINX to wait no more than 5\u00a0seconds between writes from the client for either headers or body:<\/p>\n<pre><code class=\"config\">server {\n    client_body_timeout 5s;\n    client_header_timeout 5s;\n    # ...\n}<\/code><\/pre>\n<h3>Blacklisting IP Addresses<\/h3>\n<p>If you can identify the client IP\u00a0addresses being used for an attack, you can blacklist them with the\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_access_module.html#deny\" target=\"_blank\" rel=\"noopener\">deny<\/a><\/code>directive so that NGINX and NGINX\u00a0Plus do not accept their connections or requests. For example, if you have determined that the attacks are coming from the address range 123.123.123.1 through 123.123.123.16:<\/p>\n<pre><code class=\"config\">location \/ {\n    deny 123.123.123.0\/28;\n    # ...\n}<\/code><\/pre>\n<p>Or if you have determined that an attack is coming from client IP\u00a0addresses 123.123.123.3, 123.123.123.5, and 123.123.123.7:<\/p>\n<pre><code class=\"config\">location \/ {\n    deny 123.123.123.3;\n    deny 123.123.123.5;\n    deny 123.123.123.7;\n    # ...\n}<\/code><\/pre>\n<h3>Whitelisting IP Addresses<\/h3>\n<p>If access to your website or application is allowed only from one or more specific sets or ranges of client IP\u00a0addresses, you can use the\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_access_module.html#allow\" target=\"_blank\" rel=\"noopener\">allow<\/a><\/code>\u00a0and\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_access_module.html#deny\" target=\"_blank\" rel=\"noopener\">deny<\/a><\/code>\u00a0directives together to allow only those addresses to access the site or application. For example, you can restrict access to only addresses in a specific local network:<\/p>\n<pre><code class=\"config\">location \/ {\n    allow 192.168.1.0\/24;\n    deny all;\n    # ...\n}<\/code><\/pre>\n<p>Here, the\u00a0<code>deny\u00a0all<\/code>\u00a0directive blocks all client IP\u00a0addresses that are not in the range specified by the\u00a0<code>allow<\/code>\u00a0directive.<\/p>\n<h3>Using Caching to Smooth Traffic Spikes<\/h3>\n<p>You can configure NGINX and NGINX\u00a0Plus to absorb much of the traffic spike that results from an attack, by enabling caching and setting certain caching parameters to offload requests from the backend. Some of the helpful settings are:<\/p>\n<ul>\n<li>The\u00a0<code>updating<\/code>\u00a0parameter to the\u00a0<code><a href=\"http:\/\/nginx.org\/r\/proxy_cache_use_stale\" target=\"_blank\" rel=\"noopener\">proxy_cache_use_stale<\/a><\/code>\u00a0directive tells NGINX that when it needs to fetch an update of a stale cached object, it should send just one request for the update, and continue to serve the stale object to clients who request it during the time it takes to receive the update from the backend server. When repeated requests for a certain file are part of an attack, this dramatically reduces the number of requests to the backend servers.<\/li>\n<li>The key defined by the\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_proxy_module.html#proxy_cache_key\" target=\"_blank\" rel=\"noopener\">proxy_cache_key<\/a><\/code>\u00a0directive usually consists of embedded variables (the default key,\u00a0<code>$scheme$proxy_host$request_uri<\/code>, has three variables). If the value includes the\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_core_module.html#var_query_string\" target=\"_blank\" rel=\"noopener\">$query_string<\/a><\/code>\u00a0variable, then an attack that sends random query strings can cause excessive caching. We recommend that you don\u2019t include the\u00a0<code>$query_string<\/code>\u00a0variable in the key unless you have a particular reason to do so.<\/li>\n<\/ul>\n<h3>Blocking Requests<\/h3>\n<p>You can configure NGINX or NGINX\u00a0Plus to block several kinds of requests:<\/p>\n<ul>\n<li>Requests to a specific URL that seems to be targeted<\/li>\n<li>Requests in which the\u00a0<code>User-Agent<\/code>\u00a0header is set to a value that does not correspond to normal client traffic<\/li>\n<li>Requests in which the\u00a0<code>Referer<\/code>\u00a0header is set to a value that can be associated with an attack<\/li>\n<li>Requests in which other headers have values that can be associated with an attack<\/li>\n<\/ul>\n<p>For example, if you determine that a DDoS attack is targeting the URL\u00a0<strong>\/foo.php<\/strong>\u00a0you can block all requests for the page:<\/p>\n<pre><code class=\"config\">location \/foo.php {\n    deny all;\n}<\/code><\/pre>\n<p>Or if you discover that DDoS attack requests have a\u00a0<code>User-Agent<\/code>\u00a0header value of\u00a0<code>foo<\/code>\u00a0or\u00a0<code>bar<\/code>, you can block those requests.<\/p>\n<pre><code class=\"config\">location \/ {\n    if ($http_user_agent ~* foo|bar) {\n        return 403;\n    }\n    # ...\n}<\/code><\/pre>\n<p>The\u00a0<code>http_<em>name<\/em><\/code>\u00a0variable references a request header, in the above example the\u00a0<code>User-Agent<\/code>\u00a0header. A similar approach can be used with other headers that have values that can be used to identify an attack.<\/p>\n<h3>Limiting the Connections to Backend Servers<\/h3>\n<p>An NGINX or NGINX\u00a0Plus instance can usually handle many more simultaneous connections than the backend servers it is load balancing. With NGINX\u00a0Plus, you can limit the number of connections to each backend server. For example, if you want to limit NGINX\u00a0Plus to establishing no more than 200 connections to each of the two backend servers in the\u00a0<strong>website<\/strong>\u00a0upstream group:<\/p>\n<pre><code class=\"config\">upstream website {\n    server 192.168.100.1:80 max_conns=200;\n    server 192.168.100.2:80 max_conns=200;\n    queue 10 timeout=30s;\n}<\/code><\/pre>\n<p>The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_upstream_module.html#max_conns\" target=\"_blank\" rel=\"noopener\">max_conns<\/a><\/code>\u00a0parameter applied to each server specifies the maximum number of connections that NGINX\u00a0Plus opens to it. The\u00a0<code><a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_upstream_module.html#queue\" target=\"_blank\" rel=\"noopener\">queue<\/a><\/code>\u00a0directive limits the number of requests queued when all the servers in the upstream group have reached their connection limit, and the\u00a0<code>timeout<\/code>\u00a0parameter specifies how long to retain a request in the queue.<\/p>\n<h3>Dealing with Range\u2011Based Attacks<\/h3>\n<p>One method of attack is to send a\u00a0<code>Range<\/code>\u00a0header with a very large value, which can cause a buffer overflow. For an discussion of how to use NGINX and NGINX\u00a0Plus to mitigate this type of attack in a sample case, see\u00a0<a href=\"http:\/\/nginx.com\/blog\/nginx-protect-cve-2015-1635\/\">Using NGINX and NGINX\u00a0Plus to Protect Against CVE\u20112015\u20111635<\/a>.<\/p>\n<h3>Handling High Loads<\/h3>\n<p>DDoS attacks usually result in a high traffic load. For tips on tuning NGINX or NGINX\u00a0Plus and the operating system to allow the system to handle higher loads, see\u00a0<a href=\"http:\/\/nginx.com\/blog\/tuning-nginx\/\">Tuning NGINX for Performance<\/a>.<\/p>\n<h2>Identifying a DDoS Attack<\/h2>\n<p>So far we have focused on what you can use NGINX and NGINX\u00a0Plus to help alleviate the effects of a DDoS attack. But how can NGINX or NGINX\u00a0Plus help you spot a DDoS attack? The\u00a0<a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_status_module.html\" target=\"_blank\" rel=\"noopener\">NGINX\u00a0Plus Status module<\/a>\u00a0provides detailed metrics about the traffic that is being load balanced to backend servers, which you can use to spot unusual traffic patterns. NGINX\u00a0Plus comes with a status dashboard web page that graphically depicts the current state of the NGINX\u00a0Plus system (see the example at\u00a0<a href=\"http:\/\/demo.nginx.com\/\" target=\"_blank\" rel=\"noopener\">demo.nginx.com<\/a>). The same metrics are also available through an API, which you can use to feed the metrics into custom or third\u2011party monitoring systems where you can do historical trend analysis to spot abnormal patterns and enable alerting.<\/p>\n<h2>Summary<\/h2>\n<p>NGINX and NGINX\u00a0Plus can be used as a valuable part of a DDoS mitigation solution, and NGINX\u00a0Plus provides additional features for protecting against DDoS attacks and helping to identify when they are occurring.<\/p>\n<p>To try NGINX\u00a0Plus, start your\u00a0<a href=\"https:\/\/www.nginx.com\/blog\/mitigating-ddos-attacks-with-nginx-and-nginx-plus\/#free-trial\">free 30\u2011day trial<\/a>\u00a0today or\u00a0<a href=\"https:\/\/www.nginx.com\/blog\/mitigating-ddos-attacks-with-nginx-and-nginx-plus\/#contact-us\">contact\u00a0us<\/a>\u00a0for a live demo.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A\u00a0Distributed Denial\u2011of\u2011Service (DDoS) attack\u00a0is an attempt to make a service, usually a website, unavailable by bombarding it with so much [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-600","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts\/600","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=600"}],"version-history":[{"count":0,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=\/wp\/v2\/posts\/600\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=600"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=600"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jsjs.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=600"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}