Implementando Segurança SignalR com JWT, Roles e Claims

A segurança em aplicações em tempo real🚀 Scale Out com Redis: Atenda Milhões de Conexões!🚀 Scale Out com Redis: Atenda Milhões de Conexões!Integre o Redis com SignalR no .NET e distribua mensagens entre servidores, alcançando escalabilidade e alta performance em tempo real. é essencial para garantir que apenas usuários autorizados possam se conectar, enviar e receber mensagens. Quando usamos SignalR para criar chats, dashboards ou qualquer comunicação bidirecional, precisamos cuidar da autenticação (para validar quem é o usuário) e da autorização (para definir o que cada usuário pode fazer). Nesta abordagem, vamos explorar como implementar essas duas camadas de segurança usando JWT, roles e claims de forma prática e com exemplos que podem ser usados em projetos do mundo real.

Índice🔗

Por Que a Segurança é Fundamental no SignalR?🔗

SignalR permite comunicação bidirecional entre o cliente e o servidor, o que significa que dados sensíveisBoas Práticas de Segurança no Docker SwarmBoas Práticas de Segurança no Docker SwarmDescubra como assegurar seu Docker Swarm com práticas de segurança que protegem a comunicação, gerenciamento de secrets e integridade de imagens. podem ser transmitidos. Sem medidas de segurança adequadas, você corre o risco de:

Portanto, implementar autenticação🔑 Autenticação JWT: Proteja sua API com Tokens!🔑 Autenticação JWT: Proteja sua API com Tokens!Descubra como implementar autenticação JWT no ASP.NET Core com exemplos práticos, boas práticas de segurança e dicas para proteger suas APIs de forma eficiente. e autorização é essencial para proteger sua aplicação.

Autenticação com JWT no SignalR🔗

O JWT (JSON Web Token🔑 Autenticação JWT: Proteja sua API com Tokens!🔑 Autenticação JWT: Proteja sua API com Tokens!Descubra como implementar autenticação JWT no ASP.NET Core com exemplos práticos, boas práticas de segurança e dicas para proteger suas APIs de forma eficiente.) é uma forma prática de transmitir informações sobre o usuário de forma segura. No contexto do SignalR, o processo se integra à conexão WebSocket de forma semelhante a como funciona nas APIs RESTful📡 RESTful 101: Princípios que Todo Dev API Precisa Saber!📡 RESTful 101: Princípios que Todo Dev API Precisa Saber!Descubra os fundamentos do REST e boas práticas para criar APIs simples, escaláveis e eficientes. Domine métodos HTTP e status codes com exemplos práticos..

Como Funciona:

Configuração no ASP.NET Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real.:

1. Adicionar o JWT Middleware🔒 Middleware: Intercepte Requests como um Vigilante!🔒 Middleware: Intercepte Requests como um Vigilante!Descubra como usar middlewares no ASP.NET Core para monitorar, validar e controlar o fluxo de requisições de forma segura e eficiente em seu projeto. no Startup/Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
// No método ConfigureServices:
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "SeuIssuer",
        ValidAudience = "SeuAudience",
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuaChaveSecretaAqui"))
    };
    // Permite que o SignalR receba o token via query string
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
            {
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});
// No método Configure:
app.UseAuthentication();
app.UseAuthorization();

2. Enviar📡 RESTful 101: Princípios que Todo Dev API Precisa Saber!📡 RESTful 101: Princípios que Todo Dev API Precisa Saber!Descubra os fundamentos do REST e boas práticas para criar APIs simples, escaláveis e eficientes. Domine métodos HTTP e status codes com exemplos práticos. o Token no Cliente:

Ao se conectar ao Hub, o cliente precisa incluir o token na query string📝 Strings em C#: Manipule Textos como um Mestre dos Caracteres!📝 Strings em C#: Manipule Textos como um Mestre dos Caracteres!Aprenda a dominar os segredos das strings em C# com técnicas de manipulação, concatenação, interpolação e boas práticas, impulsionando sua performance.:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/hubs/chat?access_token=SEU_JWT_AQUI")
    .build();

Implementando Autorização: Roles e Claims🔗

Enquanto o JWT garante que a identidade do usuário seja verificada, a autorizaçãoAutenticação e Autorização Assíncronas em Aplicações WebAutenticação e Autorização Assíncronas em Aplicações WebDescubra como implementar autenticação e autorização assíncronas em ASP.NET Core usando async/await para melhorar escalabilidade e desempenho da sua aplicação. define o que ele pode fazer.

