65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Nginx 防御 ASP.NET 和 WCF 免受各种攻击

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2016 年 7 月 28 日

CPOL

14分钟阅读

viewsIcon

48212

使用 nginx 保护 ASP.NET 和 WCF 免受各种暴力破解和拒绝服务攻击,并提高响应时间。

引言

ASP.NET 网站和 WCF 服务可以通过多种方式受到攻击,从而减慢服务速度甚至导致完全中断。有人可以发起 slowloirs 攻击来耗尽 IIS 上的所有连接和线程,导致完全中断。有人可以访问昂贵的 URL(例如下载 URL)或利用昂贵的 WCF 服务,导致 CPU 使用率过高并使服务崩溃。有人可以打开太多并行连接,阻止 IIS 接受更多连接。有人可以利用大文件下载 URL 并执行连续并行下载,耗尽网络带宽,导致完全中断或月底昂贵的带宽账单。有人还可以找到一种方法将大量数据插入数据库并耗尽数据库存储。

因此,ASP.NET 和 WCF 服务,与其他所有 Web 技术平台一样,需要比标准网络防火墙更多的保护。它们需要一个合适的 Web 应用程序防火墙 (WAF),它可以精确地检查应用程序上正在发生的操作,并阻止针对所保护应用程序的恶意事务。Nginx (engine x) 就是这样一种应用程序,它可以为 ASP.NET 和 WCF 提供多种防御措施,并且可以通过卸载静态文件和大文件传输来显著提高 ASP.NET 网站的速度。

即使您对 Linux 一无所知,仍然可以进行不错的 nginx 设置。

什么是 nginx

Nginx 是一个免费的开源 Web 服务器和反向代理。它现在是继 Apache 和 IIS 之后使用量第三大的服务器。除了免费版本外,还有一个商业版本 'Nginx Plus',它提供了更多用于负载均衡、安全和监控的功能。免费版本根本没有用于监控的 Web 控制台。除了简单的连接打开外,它没有后端服务器的健康检查功能,没有粘性会话功能,没有 Web 监控控制台(除了 nginxtop),没有 TCP 检查等等。Nginx 公司进行了巧妙的功能规划,将这些功能移到了商业版本中,一旦上线您就会渴望拥有它。但这并不意味着使用免费版本毫无用处。您仍然可以充分利用它。至少它比将 IIS 赤裸裸地暴露在互联网上要好得多。

在本文中,我们将探讨免费 nginx 的功能以及如何利用它们来防御 ASP.NET 和 WCF 服务。它的主要功能是作为反向代理,卸载后端服务器(如 IIS)处理静态文件、大文件下载、视频流等任务。它可以作为负载均衡器和速率限制器,限制进入后端 Web 服务器的流量。

我们可以阻止哪些攻击?

使用 nginx,我将展示如何实现以下用例

  • 阻止拒绝服务攻击
    • 阻止 slowloris 攻击以防止完全中断
    • 限制每个 IP 的开放连接数,从而降低 DOS 攻击的影响
    • 限制每个 IP 每秒/每分钟的 HTTP 请求数,从而降低 DOS 攻击的影响
    • 白名单、黑名单 IP
    • 限制每个 IP 的下载带宽,以防止带宽耗尽
  • ASP.NET 和 WCF 特定保护
    • 防止 ASP.NET 和 WCF 遭受大型 POST 请求
    • 为每个 URL 定义 POST 大小,例如,整个网站限制为 100k,但下载 URL 限制为 4MB
    • 禁用 WCF 服务 WSDL/Mex 端点,以免他人窥探服务可用性。
    • 从错误页面中隐藏堆栈跟踪,因为堆栈跟踪会泄露应用程序的内部信息,从而帮助攻击者。
    • 隐藏 IIS 和 ASP.NET 的标头,以免他人得知运行的版本并利用漏洞
    • 保护 IIS 免受范围攻击 CVE-2015-1635
  • 提高性能并卸载 IIS
    • 在浏览器中缓存 ASMX 和 WCF Ajax 代理,节省重复请求
    • 在 nginx 服务器上缓存静态文件,防止重复请求发送到 IIS
    • 卸载昂贵的 gzip 压缩,节省 IIS CPU
    • 通过首次请求时将大文件存储在 nginx 服务器上来卸载大文件传输
  • 阻止 AntiXSS 和注入攻击
  • 使用 Google 的 PageSpeed 模块提升站点性能

部署

您在 Linux 服务器上部署 nginx。您可以选择一个小型或中型的 Linux VM,因为 nginx 的资源占用非常低,却能处理每秒数千个请求。然后 Linux VM 将连接转发给 IIS 服务器。

安装

