背景很重要

背景很重要

returnserver区块中

使用 Gunicorn、Nginx 和 HTTPS 安全部署 Django 应用使用以下 HTTP 到 HTTPS 重定向块:

# Redirect HTTP to HTTPS
server {
  server_name             .supersecure.codes;
  listen                  80;
  return                  307 https://$host$request_uri;
}

我将其解释为对 HTTP 标头匹配的端口 80 上的每个 HTTP 请求执行 307 重定向Host

returnlocation

Mozilla SSL 配置生成器使用以下块执行相同的 HTTP 到 HTTPS 重定向(例如这里):

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location / {
        return 301 https://$host$request_uri;
    }
}

我假设它被放在一个location块中,以防有人想通过 HTTP 提供其他东西(例如,静态资产),那么添加它的工作量就会减少。

returnif区块中

这是来自生产中的旧服务器:

server {
    
    if ($host = some.org) {
        return 301 https://$host$request_uri;
    }

    listen 80;
    server_name some.org;
    return 404;
}

我只能猜测为什么会被if阻止,所以我假设HostHTTP 标头明确检查了正确的域,如果不匹配,则返回 404。否则,它将被踢到server设置 TLS 的阻止。

使用这种形式有什么好处吗?

编辑:找到了Certbot 的 Nginx 插件导致重定向的安全问题在 Let's Encrypt 论坛上发帖,解释了区块return内部如何if更安全。仍然不明白具体原因,但这是一个开始。还有开放的certbot 问题 #9705问同样的事情。

编辑-2:解释为什么“裸露”return 301​​有害的相关段落发布在下面。(@艾萨·约基宁:我选择不回答的原因是因为我不明白这一点。)

Certbot 在 Nginx 中创建 HTTP 到 HTTPS 重定向的方式是通过以下配置指令:

return 301 https://$host$request_uri;

这样做的潜在问题是,它依赖于标头的值来 Host确定将请求重定向到何处。如果在 Nginx 配置中将这样的指令添加到 HTTP 流量的默认服务器块,则用户可以修改Host其请求的标头以将其重定向到任意域。就其本身而言,这不是问题,因为重定向到任意域只会发送给将该域放入Host其请求标头的用户。

如果攻击者可以欺骗某个缓存来存储来自 Nginx 的重定向,并将其提供给尝试通过正常方式使用未修改的Host标头连接到服务器的用户,则会出现问题。如果可以做到这一点,攻击者可以将来自该缓存用户的流量重定向到他们选择的域。

由于虚拟主机的盛行,能够执行此操作的缓存必须有自己的错误或配置错误。Certbot 添加的重定向本身无法利用,但当与其他具有自身错误、漏洞或配置错误的软件结合使用时,可能会成为安全问题。

答案1

背景很重要

这真的取决于你的需求

仅限/第一个/default_server

当你只有单个server {}TCP 端口 80 上的纯 HTTP 阻止,或者您已隐式/显式配置默认/全部捕获服务器 在还包含用于虚拟服务器的附加服务器块的配置中,服务器通常不仅处理您实际托管的站点的请求

该服务器还将处理对您的裸 IP 地址的请求,对您的 IP 地址的先前用户尚未清理但仍可解析为您的 IP 地址的 DNS 条目的请求,和/或可能存在通配符 DNS 条目。

您不太可能拥有针对您的 IP 地址、您从未拥有和/或不再托管的 DNS 名称的有效 TLS 证书,并且您可能没有与通配符 DNS 记录匹配的通配符 TLS 证书。

当您在该服务器中配置无条件参数化重定向时,例如:

 return 301 https://$host$request_uri;

它会将每个请求重定向到原始请求的 HTTPS 版本,即使对于您没有有效 TLS 证书的网站也是如此。

因此,这可能会导致网站访问者出现错误。

替代方法:

这是一个观点/风格和偏好的问题。

  1. 不要使用参数化重定向, 但重定向到特定网站,您确实拥有有效的 TLS 证书。

     return 301 https://www.example.com$request_uri;
    
  2. 有些人建议始终设置一个默认服务器,以便对所有不合格的请求返回通用错误。它们不会从该 default_server 重定向到其他地方。并明确创建一个或多个附加服务器来处理“真实访客”针对托管在该 Web 服务器上的实际网站。这样他们就不会向访问裸 IP 地址的访问者透露他们运营的网站。不同的服务器还允许您设置不同的日志文件,以跟踪您的不同服务器仍在处理多少个普通 HTTP 请求。

    server {
       listen 80 default_server; 
       server_name _; # This is just an invalid value which will never trigger on a real hostname.
    
        server_name_in_redirect off;
        # return a 404 error response to all unqualified requests 
        return 404;
    } 
    server {
        listen      80;
        server_name example.net www.example.net;
        # always redirect plain http to https 
        # and redirect bare domain to www 
        # if works as desired - change temporary to permanent redirect 
        return 301 https://www.example.net$request_uri;
    }    
    
  3. 您发布的块示例if与上面的示例大致相同,并且是一种过滤和重定向到仅对您具有 TLS 站点的站点的 HTTPS 请求的方法,但在单个服务器块中:

    server {
    
        if ($host = some.org) {
            return 301 https://$host$request_uri;
        }
    
        listen 80;
        server_name some.org;
        return 404;
    }
    

    注意:我个人不会在这个构造中使用参数化重定向,因为 nginxhttps:/some.org$request_uri;无论如何都会重定向到。

附加服务器

server {
   #default server
}
server {
   #first additional virtual server
}
server {
   #second additional virtual server
}

当您已经拥有包含用于虚拟服务器的附加服务器块的配置时,仅当网站访问者发送正确的Host:标头时才会使用这些服务器块,并且这些服务器不必处理不合格的请求。

在附加/虚拟服务器中,if ($host = some.org)条件重定向将完全是多余的,因为该虚拟服务器无论如何只会处理针对该特定主机的请求。

server {
   #default server
}
server {
   #first virtual server
   listen 80;
   server_name example.org;

   # Although valid syntax logically this "if" construct is completely redundant here
   # this server will only process requests for example.org anyway
   if ($host = example.org) {
       return 301 https://$host$request_uri;
   }
    
server {
   #second virtual server
   listen 80;
   server_name example.com www.example.com;

   # this redirects www.example.com to https://www.example.com
   # this redirects example.com to https://example.com 
   # that only makes sense when those are either completely different sites
   # OR they are one and the same. 

   # The parameterised redirect doesn't make sense when subsequently
   #  https://example.com redirects to https://www.example.com or vice-versa

   return 301 https://$host$request_uri;
   
   # The above  is functionally completely equivalent to 
   # enclosing the redirect in a location block as done below

   location / {
       return 301 https://$host$request_uri;
   }
        

相关内容