使用 Nginx 阻止未发送到我的域的连接

使用 Nginx 阻止未发送到我的域的连接

我在 AWS elastic beanstalk 上运行一个 django 应用程序,并收到试图扫描漏洞的机器人的垃圾邮件。这导致大量错误,例如:

(其中 xx.xxx.xx.xx 是我的 ec2 实例的 IP 地址。)

DisallowedHost at //www/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
Invalid HTTP_HOST header: 'xx.xxx.xx.xx'. You may need to add 'xx.xxx.xxx.xx' to ALLOWED_HOSTS.

我的合法用户只能使用域名访问网站。我一直在尝试弄清楚如何修改我的 nginx 配置以阻止所有未发送到 *.mydomain.com 或 mydomain.com 的连接。

我根据需要动态地添加和删除子域,因此我为子域做了一个通配符。

AWS Elastic beanstalk 为我生成以下默认配置文件:

/etc/nginx/nginx.conf

#Elastic Beanstalk Nginx Configuration File

user                    nginx;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
worker_processes        auto;
worker_rlimit_nofile    32788;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    include       conf.d/*.conf;

    map $http_upgrade $connection_upgrade {
        default     "upgrade";
    }

    server {
        listen        80 default_server;
        access_log    /var/log/nginx/access.log main;

        client_header_timeout 60;
        client_body_timeout   60;
        keepalive_timeout     60;
        gzip                  off;
        gzip_comp_level       4;
        gzip_types text/plain text/css application/json application/javascript $

        # Include the Elastic Beanstalk generated locations
        include conf.d/elasticbeanstalk/*.conf;
    }
}

然后我用这个文件来扩展它:

.platform\nginx\conf.d\elasticbeanstalk\00_application.conf

location / {
    set $redirect 0;
    if ($http_x_forwarded_proto != "https") {
        set $redirect 1;
    }
    if ($redirect = 1) {
        return 301 https://$host$request_uri;
    }   

    proxy_pass        http://127.0.0.1:8000;
    proxy_http_version  1.1;

    proxy_set_header    Connection         $connection_upgrade;
    proxy_set_header    Upgrade            $http_upgrade;
    proxy_set_header    Host               $host;
    proxy_set_header    X-Real-IP          $remote_addr;
    proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
    
    gzip on;
    gzip_comp_level 4;
    gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    client_max_body_size 2000M;
}

location = /health-check.html {
    set $redirect 0;
    if ($http_x_forwarded_proto != "https") {
        set $redirect 1;
    }
    if ($http_user_agent ~* "ELB-HealthChecker") {
        set $redirect 0;
        return 204;
    }
    if ($redirect = 1) {
        return 301 https://$host$request_uri;
    }   

    proxy_pass        http://127.0.0.1:8000;
    proxy_http_version  1.1;

    proxy_set_header    Connection         $connection_upgrade;
    proxy_set_header    Upgrade            $http_upgrade;
    proxy_set_header    Host               $host;
    proxy_set_header    X-Real-IP          $remote_addr;
    proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

该覆盖文件的目的是使得 nginx 将 http 重定向到 https 并响应 ELB 健康检查。

我不太熟悉 nginx 或 elastic beanstalk,但从研究此问题时我所能收集到的信息来看,我需要让我的默认服务器连接返回 444,然后有一个单独的服务器块,其中 server_name 设置为我的域。

这是处理此问题的正确方法吗?它可以与通配符子域名一起使用吗?

谢谢

答案1

看来您的唯一虚拟主机是具有default_server属性的虚拟主机。这意味着如果未找到匹配的虚拟主机,则使用该块来处理请求。

为了妥善处理您的案件,您需要:

  1. serverdefault_server指令中的块listen。此块应仅包含return 404;或。您可能还想在此块中return 444;关闭。access_log
  2. server用 块server_name example.com *.example.com;。此虚拟主机应包含您的实际应用程序。

请注意,这是完全控制 nginx 配置时配置的方式。我不知道 Elastic Beanstalk 是否具有一些以这种方式自动生成配置文件的功能。

答案2

server分配一个用于处理垃圾邮件的指令的解决方案default_server是相当错误的。

从根本上讲,这是不正确的,因为您的网站无法与不支持 SNI 的客户端兼容。这些客户端无法协商通过 TLS 连接请求的域名,因此 NGINX 将使用您配置为 的服务器default_server。然后,当然,他们将无法获得任何东西,因为它们将进入您的“垃圾邮件处理程序”服务器。

尽管如今此类客户端的普及率可能可以忽略不计,但您肯定不希望垃圾邮件处理以任何方式影响到您真正的网站访问者。

我已经在文章中介绍了如何正确处理请求的无效域名“NGINX 蜜罐 – 阻止机器人的最简单、最快捷的方法”

如果您不打算实施蜜罐方法(在无效请求时在防火墙中触发阻止),而只是想拒绝这些请求,那么就需要在map您的服务器上放置一个包含所有有效域名的域名,然后if在您现有的域名旁边添加一个条件server,该条件具有default_server

在 中/etc/nginx/nginx.conf,设置一个列出您所有网站域名的地图:

map $http_host $default_host_match {
    example.com 1;
    *.example.com 1;
    default 0;
}

在默认服务器的配置中,阻止对无效域的请求:

server {
    if ($default_host_match = 0) {
        return 410;
    }
    server_name example.com *.example.com default_server;

410 是我个人的偏好,你可以使用任何你认为适合你情况的。410 已消失是:

客户端错误响应代码表示源服务器不再能够访问目标资源,并且这种情况很可能是永久性的。

这种方法适用于 SNI 客户端,因为服务器块的选择由 NGINX 完成TLS 连接已建立,为了检查请求域的有效性,我们使用Host:HTTP 标头。

处理机器人流量对于安全性来说至关重要,并且非常值得推荐性能。为什么要性能?很多时候,请求会导致您的后端被不必要地调用,这会占用 CPU 时间并将其从有效客户端那里窃取。

如果实施“蜜罐”方法(第一个无效请求触发防火墙拦截),则给定 IP 地址将不再可能发出其他无效请求。这既减少了攻击面,也减少了由此造成的潜在负载。

答案3

Tero 的回答非常棒而且正确,但我想补充一些内容。

首先,如果您允许来自您无法控制的 IP 的连接,并将您的域名发布在任何地方、可发现或可猜测的地方,那么您就会收到这些攻击。

禁止承载错误主机的连接是个好主意,但不一定是为了这个特定的安全原因。通常,这样做是为了在设置指向同一主机的新域时,不会在配置 Web 服务器时无意中在新域上提供错误的网站。

其次,我不认为这是一个“问题”。担心这些攻击的主要原因是您的软件是否过时,第二个原因是零日漏洞。任何有能力的系统管理员都不必担心第一个原因,而且仅允许识别的主机头根本无法阻止这两种攻击。


正如 TooTea 在评论中指出的那样,如果目的是阻止这些虚假请求到达昂贵的支持服务,而这些支持服务必须想办法拒绝这些请求,那么在廉价的反向代理级别拒绝这些请求仍然行不通。这些爬虫会尝试攻击,因为对他们来说这基本上是免费的。例如,我的静态 HTML 网站不断收到以 wordpress 为中心的攻击,而我基于 Django 的抵押贷款模拟器也受到以 wordpress 为中心的攻击。(不要使用 wordpress)。

更好的解决方案是在反向代理中定义一组对于给定服务合法的路径/路径模式,并返回与模式不匹配的 444(或 tarpit)请求。一旦设置好,这是一个很好的解决方案,但确实需要服务中的一些机制来输出合法的路径模式,然后其他一些机制在部署时抓取该输出,更新反向代理配置并重新加载配置。

就我个人而言,我不会担心做这样的事情,除非你计算出了使用你的支持服务满足这些请求所浪费的美元成本,与开发人员研究/编写机器的实际美元成本相比。......或者如果你有自己的时间,你只是想为了好玩而做这件事,我也很尊重这一点。

相关内容