有针对各种 Linux 发行版的特定安装说明。但是,我从源代码编译它,因为我需要 headers-more-nginx-module,它允许进行标头操作。以下是以 root 用户身份运行的命令,用于下载、编译和安装 nginx 和该模块,使其准备就绪。您应根据可用的最新 nginx 版本更改 nginx URL 和文件夹名称。

mkdir nginx
cd nginx/
wget https://nginx.ac.cn/download/nginx-1.11.2.tar.gz
tar xf nginx-1.11.2.tar.gz
mkdir modules
cd modules/
wget https://github.com/openresty/headers-more-nginx-module/archive/master.zip
unzip master.zip
mv headers-more-nginx-module-master/ headers-more-nginx-module/
cd ..
cd nginx-1.11.2
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module \
--with-http_addition_module --with-http_sub_module --with-http_dav_module \
--with-http_flv_module --with-http_mp4_module --with-http_gunzip_module \
--with-http_gzip_static_module --with-http_random_index_module \
--with-http_secure_link_module --with-http_stub_status_module \
--with-http_auth_request_module --with-http_xslt_module=dynamic \
--with-threads --with-stream --with-stream_ssl_module \
--with-http_slice_module --with-mail --with-mail_ssl_module \
--with-file-aio --with-ipv6 --with-http_stub_status_module   \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions 
                      -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' \
--add-module=../modules/headers-more-nginx-module/

make

make install

which nginx

ginx -V

service nginx start

nginx 配置文件存储在 /etc/nginx/nginx.conf

让我们创建一个简单的配置文件,不带任何保护,仅用于负载均衡后端 IIS。

user  nginx;
worker_processes  4;  # 2 * Number of CPUs
events {
  # It's the key to high performance - have a lot of connections available
  worker_connections  19000;
}
# Each connection needs a filehandle (or 2 if you are proxying)
# Total amount of users you can serve = worker_processes * worker_connections
worker_rlimit_nofile    40000;

