9

我的应用在 Google Compute Engine 上运行。Nginx 用作代理服务器。Nginx 被配置为使用 SSL。以下是 /etc/nginx/sites-available/default 的内容:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name mywebapp.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    include snippets/ssl-mywebapp.com.conf;
    include snippets/ssl-params.conf;
    root /home/me/MyWebApp/wwwroot;
    location /.well-known/ {
    }
    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

在 Startup.cs 我有:

app.UseGoogleAuthentication(new GoogleOptions()
{
    ClientId = Configuration["Authentication:Google:ClientId"],
    ClientSecret = Configuration["Authentication:Google:ClientSecret"],
});

现在在 Google Cloud Platform 中,我需要指定授权重定向 URI。如果我输入以下内容,我的 Web 应用程序将按预期工作:

http://mywebapp.com/signin-google

但是,如果使用它将不起作用https;浏览器显示以下错误:

The redirect URI in the request, http://mywebapp.com/signin-google, does
not match the ones authorized for the OAuth client.

在这种情况下,使用 http 作为授权重定向 uri 是否安全?如果我希望它是 https,我需要什么配置?

4

2 回答 2

24

发生这种情况是因为您在反向代理服务器后面运行的应用程序不知道最初的请求是通过 HTTPS 来的。

SSL/TLS 终止代理

问题中描述的反向代理的配置称为 SSL/TLS 终止反向代理。这意味着在客户端和代理服务器之间建立了安全流量。代理服务器解密请求,然后通过 HTTP 协议将其转发给应用程序。

此配置的问题在于其背后的应用程序不知道客户端通过 HTTPS 发送请求。因此,当涉及到重定向到自身时,它使用HttpContext.Request.Scheme,HttpContext.Request.HostHttpContext.Request.Port为重定向构建一个有效的 URL。

X-Forwarded-* HTTP 标头

这就是X-Forwarded-*标题发挥作用的地方。为了让应用程序知道请求最初是通过 HTTPS 通过代理服务器发出的,我们必须配置代理服务器以设置X-Forwarded-ForX-Forwarded-ProtoHTTP 标头。

location / {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection keep-alive;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

好的,现在如果我们回到 ASP.NET Core 应用程序并查看传入的 HTTP 请求,我们将看到X-Forwarded-*设置了两个标头,但是重定向 URL 仍然使用 HTTP 方案。

转发标头中间件

基本上,这个中间件会覆盖HttpContext.Request.Scheme和适当地HttpContext.Connection.RemoteIpAddress提供由X-Forwarded-ProtoX-Forwarded-For标头提供的值。为了实现它,让我们通过在Startup.Configure()方法开头的某处添加以下行来将其添加到管道中。

        var forwardedHeadersOptions = new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
            RequireHeaderSymmetry = false
        };
        forwardedHeadersOptions.KnownNetworks.Clear();
        forwardedHeadersOptions.KnownProxies.Clear();

        app.UseForwardedHeaders(forwardedHeadersOptions);

这最终应该使您的应用程序使用 HTTPS 方案构造有效的 URL。

我的故事

上面的代码看起来与微软建议的不同。如果我们看一下文档,他们的代码看起来会短一些:

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
     ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

然而,这对我不起作用。同样根据这个问题下的评论,我并不孤单。

我有一个 nginx 设置为在 Docker 容器中运行的 ASP.NET Core 应用程序的反向代理。在我将所有内容都放在 Amazon 负载均衡器 (ELB) 之后,情况变得更加复杂。

我首先遵循了文档中的建议,但它对我不起作用。我的应用程序中收到以下警告:

X-Forwarded-For 和 X-Forwarded-Proto 之间的参数计数不匹配

然后我查看了我的X-Forwarded-*标题并意识到它们的长度不同。X-Forwarded-For标头包含 2 条记录(逗号分隔的 IP 地址),而X-Forwarded-Proto只有 1 条记录https。这就是我想出将属性设置RequireHeaderSymmetryfalse.

好吧,我摆脱了“参数计数...”警告消息,但紧接着我又遇到了另一个奇怪的调试消息:

未知代理:172.17.0.6:44624

在查看了ForwardedHeadersMiddleware的源代码之后,我终于发现我必须清理它们的集合KnownNetworksKnownProxies集合,ForwardedHeadersOptions或者将我的 docker 网络添加172.17.0.1/16到已知网络列表中。在那之后,我终于让它工作了。

PS:对于那些在负载均衡器(例如 Amazon 负载均衡器或 ELB)上设置 SSL/TLS 终止的用户,不要X-Forwarded-Proto在 nginx 配置中设置标头。这将覆盖https来自负载平衡到http方案的正确值,并且重定向 url 将是错误的。我还没有找到如何将 nginx 中使用的方案附加到标头而不是覆盖它。

于 2017-05-09T19:29:03.463 回答
0

对于apache用户,只需要添加一个header:RequestHeader set X-Forwarded-Proto "https"

首先需要确保 mod_headers 已启用。

于 2021-07-30T11:07:03.060 回答