Asp.Net 核心 Google 身份验证

Asp.Net Core Google authentication

我的应用在 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。如果我输入以下内容,我的网络应用程序将按预期运行:

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,我需要什么配置?

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

SSL/TLS 终止代理

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

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

X-Forwarded-* HTTP Headers

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

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 核心应用程序并查看传入的 HTTP 请求,我们将看到 X-Forwarded-* header 都已设置,但是重定向 URL 仍然使用 HTTP 方案。

已转发Headers中间件

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

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

        app.UseForwardedHeaders(forwardedHeadersOptions);

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

我的故事

上面的代码看起来与 Microsoft 建议的不同。如果我们看一下 documentation 他们的代码看起来有点短:

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

但是这对我不起作用。另外根据 this issue 下的评论,我并不孤单。

我在 Docker 容器中将 nginx 设置为 ASP.NET 核心应用程序 运行 的反向代理。在我把所有东西都放在 Amazon Load Balancer (ELB) 之后,它变得更加复杂。

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

Parameter count mismatch between X-Forwarded-For and X-Forwarded-Proto

然后我查看了我的 X-Forwarded-* headers 并意识到它们的长度不同。 X-Forwarded-For header 包含 2 条记录(逗号分隔的 IP 地址),而 X-Forwarded-Proto 只有一条记录 https。这就是我将 属性 RequireHeaderSymmetry 设置为 false 的方式。

好吧,我摆脱了 'Parameter count...' 警告消息,但紧接着我又遇到了另一条奇怪的调试消息:

Unknown proxy: 172.17.0.6:44624

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

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

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

首先需要确保mod_headers已启用。