http {
  upstream aspnetservers {
    server 10.187.147.99:80 max_fails=3 fail_timeout=30s;
    server 10.187.147.100:80 max_fails=3 fail_timeout=30s;
    keepalive 32; # keep some connections alive to save connection open time
  }
  server {
    listen 80;
    server_name mynginx.com www.mynginx.com;
    location / {
       proxy_pass http://aspnetservers;
      add_header       X-Upstream      $upstream_addr;
    }
  }

这里需要记住的关键点

  • http {} - 这定义了所有服务器、域、URL 的所有 http 服务的通用行为。在这里,您可以为 nginx 处理的任何 http 流量定义通用配置。这就像全局配置。
  • upstream {} - 创建后端 IIS 服务器的命名组。
  • server {} - 定义监听 IP 和端口并支持特定主机标头的单个虚拟主机。这类似于 IIS 上的网站。
  • location {} - 定义 URL 模式以及如何处理它们。模式定义为正则表达式,并且仅匹配 URI 路径部分,没有查询字符串可用于匹配。

在接下来的部分中,我将说“将此放入 http 中”、“将该项放在 server 中”,您需要理解它指的是哪个范围。

设置完 nginx.conf 后,您应该 查看本文,以确保您没有进行任何可能打开安全漏洞的配置。

阻止拒绝服务攻击

让我们看看各种拒绝服务攻击的类型以及如何阻止它们。

Slowloris 攻击

Slowloris 打开许多连接,并非常缓慢地发送请求头和请求体。它在发送的每个字节之间都加入延迟,让 IIS 认为它是一个合法的客户端,只是响应缓慢。IIS 将保持所有这些连接打开,直到耗尽连接。Slowloris 攻击可以通过使用一个广泛可用的免费工具来执行。

为了阻止 slowloris 攻击,我们需要拒绝发送慢速请求。将此放在 http 部分

# Timeouts, do not keep connections open longer then necessary to reduce
# resource usage and deny Slowloris type attacks.
client_body_timeout      5s; # maximum time between packets the client can pause 
                             # when sending nginx any data
client_header_timeout    5s; # maximum time the client has to send the entire header to nginx
keepalive_timeout       75s; # timeout which a single keep-alive client connection will stay open
send_timeout            15s; # maximum time between packets nginx is allowed to pause 
                             # when sending the client data

阻止过多连接导致拒绝服务

某些形式的 DOS 攻击是通过打开过多连接并耗尽负载均衡器或 IIS 级别的可用连接来执行的。因此,我们需要为每个 IP 实现最大开放连接数限制。在执行此操作时,我们需要有一些白名单机制,以便我们不对需要打开许多连接的客户端施加限制。

没有任何连接限制,您可以产生大量负载并耗尽服务器 CPU。这是打开过多连接、发送过多请求并导致 CPU 非常高的示例

图:发送过多请求

IIS 上的 CPU 占用率非常高。

让我们限制每个 IP 可以打开的连接数。这将有助于减轻服务器负载。让我们将此放在 http 部分

geo $whitelist {
       default          0;
       # CIDR in the list below are not limited
       1.2.3.0/24       1;
       9.10.11.12/32    1;
       # All private IP ranges
       127.0.0.1/32     1;
       10.0.0.0/8       1;
       172.16.0.0/12    1;
       192.168.0.0/16   1;
    }
    map $whitelist $limit {
        0     $binary_remote_addr;
        1     "";
    }
    # The directives below limit concurrent connections from a
    # non-whitelisted IP address
    limit_conn_zone      $limit    zone=connlimit:10m;
    limit_conn           connlimit 20;
    limit_conn_log_level warn;   # logging level when threshold exceeded
    limit_conn_status    503;    # the error code to return

一旦我们部署了这个,客户端就会开始从 nginx 收到大量的 503 错误

我们可以看到许多请求现在失败了,这是个好消息!但 IIS 仍然过载

当如此多的请求在 nginx 中被拒绝时,IIS 应该消耗更少的 CPU,不是吗?

原因是,即使有连接限制,每秒仍有超过 600 个请求到达 IIS,这足以使服务器崩溃。我们已经对每个 IP 的开放连接数施加了限制,但没有对 IP 在开放连接上可以进行的请求数施加限制。每个 IP 可以通过少量开放连接发送大量请求。我们真正需要的是在 nginx 上强制执行每秒请求数限制。让我们在下一节中这样做。

阻止高请求/秒和暴力破解攻击

大多数暴力破解攻击要么通过发送许多并行请求,要么通过在少量开放连接上发送许多后续请求来执行。DOS 攻击也针对非常小的 URL 或服务执行,但可以非常频繁地访问,以生成高每秒请求数并导致过度的 CPU 消耗。为了保护我们的系统免受暴力破解和此类 DOS 攻击的影响,我们需要对每个 IP 强制执行每秒请求数限制。以下是您可以在 nginx 中执行此操作的方法

    # Allow N req/sec to non-whitelisted IPs with a burst of another 10.
    limit_req_zone       $limit   zone=one:10m  rate=50r/s;
    limit_req            zone=one burst=10;
    limit_req_log_level  warn;
    limit_req_status     503;

部署速率限制后,我们可以看到 IIS 的 CPU 已恢复到正常水平

我们还可以看到到达 IIS 的成功请求数低于每秒 50 个请求的限制。

您可能会想,为什么不为每个 IIS 服务器设置每秒最大请求数限制呢?那样的话,只要有一个 IP 消耗了该每秒请求数限制,您就会导致所有人中断。

白名单和黑名单 IP

这非常简单,只需在 http 作用域内添加“deny <ip>;”语句即可完全阻止 IP。如果将其放在 server 块内,则仅该虚拟主机会被阻止。您也可以在特定位置阻止特定 IP。

location / {
    deny  192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    allow 2001:0db8::/32;
    deny  all;
}

如果您想阻止所有人并白名单特定 IP,请添加一些“allow <ip>”然后以“deny all;”结尾

防止带宽耗尽

有人可以通过超出每月带宽配额来进行 DOS 攻击。这通常是通过反复下载大文件来完成的。由于上传带宽有限,但下载带宽通常不受限制,因此有人可以获得廉价的 VM 并访问服务器,轻易耗尽其每月带宽配额。因此,我们需要限制每个 IP 的网络传输速率。

首先,将此放在 http 作用域

    # zone to hold all remote ip
    limit_conn_zone $binary_remote_addr zone=addr:10m;

这会将限制应用于以 /Download 结尾的任何路径。例如,/Home/Index/Download 将受到此限制。假设您有一个提供文件的控制器

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";
            return View();
        }
        public FileResult Download()
        {
            var path = ConfigurationManager.AppSettings["Video"];
            return File(path,
                MimeMapping.GetMimeMapping(path), Path.GetFileName(path));
        }

Nginx 将保护此类易受攻击的控制器。

让我们更深入地了解 nginx 配置。Limit_rate_after 表示前 200k 字节享受全速,超过这个数量将限制为 100k/秒。此外,使用 limit_conn addr,它只允许对该 URL 进行 2 次并行访问。如果您不添加最后一个,那么有人可以打开许多并行连接并以 100k/秒的速度下载,仍然会耗尽您的带宽。

