asp.net核心JWT在uri查询参数?

Joh*_*hnC 7 url-parameters jwt asp.net-core-mvc asp.net-core

我有一个受JWT和Authorize属性保护的api,在客户端,我使用jquery ajax调用来处理它。

这可以正常工作,但是我现在需要能够安全地下载文件,因此无法设置标头Bearer值,可以在URI中将其作为url参数吗?

=-=-=-=-

更新:这是我最终为自己的方案所做的事情,这是一个内部项目,数量很少,但是安全性很重要,将来可能需要扩展:

当用户登录时,我生成一个随机的下载密钥,并将其与JWT的到期日期一起放入数据库的用户记录中,然后将下载密钥返回给客户端。如果有一个具有下载密钥的查询参数,并且该密钥存在于用户记录中并且尚未过期,则下载路径受保护仅允许下载。这样,dl密钥对每个用户都是唯一的,只要用户的auth会话有效且可以轻松吊销,该密钥就有效。

Tec*_*ium 8

这是一个普遍的问题。

每当您想直接从单个页面应用程序的HTML中的API引用图像或其他文件时,都无法Authorization<img>or <a>元素和对该API 的请求之间注入请求标头。您可以通过使用此处所述的一些相当新的浏览器功能来回避此问题,但是您可能需要支持缺少此功能的浏览器。

幸运的是,RFC 6750通过“ URI查询参数”身份验证方法指定了一种完全按照您的要求进行操作的方法。如果遵循其约定,您将使用以下格式接受JWT:

https://server.example.com/resource?access_token=mF_9.B5f-4.1JqM&p=q

如另一个答案和RFC 6750本身所述,您应该仅在必要时这样做。从RFC:

由于与URI方法相关的安全性弱点(请参阅第5节),包括记录访问令牌的URL的可能性很高,因此,除非无法在“授权”中传输访问令牌,否则不应使用该URL。请求标头字段或HTTP请求实体正文。

如果您仍然决定实施“ URI查询参数”身份验证,则可以使用Invio.Extensions.Authentication.JwtBearer库和上的调用AddQueryStringAuthentication()扩展方法JwtBearerOptions。或者,如果您想手动执行此操作,当然也可以执行此操作。这是一个代码示例,将两种方式都显示为Microsoft.AspNetCore.Authentication.JwtBearer库的扩展。

public void ConfigureServices(IServiceCollection services) {
    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(
            options => {
                var authentication = this.configuration.GetSection("Authentication");

                options.TokenValidationParameters = new TokenValidationParameters {
                    ValidIssuers = authentication["Issuer"],
                    ValidAudience = authentication["ClientId"],
                    IssuerSigningKey = new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(authentication["ClientSecret"])
                    )
                };

                // OPTION 1: use `Invio.Extensions.Authentication.JwtBearer`

                options.AddQueryStringAuthentication();

                // OPTION 2: do it manually

                options.Events = new JwtBearerEvents {
                    OnMessageReceived = (context) => {
                        StringValues values;

                        if (!context.Request.Query.TryGetValue("access_token", out values)) {
                            return Task.CompletedTask;
                        }

                        if (values.Count > 1) {
                            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                            context.Fail(
                                "Only one 'access_token' query string parameter can be defined. " +
                                $"However, {values.Count:N0} were included in the request."
                            );

                            return Task.CompletedTask;
                        }

                        var token = values.Single();

                        if (String.IsNullOrWhiteSpace(token)) {
                            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                            context.Fail(
                                "The 'access_token' query string parameter was defined, " +
                                "but a value to represent the token was not included."
                            );

                            return Task.CompletedTask;
                        }

                        context.Token = token;

                        return Task.CompletedTask;
                    }
                };
            }
        );
}
Run Code Online (Sandbox Code Playgroud)


Kir*_*kin 6

尽管从技术上讲可以在URL中包含JWT,但强烈建议不要这样做。请参阅此处的引言,它解释了为什么这是一个坏主意:

不要在页面URL中传递承载令牌:不应在页面URL中传递承载令牌(例如,作为查询字符串参数)。相反,承载令牌应该在采取保密措施的HTTP消息头或消息主体中传递。浏览器,Web服务器和其他软件可能无法充分保护浏览器历史记录,Web服务器日志和其他数据结构中的URL。如果在页面URL中传递了承载令牌,则攻击者可能能够从历史数据,日志或其他不安全的位置中窃取它们。

但是,如果您别无选择或者只是不在乎安全性实践,请参阅Technetium的答案


Zil*_*iav 6

您可以使用中间件从查询参数中设置授权标头:

        public class SecureDownloadUrlsMiddleware
        {
            private readonly RequestDelegate next;

            public SecureDownloadUrlsMiddleware(RequestDelegate next)
            {
                this.next = next;
            }

            public async Task Invoke(HttpContext context /* other dependencies */)
            {
                // get the token from query param
                var token = context.Request.Query["t"];
                // set the authorization header only if it is empty
                if (string.IsNullOrEmpty(context.Request.Headers["Authorization"]) &&
                    !string.IsNullOrEmpty(token))
                {
                    context.Request.Headers["Authorization"] = $"Bearer {token}";
                }
                await next(context);
            }
        }
Run Code Online (Sandbox Code Playgroud)

然后在 Startup.cs 中使用身份验证中间件之前的中间件:

app.UseMiddleware(typeof(SecureDownloadUrlsMiddleware));
app.UseAuthentication();
Run Code Online (Sandbox Code Playgroud)