Utilizando o Atributo📜 Atributos Customizados: Metadados que Guiam seu Código!📜 Atributos Customizados: Metadados que Guiam seu Código!Descubra como atributos customizados potencializam a organização do código, facilitam auditorias e testes, e garantem eficiência. [Authorize]:

No próprio Hub, você pode restringir o acesso utilizando o atributo📜 Atributos Customizados: Metadados que Guiam seu Código!📜 Atributos Customizados: Metadados que Guiam seu Código!Descubra como atributos customizados potencializam a organização do código, facilitam auditorias e testes, e garantem eficiência. [Authorize]:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
[Authorize] // Garante que somente usuários autenticados possam se conectar
public class ChatHub : Hub
{
    public async Task SendMessage(string message)
    {
        // Usuário já está autenticado, podemos pegar os claims
        var userName = Context.User.Identity.Name;
        await Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
}

AutorizaçãoAutenticação e Autorização Assíncronas em Aplicações WebAutenticação e Autorização Assíncronas em Aplicações WebDescubra como implementar autenticação e autorização assíncronas em ASP.NET Core usando async/await para melhorar escalabilidade e desempenho da sua aplicação. por Roles ou Policies:

Se o cenário exigir restrição mais fina (por exemplo, separando administradores de usuários comuns), você pode usar roles ou definir policies específicas:

[Authorize(Roles = "Admin")] // Apenas administradores podem acessar este Hub
public class AdminHub : Hub
{
    public async Task NotifyAll(string notification)
    {
        await Clients.All.SendAsync("ReceiveNotification", notification);
    }
}

Você também pode criar policies customizadas na configuraçãoGerenciando Secrets e Configs em Docker SwarmGerenciando Secrets e Configs em Docker SwarmAprenda a proteger credenciais, chaves e tokens com Docker Swarm. Gerencie Secrets e Configs de forma segura, garantindo integridade dos dados críticos. do JWT:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AcessoPremium", policy =>
          policy.RequireClaim("Subscription", "Premium"));
});

E📊 Behavior-Driven Development: Testes que Todo Mundo Entende!📊 Behavior-Driven Development: Testes que Todo Mundo Entende!Descubra como o BDD transforma testes em linguagens acessíveis. Aprenda a usar SpecFlow em C# para criar testes claros, colaborativos e sem ambiguidades. aplicar no Hub:

[Authorize(Policy = "AcessoPremium")]
public class PremiumHub : Hub
{
    // Métodos para usuários premium
}

Gerenciamento de Conexões e Grupos Seguros🔗

Rastreamento de Conexões Ativas:

// Armazena conexões por usuário (em memória - não usar em produção!)
private static readonly Dictionary<string, HashSet<string>> UserConnections = new();
public override async Task OnConnectedAsync()
{
    var userId = Context.User.Identity.Name;
    if (!UserConnections.ContainsKey(userId))
    {
        UserConnections[userId] = new HashSet<string>();
    }
    UserConnections[userId].Add(Context.ConnectionId);
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
    var userId = Context.User.Identity.Name;
    if (UserConnections.TryGetValue(userId, out var connections))
    {
        connections.Remove(Context.ConnectionId);
        if (connections.Count == 0)
        {
            UserConnections.Remove(userId);
        }
    }
}

Controle de Grupos:

public async Task JoinGroup(string groupName)
{
    if (Context.User.IsInRole("SupportAgent"))
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
    }
    else
    {
        throw new HubException("Acesso negado a grupos de suporte!");
    }
}

Boas Práticas de Segurança🔗

1. HTTPS Obrigatório:

services.AddSignalR(options =>
{
    options.EnableDetailedErrors = false; // Desligue em produção!
}).AddHubOptions<ChatHub>(options =>
{
    options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
    options.KeepAliveInterval = TimeSpan.FromSeconds(15);
});

2. ValidaçãoComo Assegurar Validação e Cobertura de Código em Projetos LINQComo Assegurar Validação e Cobertura de Código em Projetos LINQAprenda estratégias avançadas para testar consultas LINQ, garantindo cobertura de código e confiabilidade com ferramentas e práticas recomendadas. de Input:

public async Task SendMessage(string message)
{
    if (string.IsNullOrWhiteSpace(message) || message.Length > 500)
    {
        throw new HubException("Mensagem inválida!");
    }
    // ...
}

3. CORS Restritivo:

services.AddCors(options =>
{
    options.AddPolicy("SignalRCors", builder =>
    {
        builder.WithOrigins("https://seusite.com")
               .AllowAnyHeader()
               .AllowAnyMethod()
               .AllowCredentials();
    });
});