您也应该对此类静态文件设置限制
        # Static URLs
        location ~* \.(mp4|flv|css|bmp|tif|ttf|docx|woff2|js|pict|tiff|eot|xlsx|jpg|
        csv|eps|woff|xls|jpeg|doc|ejs|otf|pptx|gif|pdf|swf|svg|ps|ico|pls|midi|svgz|
        class|png|ppt|mid|webp|jar)$ {
            limit_rate_after 200k;
            limit_rate       100k;
            limit_conn addr 2; # beware of ISP proxies
}

在 nginx 服务器上缓存静态文件

Nginx 非常擅长缓存文件,然后无需将请求发送到后端服务器即可提供这些文件。您可以使用 nginx 为您的网站提供静态文件,并根据需要生成自定义的过期和缓存标头。

首先,将此放在 http 作用域

proxy_cache_path  /var/nginx/cache  levels=1:2  keys_zone=STATIC:10m inactive=24h  max_size=1g;

然后将其放在 server 作用域内

# Static URLs
        location ~* \.(css|bmp|tif|ttf|docx|woff2|js|pict|tiff|eot|xlsx|
                       jpg|csv|eps|woff|xls|jpeg|doc|ejs|otf|pptx|gif|pdf|
                       swf|svg|ps|ico|pls|midi|svgz|class|png|ppt|mid|webp|jar)$ {
            proxy_cache STATIC;
            expires 2d;
            add_header X-Proxy-Cache $upstream_cache_status;
            proxy_ignore_headers "Set-Cookie";
            proxy_hide_header "Set-Cookie";
            proxy_cache_valid 200 302  2d; # deploy changes on Sat, get fresh on Mon
            proxy_cache_valid 301      1h;
            proxy_cache_valid any      1m; # cache those nasty 404s
            proxy_pass http://aspnetservers;
        }

所有这些 ignore_headerhide_headercache_valid 选项对于此缓存业务正常运行是必需的。否则缓存会出问题。这是 nginx 的一个领域,我花了很长时间才使其正常工作。我从许多文章和 stackoverflow 文章中查找了各种问题的解决方法,最终才得到您在此处看到的参数组合。

您可以使用 curl 进行测试

>  curl -I -X GET http://mynginx.com/loadtest/Content/bootstrap.css
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 28 Jul 2016 15:57:44 GMT
Content-Type: text/css
Content-Length: 120502
Connection: keep-alive
Vary: Accept-Encoding
Last-Modified: Wed, 20 Jul 2016 22:18:33 GMT
ETag: "37e47a7d4e2d11:0"
Expires: Sat, 30 Jul 2016 15:57:44 GMT
Cache-Control: max-age=172800
X-Proxy-Cache: HIT
Accept-Ranges: bytes

响应显示 X-Proxy-Cache:HIT,表示它已从 nginx 提供服务而未命中 IIS。如果是 MISS,则会转到 IIS。首次命中时,您将获得 MISS,但后续命中应为 HIT。

卸载大文件传输

您可以将视频、PDF、文档等大文件存储在 nginx 服务器的存储中,然后在特定 URL 被访问时直接提供这些文件。例如,以下配置显示了如何从 nginx 服务器提供内容

        #location ~* \.(mp4|flv)$ {
        location ~* /Download$ {
            # files can be read and sent using multi-threading (1.7.11), 
            # without blocking a worker process:
            sendfile       on;
            aio            threads;
            limit_rate_after 200k;
            limit_rate       100k;
            limit_conn addr 2; # beware of ISP proxies
            expires 2d;
            root               /var/nginx/cache/video; # path where nginx stores files
            error_page         404 = @fetch;
        }

它在您在 root 中定义的本地路径中查找文件。例如,当您访问 URL /Home/Index/Download 时,它会读取文件 /var/nginx/cache/video/Home/Index/Download。如果您使用了 mp4 模式,然后访问 URL /Home/video.mp4,它将查找文件 /var/nginx/cache/video/Home/video.mp4

但如果这是文件第一次被访问,并且 nginx 还没有将其缓存到本地存储怎么办?然后会产生 HTTP 404,然后会重定向到以下 location 规则

    location @fetch {
            internal;
            add_header X-Proxy-Cache $upstream_cache_status;
            proxy_ignore_headers "Set-Cookie";
            proxy_hide_header "Set-Cookie";
            proxy_cache_valid 200 302  2d; # deploy changes on Sat, get fresh on Mon
            proxy_cache_valid 301      1h;
            proxy_cache_valid any      1m; # cache those nasty 404s
            proxy_pass         http://aspnetservers;
            proxy_store        on;
            proxy_store_access user:rw group:rw all:r;
            proxy_temp_path    /var/nginx/cache/video;
            root               /var/nginx/cache/video;
        }

第一个块与缓存 css、js 等静态文件相同。但是,重要部分是 proxy_store 块,它告诉 nginx 将从后端服务器接收的文件永久保存到该路径。

