【问题标题】:How to make SignalR Bearer Authentication Secure OR how to Not Log the Query String如何使 SignalR 承载身份验证安全或如何不记录查询字符串
【发布时间】:2022-01-18 02:32:08
【问题描述】:
使用Bearer Token Authentication 时,SignalR 通过查询字符串传递 JWT 令牌。显然,这会带来the token gets logged 连同请求的风险,因此任何可以读取日志的人都可以通过复制令牌来冒充。
文档说:
如果您担心使用服务器日志记录这些数据,可以通过将 Microsoft.AspNetCore.Hosting 记录器配置为警告级别或更高级别(这些消息写入信息级别)来完全禁用此日志记录
在这个阶段我想到了一个附带问题:谁保证如果日志级别是Warning并且发生了一些不好的事情,日志中不会仍然包含请求 URL?
文档继续:
如果您仍想记录某些请求信息,您可以编写一个中间件来记录您需要的数据并过滤掉 access_token 查询字符串值(如果存在)。
我想这个想法是完全关闭请求的默认日志记录并用自定义日志记录中间件替换它。这对我来说听起来并不简单。所以我想知道:
- 是否有任何方法可以连接到记录器,然后自定义实际记录的内容?
- 或者我们可以利用现有的HTTP Logging 吗?乍一看,在查询字符串的日志记录方面,它似乎也是一个全有或全无的选项,或者有没有一种自定义方法?
- 是否有解决此问题的 NuGet 包?
- 其他人是如何解决这个问题的?
【问题讨论】:
标签:
c#
asp.net-core
security
logging
asp.net-core-signalr
【解决方案1】:
我已经采取了一种方法,即确实需要将 JWT 令牌作为查询字符串的一部分发送,正如 here 所解释的那样。
总而言之,当设置为 cookie 时,cookie 将作为 SignalR 连接初始化的一部分被浏览器自动发送:
document.cookie = `X-Authorization=${token}; path=/; secure; samesite=strict`; // https://stackoverflow.com/a/48618910/331281
const newConnection = new HubConnectionBuilder()
.withUrl('/background-queue-hub', {
skipNegotiation: true, // to avoid CORS problems (see: https://stackoverflow.com/a/52913505/331281)
transport: HttpTransportType.WebSockets,
})
...
但是,这会冒CSWSH 的风险。因此,在服务器端,我们必须检查原始标头以减轻这种情况。它可以在 cookie 值被复制到 JWT Bearer 身份验证上下文的地方完成:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => // https://stackoverflow.com/a/66485247/331281
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// for SignalR authentication we need to read the access token form the cookie
// since we don't want to pass the authentication token through the query string
// as the query string is being logged
if (context.HttpContext.Request.Path.StartsWithSegments(SignalRHubPath))
{
var allowedOrigins = Configuration["SignalR:AllowedOrigins"].Split(';');
if (allowedOrigins.Contains(context.Request.Headers["Origin"].ToString())) // see: https://www.tpeczek.com/2017/07/preventing-cross-site-websocket.html
{
context.Token = context.Request.Cookies["X-Authorization"];
}
else
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
}
}
return Task.CompletedTask;
}
};
});