4. Logging de TentativasTimeout e Retries: Estratégias de Resiliência com Async/AwaitTimeout e Retries: Estratégias de Resiliência com Async/AwaitAprenda a usar Timeout e Retries com async/await em C# para garantir operações assíncronas robustas e melhorar a resiliência da sua aplicação. Suspeitas:

public override async Task OnConnectedAsync()
{
    var ip = Context.GetHttpContext()?.Connection.RemoteIpAddress?.ToString();
    _logger.LogInformation($"Nova conexão de {ip} - Usuário: {Context.User.Identity.Name}");
}
⚠️ Atenção a XSS:

Sempre sanitize mensagens antes de exibir no client:

// Client-side
connection.on("ReceiveMessage", (user, message) => {
    const encodedMsg = $('<div />').text(message).html();
    $("#messages").append(`<li>${user}: ${encodedMsg}</li>`);
});
// Server-side
message = System.Net.WebUtility.HtmlEncode(message);

Exemplo Prático: Hub Seguro com JWT e Autorização🔗

Vamos criar um exemplo prático📝 Logging com Serilog: Registre Tudo como um Detetive de Bugs!📝 Logging com Serilog: Registre Tudo como um Detetive de Bugs!Aprenda a usar Serilog em .NET para registrar logs estruturados, identificar erros e enriquecer informações, transformando seu código num enigma solucionável. que une as duas estratégias:

1. ConfiguraçãoGerenciando Secrets e Configs em Docker SwarmGerenciando Secrets e Configs em Docker SwarmAprenda a proteger credenciais, chaves e tokens com Docker Swarm. Gerencie Secrets e Configs de forma segura, garantindo integridade dos dados críticos. do JWT no Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "MeuIssuer",
        ValidAudience = "MeuAudience",
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MinhaChaveSuperSecreta"))
    };
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs/chat"))
            {
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});
builder.Services.AddAuthorization();
builder.Services.AddSignalR();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<ChatHub>("/hubs/chat");
app.Run();

2. Definição do Hub com AutorizaçãoAutenticação e Autorização Assíncronas em Aplicações WebAutenticação e Autorização Assíncronas em Aplicações WebDescubra como implementar autenticação e autorização assíncronas em ASP.NET Core usando async/await para melhorar escalabilidade e desempenho da sua aplicação.:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
[Authorize]
public class ChatHub : Hub
{
    public async Task SendMessage(string message)
    {
        var userName = Context.User.Identity?.Name ?? "Anônimo";
        await Clients.All.SendAsync("ReceiveMessage", userName, message);
    }
    // Exemplo de método com autorização específica via role
    [Authorize(Roles = "Admin")]
    public async Task SendAdminMessage(string message)
    {
        var userName = Context.User.Identity?.Name;
        await Clients.All.SendAsync("ReceiveAdminMessage", userName, message);
    }
}

3. Conexão do Cliente (JavaScript):

const token = "SEU_JWT_VALIDO"; // Obtenha esse token via login/autenticação
const connection = new signalR.HubConnectionBuilder()
    .withUrl("/hubs/chat?access_token=" + token)
    .build();
connection.start()
    .then(() => console.log("Conectado com sucesso!"))
    .catch(err => console.error("Erro na conexão: ", err));
connection.on("ReceiveMessage", (user, msg) => {
    console.log(`${user}: ${msg}`);
});

Conclusão🔗

Segurança em SignalR não é apenas uma boa prática, é uma necessidade. Com autenticação JWT📄 Swagger/OpenAPI: Documente sua API Automaticamente!📄 Swagger/OpenAPI: Documente sua API Automaticamente!Descubra como gerar documentação interativa e automatizada em APIs com o Swagger/OpenAPI. Aprenda a configurar no .NET e testar endpoints facilmente., autorização baseada em roles e claims, e boas práticas de gerenciamento de conexões, você pode proteger sua aplicação contra ameaças comuns. Lembre-se de sempre testar sua implementação e monitorar o comportamento em produção para garantir que tudo funcione como esperado.

Pronto para levar sua aplicação SignalR🚀 SignalR Básico: Crie um Chat em Tempo Real!🚀 SignalR Básico: Crie um Chat em Tempo Real!Descubra como criar um chat em tempo real com SignalR e ASP.NET Core. Tutorial prático com passo a passo para iniciantes e especialistas. para o próximo nível? 🚀

Autor: Marcelo V. Souza - Engenheiro de Sistemas e Entusiasta em IoT e Desenvolvimento de Software, com foco em inovação tecnológica.

Referências🔗

Compartilhar artigo

Artigos Relacionados