卸载昂贵的 gzip 压缩

这有争议。您是将所有 gzip 压缩负载放在您的中心 nginx 服务器上(这是一个单点故障),还是应该将 gzip 负载分发到您的 IIS 服务器上?这取决于哪个成本最低。如果您发现 IIS 在处理 CPU 密集型应用程序时消耗过多 CPU,那么您可能希望将其卸载到 nginx。否则,将其保留在 IIS 上,让 nginx 保持空闲。

将此块放在 http 作用域中以在所有请求中启用 gzip 压缩。您也可以在 server 或 location 级别执行此操作

    # Compression
    # Enable Gzip compressed.
    gzip on;
    gzip_disable "msie6";
    # Enable compression both for HTTP/1.0 and HTTP/1.1.
    gzip_http_version  1.1;
    # Compression level (1-9).
    # 5 is a perfect compromise between size and cpu usage, offering about
    # 75% reduction for most ascii files (almost identical to level 9).
    gzip_comp_level    5;
    # Don't compress anything that's already small and unlikely to shrink much
    # if at all (the default is 20 bytes, which is bad as that usually leads to
    # larger files after gzipping).
    gzip_min_length    256;
    # Compress data even for clients that are connecting to us via proxies,
    # identified by the "Via" header (required for CloudFront).
    gzip_proxied       any;
    # Tell proxies to cache both the gzipped and regular version of a resource
    # whenever the client's Accept-Encoding capabilities header varies;
    # Avoids the issue where a non-gzip capable client (which is extremely rare
    # today) would display gibberish if their proxy gave them the gzipped version.
    gzip_vary          on;
    # Compress all output labeled with one of the following MIME-types.
    gzip_types
      application/atom+xml
      application/javascript
      application/json
      application/rss+xml
      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/x-component;
    # text/html is always compressed by HttpGzipModule

.NET 特定保护

防止 ASP.NET 和 WCF 遭受大型 POST 请求

首先,您应该为站点设置一个较低的全局 POST 限制,以免意外留下一些易受攻击的端点让某人发布大量数据。您应该将此放在 http 作用域

    client_max_body_size       100k; # Start with small, increase where needed
    client_body_buffer_size    128k;
    client_body_in_single_buffer on;
    client_body_temp_path      /var/nginx/client_body_temp;
    client_header_buffer_size    1k;
    large_client_header_buffers  4 4k;

然后,您可以在特定 URL 上定义更高的限制。例如,假设您实现了一个上传控制器操作

       [HttpPost]
        public ActionResult Index(HttpPostedFileBase file)
        {
            if (file.ContentLength > 0)
            {
                var fileName = Path.GetFileName(file.FileName);
                var path = Path.Combine(Server.MapPath("~/App_Data/"), fileName);
                file.SaveAs(path);
            }
            return RedirectToAction("Index");
        }

您想在此 URL 上允许高达 500KB 的上传

        # Upload URL with special limit
        location ~* /Home/Index$ {
            client_max_body_size       500k;
            proxy_pass http://aspnetservers;
        }

同样,假设您有 WCF/ASMX Web 服务,您想允许高达 200 KB 的 POST 请求

        location ~* \.(svc|asmx)$ {
            client_max_body_size       200k;
            proxy_pass http://aspnetservers;
        }

禁用 WCF 服务 WSDL/Mex 端点

您应该禁用 WCF 服务 WSDL/Mex 端点,以免他人窥探可用的服务。这使得黑客更容易看到服务的定义,然后识别可以利用的内容。通过在生产服务器上隐藏定义,您可以让他们更难工作。

您可以收听这个很棒的讲座“Attacking WCF”。

        # WCF usual mex endpoint URL
        location ~* \.svc\/mex$ {
            return 444 break;
        }
        # All other WCF hits
        location ~* \.(svc|asmx)$ {
            client_max_body_size       200k;
            # Suppress WSDL
            if ( $args ~* wsdl ) {
                return 444 break;
            }
            proxy_pass http://aspnetservers;
        }

从错误页面隐藏堆栈跟踪

您应该从错误页面或 JSON/XML 服务的错误响应中隐藏堆栈跟踪。堆栈跟踪会泄露应用程序的内部信息,从而帮助攻击者。您希望让攻击者的生活更困难,并浪费尽可能多的时间。一个简单的方法是捕获 IIS 生成的所有 HTTP 500,并返回一些假的错误消息。试图通过从每个端点生成错误来查找应用程序信息的攻击者将浪费大量时间,然后才能发现这些都是假的。

server {
     listen 80;
     server_name mynginx.com www.mynginx.com;

     error_page 500 502 503 504 = /500.html;
     proxy_intercept_errors on;

     location /500.html {
         return 500 "{\"Message\":\"An error has occurred.\",\"ExceptionMessage\":
         \"An exception has occurred\",\"ExceptionType\":\"SomeException\",\"StackTrace\": null}";
      }

隐藏 IIS 和 ASP.NET 标头

您应该隐藏 IIS 和 ASP.NET 标头,以免他人得知运行的版本并利用漏洞。您可以在 http 作用域中定义此项

# Remove nginx version
server_tokens off;
# Remove ASP.NET headers
more_clear_headers 'X-Powered-By';
more_clear_headers 'X-AspNet-Version';
more_clear_headers 'X-AspNetMvc-Version';

在您所有的“脏内裤”被暴露之前

HTTP/1.1 200 OK
Server: nginx/1.11.2
Date: Thu, 28 Jul 2016 16:58:49 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 3254
Connection: keep-alive
Vary: Accept-Encoding
Cache-Control: private
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET

操作后

HTTP/1.1 200 OK
Server: nginx
Date: Mon, 25 Jul 2016 08:54:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 3062
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private

请注意,我保留了 Server: nginx 标头,但隐藏了版本号。这是一个威慑。一旦攻击者看到您足够聪明地使用 nginx,他们就会退缩,因为他们知道他们不能轻易破解 nginx。他们会意识到,破解您的网站需要更多的预算,因为 nginx 会使拒绝服务变得非常困难。所以,请自豪地向所有人展示您的 nginx。

在浏览器中缓存 ASMX 和 WCF Ajax 代理,节省重复请求

您应该在浏览器中缓存 ASMX 和 WCF Ajax 代理,并节省重复请求。这是最有效的优化技术之一,因为如果您不这样做,每次页面加载时,浏览器都会向服务器发送请求来下载 Ajax 代理。这是一种糟糕的流量浪费,也是导致 ASP.NET AJAX 网站加载缓慢的主要原因之一。

如果您不这样做,每次访问 .svc/js .asmx/js URL 时,您将不会收到缓存标头

HTTP/1.1 200 OK
Cache-Control: public
Content-Length: 2092
Content-Type: application/x-javascript
Expires: Thu, 28 Jul 2016 17:01:51 GMT
Last-Modified: Thu, 28 Jul 2016 17:01:51 GMT
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 28 Jul 2016 17:02:41 GMT

带有当前日期时间的 Expires 标头会破坏所有缓存。

现在将此配置放入 nginx 配置中

# WCF AJAX Proxies
location ~* \.(svc|asmx)\/js$ {
     expires 1d;
     more_clear_headers 'Expires';
     proxy_pass http://aspnetservers;
}

然后您将获得漂亮的缓存标头

Cache-Control:max-age=86400
Connection:keep-alive
Content-Length:2096
Content-Type:application/x-javascript
Date:Wed, 27 Jul 2016 07:59:20 GMT
Last-Modified:Wed, 27 Jul 2016 07:59:20 GMT
Server:nginx

浏览器显示它正在从缓存加载 ajax 代理

保护 IIS 免受范围攻击 CVE-2015-1635

这是一个老漏洞。大多数 IIS 都已经打了补丁。但您仍应在 nginx 上启用它以保护易受攻击的 IIS

map $http_range $saferange {
        "~\d{10,}" "";  # if it matches a string of 10 or more integers, remove it
        default $http_range;
    }
    proxy_set_header Range $saferange;

这可以防止有人发送一个具有非常大值的 range 标头并导致 IIS 崩溃!

阻止 AntiXSS 和注入攻击

Nginx 有一个强大的模块 Naxsi,它提供 AntiXSS 和注入攻击防护。您可以在 Naxsi 网站上阅读有关它的所有信息。

使用 Google 的 PageSpeed 模块提升站点性能

Google 为 nginx 提供了一个 PageSpeed 模块,该模块执行大量自动优化。它拥有一长串功能,可以利用这些功能来显著提高性能。 示例和演示在这里

结论

除非您使用的是商业版 NGINX PLUS,否则我只会将开源免费 NGINX 用作缓存代理来缓存静态文件,特别是提供大文件。对于其他所有内容,我会使用 HAProxy,因为它具有更好的监控控制台、健康检查、负载均衡控制、粘性会话、检查内容正文、TCP 检查等。另一方面,nginx 有一个强大的 Web 应用程序防火墙模块 Naxsi,它可以阻止 AntiXSS 和许多类型的注入攻击。因此,为了最大限度地提高性能、监控、保护和可用性,您需要结合使用 nginx 和 haproxy。

完整的 nginx.conf

user  nginx;
worker_processes  4;  # 2 * Number of CPUs
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  19000;  # It's the key to high performance - have a lot of connections available
}
worker_rlimit_nofile    40000;  # Each connection needs a filehandle (or 2 if you are proxying)
# Total amount of users you can serve = worker_processes * worker_connections
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format full '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time '
                    '$upstream_addr '
                    '$upstream_status '
                    '$upstream_connect_time '
                    '$upstream_header_time '
                    '$upstream_response_time';
    access_log  /var/log/nginx/access.log  full;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/blacklist.conf;
   ################# SSLv3 POODLE vulnerability ##############
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    #################### Block slow clients - slowris attacks ###############
    # Timeouts, do not keep connections open longer then necessary to reduce
    # resource usage and deny Slowloris type attacks.
    client_body_timeout      5s; # maximum time between packets the client can pause 
                                 # when sending nginx any data
    client_header_timeout    5s; # maximum time the client has to send the entire header to nginx
    keepalive_timeout       75s; # timeout which a single keep-alive client connection will stay open
    send_timeout            15s; # maximum time between packets nginx is allowed to pause 
                                 # when sending the client data
    ################### Block large POST ###################
    client_max_body_size       100k; # Start with small, increase where needed
    client_body_buffer_size    128k;
    client_body_in_single_buffer on;
    client_body_temp_path      /var/nginx/client_body_temp;
    client_header_buffer_size    1k;
    large_client_header_buffers  4 4k;
    ######## Defend against IIS range attack CVE-2015-1635 ########
    map $http_range $saferange {
        "~\d{10,}" "";  # if it matches a string of 10 or more integers, remove it
        default $http_range;
    }
    proxy_set_header Range $saferange;
    #################### Performance optimizations #################
    # Compression
    # Enable Gzip compressed.
    gzip on;
    gzip_disable "msie6";
    # Enable compression both for HTTP/1.0 and HTTP/1.1.
    gzip_http_version  1.1;
    # Compression level (1-9).
    # 5 is a perfect compromise between size and cpu usage, offering about
    # 75% reduction for most ascii files (almost identical to level 9).
    gzip_comp_level    5;
    # Don't compress anything that's already small and unlikely to shrink much
    # if at all (the default is 20 bytes, which is bad as that usually leads to
    # larger files after gzipping).
    gzip_min_length    256;
    # Compress data even for clients that are connecting to us via proxies,
    # identified by the "Via" header (required for CloudFront).
    gzip_proxied       any;
    # Tell proxies to cache both the gzipped and regular version of a resource
    # whenever the client's Accept-Encoding capabilities header varies;
    # Avoids the issue where a non-gzip capable client (which is extremely rare
    # today) would display gibberish if their proxy gave them the gzipped version.
    gzip_vary          on;
    # Compress all output labeled with one of the following MIME-types.
    gzip_types
      application/atom+xml
      application/javascript
      application/json
      application/rss+xml
      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/x-component;
    # text/html is always compressed by HttpGzipModule
    output_buffers   1 32k;
    postpone_output  1460;
    sendfile         on;
    sendfile_max_chunk 512k; # match with allowed download speed
    tcp_nopush       on;
    tcp_nodelay      on;
    #send_lowat       12000;
    ################### Edge cache #################
    proxy_cache_path  /var/nginx/cache  levels=1:2  keys_zone=STATIC:10m inactive=24h  max_size=1g;
       
    ################# Common Proxy configurations ##############
    proxy_redirect     off;
    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_connect_timeout      5;
    proxy_send_timeout         90;
    proxy_read_timeout         90;
    #proxy_send_lowat           12000;
    proxy_buffering on;
    proxy_buffer_size          4k;
    proxy_buffers              4 32k;
    proxy_busy_buffers_size    64k;
    proxy_temp_file_write_size 64k;
    proxy_temp_path            /var/nginx/proxy_temp;
    # Use HTTP/1.1 for nginx to server connection for keepalive
    proxy_http_version 1.1;
    ########### Retry failed requests ###########
    # how to handle failure from one server and send request to another server
    proxy_next_upstream error http_502 http_503 http_504;
    proxy_next_upstream_timeout 30s;
    proxy_next_upstream_tries 1; # do not blow up all servers
    ############## Hide ASP.NET servers and nginx version #######
    # Remove nginx version
    server_tokens off;
    # Remove ASP.NET headers
    more_clear_headers 'X-Powered-By';
    more_clear_headers 'X-AspNet-Version';
    more_clear_headers 'X-AspNetMvc-Version';
    ################## IP Whitelisting & Rate limiting ############
    geo $whitelist {
       default          0;
       # CIDR in the list below are not limited
       1.2.3.0/24       1;
       9.10.11.12/32    1;
       # All private IP ranges
       127.0.0.1/32     1;
       10.0.0.0/8       1;
       #172.16.0.0/12    1;
       192.168.0.0/16   1;
    }
    map $whitelist $limit {
        0     $binary_remote_addr;
        1     "";
    }
    # The directives below limit concurrent connections from a
    # non-whitelisted IP address
    limit_conn_zone      $limit    zone=connlimit:10m;
    limit_conn           connlimit 20;
    limit_conn_log_level warn;   # logging level when threshold exceeded
    limit_conn_status    503;    # the error code to return
    # Allow N req/sec to non-whitelisted IPs with a burst of another 10.
    limit_req_zone       $limit   zone=one:10m  rate=50r/s;
    limit_req            zone=one burst=10;
    limit_req_log_level  warn;
    limit_req_status     503;
    # zone to hold all remote ip
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    ################# Upstream / backend servers ###############
    upstream aspnetservers {
        server 10.187.147.99:80 max_fails=3 fail_timeout=30s;
        #server 10.187.147.100:80 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }
    ############### Virtual hosts ####################
    server_names_hash_bucket_size 128;  # this seems to be required for some vhosts
    # Prevent direct IP hit
    server {
        listen      80 default_server;
        server_name "";
        return      444 break;
    }
    server {
        listen 80;
        server_name mynginx.com www.mynginx.com;
        error_page 500 502 503 504 = /500.html;
        proxy_intercept_errors on;
        location / {
            proxy_pass http://aspnetservers;
        }
        # Upload URL with special limit
        location ~* /Home/Index$ {
            client_max_body_size       500k;
            proxy_pass http://aspnetservers;
        }
        # WCF AJAX Proxies
        location ~* \.(svc|asmx)\/js$ {
             expires 1d;
             more_clear_headers 'Expires';
             proxy_pass http://aspnetservers;
        }
        # WCF usual mex endpoint URL
        location ~* \.svc\/mex$ {
            return 444 break;
        }
        # All other WCF hits
        location ~* \.(svc|asmx)$ {
            client_max_body_size       100k;
            # Suppress WSDL
            if ( $args ~* wsdl ) {
                return 444 break;
            }
            proxy_pass http://aspnetservers;
        }
        # Custom error handler
        location /500.html {
            return 500 "{\"Message\":\"An error has occurred.\",\"ExceptionMessage\":
            \"An exception has occurred\",\"ExceptionType\":\"SomeException\",\"StackTrace\": null}";
        }
        # Static URLs
        location ~* \.(css|bmp|tif|ttf|docx|woff2|js|pict|tiff|eot|xlsx|jpg|csv|eps|woff|
        xls|jpeg|doc|ejs|otf|pptx|gif|pdf|swf|svg|ps|ico|pls|midi|svgz|class|png|ppt|mid|webp|jar)$ {
            proxy_cache STATIC;
            expires 2d;
            #add_header Cache-Control "public";
            add_header X-Proxy-Cache $upstream_cache_status;
            proxy_ignore_headers "Set-Cookie";
            proxy_hide_header "Set-Cookie";
            proxy_cache_valid 200 302  2d; # deploy changes on Sat, get fresh on Mon
            proxy_cache_valid 301      1h;
            proxy_cache_valid any      1m; # cache those nasty 404s
            proxy_pass http://aspnetservers;
        }
        # Video
        #location ~* \.(mp4|flv)$ {
        location ~* /Download$ {
            # files can be read and sent using multi-threading (1.7.11), 
            # without blocking a worker process:
            sendfile       on;
            aio            threads;
            limit_rate_after 200k;
            limit_rate       100k;
            limit_conn addr 2; # beware of ISP proxies
            expires 2d;
            root               /var/nginx/cache/video;
            error_page         404 = @fetch;
        }
        location @fetch {
            internal;
            add_header X-Proxy-Cache $upstream_cache_status;
            proxy_ignore_headers "Set-Cookie";
            proxy_hide_header "Set-Cookie";
            proxy_cache_valid 200 302  2d; # deploy changes on Sat, get fresh on Mon
            proxy_cache_valid 301      1h;
            proxy_cache_valid any      1m; # cache those nasty 404s
            proxy_pass         http://aspnetservers;
            proxy_store        on;
            proxy_store_access user:rw group:rw all:r;
            proxy_temp_path    /var/nginx/cache/video;
            root               /var/nginx/cache/video;
        }
        location /status {
            # Turn on nginx stats
            stub_status on;
            # I do not need logs for stats
            access_log   off;
            # Security: Only allow access from 192.168.1.100 IP #
            #allow 192.168.1.100;
            # Send rest of the world to /dev/null #
            #deny all;
       }
    }
}
© . All rights reserved.