C# - Segurança - Verificando as informações do usuário (.NET Core)


 Neste artigo vamos tratar da segurança em aplicações .NET Core, iniciando com a verificação das informações do usuário.

Para garantir a segurança das aplicações existem vários elementos importantes que você precisa considerar para tornar seus aplicativos seguros.

O principal, claro, é o usuário do aplicativo. O usuário é a pessoa autorizada a acessar a aplicação, mas pode também ser alguém que se passa como um usuário legítimo. Como saber então se esse usuário é confiável ?

Para garantir a segurança de um aplicativo em relação ao usuário temos um processo em duas partes:

Tendo essas premissas estabelecidas veremos como obter informações sobre usuários usando os recursos identity e principals.

Trabalhando com identidades do Windows - WindowsIdentity

Você pode identificar o usuário que está executando o aplicativo usando os recursos identity e principal.

A classe WindowsIdentity representa um usuário do Windows. Se você não identificar o usuário com uma conta do Windows, poderá usar outras classes que implementam a interface IIdentity. Com esta interface você tem acesso ao nome do usuário, informações sobre se o usuário está autenticado e o tipo de autenticação.

Um principal é um objeto que contém a identidade do usuário e os roles às quais o usuário pertence. A interface IPrincipal define a propriedade Identity, que retorna um objeto IIdentity, e o método IsInRole com o qual você pode verificar se o usuário é membro de uma role específica.

Um role é uma coleção de usuários que têm as mesmas permissões de segurança sendo a unidade de administração dos usuários.  As roles podem ser grupos do Windows ou apenas uma coleção de seqüências de caracteres que você define.

As classes principal disponíveis com o .NET são WindowsPrincipal, GenericPrincipal e RolePrincipal. Desde o .NET 4.5, esses tipos principais derivam da classe base ClaimsPrincipal.

Você também pode criar uma classe principal personalizada que implemente a interface IPrincipal ou derive de
ClaimsPrincipal.

Vejamos um exemplo de utilização destes recursos.

Criando o projeto Console

Vamos criar  uma aplicação Console  App (.NET Core) que fornece acesso ao principal e permite acessar a conta do Windows.

Vamos então criar essa aplicação usando o VS 2017 Community com o nome NetCore_Seguranca.

Para poder usar os recursos precisamos incluir no projeto o pacote System.Security.Principals.Windows via Nuget:

Namespaces usados no projeto:

using System.Collections.Generic;
using System.Security.Claims;
using System.Security.Principal;
using static System.Console;

Com o projeto criado abra o arquivo Program.cs e no método Main inclua o código abaixo:

        static void Main(string[] args)
        {
            WindowsIdentity identity = ExibeInfoIdentity();
            WindowsPrincipal principal = ExibeInfoPrincipal(identity);
            ExibeInfoClaims(principal.Claims);
            ReadLine();
        }

Definimos 3 métodos que vão retornar informações do identity, do principal e das claims do usuário. Vamos a seguir definir o código em cada um desses métodos.

1- ExibeInfoIdentity

        public static WindowsIdentity ExibeInfoIdentity()
        {
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            if (identity == null)
            {
                WriteLine("Não é um Windows Identity");
                return null;
            }
            WriteLine($"Tipo de Identity : {identity}");
            WriteLine($"Nome : {identity.Name}");
            WriteLine($"Autenticado : {identity.IsAuthenticated}");
            WriteLine($"Tipo de Autenticação : {identity.AuthenticationType}");
            WriteLine($"É usuário Anônimo ? : {identity.IsAnonymous}");
            WriteLine($"Token de acesso : " +  $"{identity.AccessToken.DangerousGetHandle()}");
            WriteLine();
            return identity;
        }

Este método cria um objeto WindowsIdentity invocando o método estático GetCurrent() e acessa suas propriedades exibindo o identitytype, nome da identidade, o tipo e outros valores conforme vemos a seguir:

A classe WindowsIdentity implementa a interface IIdentity que contêm três propriedades :

  1. AuthenticationType
  2. IsAuthenticated
  3. Name

As demais propriedades exibidas são especificas do tipo de identidade que estamos usando.

O tipo de autenticação mostra o CloudAP porque estou conectado ao sistema usando uma conta do Microsoft Live e o Active Directory vair aparecer no tipo de autenticação, se você estiver usando o Active Directory.

2- ExibeInfoPrincipal(identity)

Um principal contém uma identidade e oferece informações adicionais, como as roles às quais o usuário pertence.

O recurso Principals implementam a interface IPrincipal, que oferece o método IsInRole além de uma propriedade Identity. Com o Windows, todos os grupos do Windows dos quais o usuário é membro são mapeados para roles, e,
o método IsInRole está sobrecarregado para aceitar um identificador de segurança, uma string role ou um valor da enumeração WindowsBuiltInRole.

No usado verificamos se o usuário pertence às roles internas Users e Administrator:

        public static WindowsPrincipal ExibeInfoPrincipal(WindowsIdentity identity)
        {
            WriteLine("Informação do Principal");
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            if (principal == null)
            {
                WriteLine("Não é um Windows Principal");
                return null;
            }
            WriteLine($"É Usuário ? {principal.IsInRole(WindowsBuiltInRole.User)}");
            WriteLine($"É Administrador ? {principal.IsInRole(WindowsBuiltInRole.Administrator)}");
            WriteLine();
            return principal;
        }

Ao executar o código teremos o seguinte resultado :

Isso mostra como podemos acessar facilmente detalhes sobre os usuários atuais e suas roles, e, com essa informação podemos tomar decisões sobre quais ações devem ser permitidas ou negadas.

Todas as classes principal derivam da classe base ClaimsPrincipal, e, dessa forma é possível acessar claims ou declarações dos usuários usando a propriedade Claims de um objeto principal.

É o que faremos no próximo método.

3- ExibeInfoClaims(principal.Claims)

As Claims ou declarações/reivindicações oferecem mais flexibilidade em comparação com as roles.

Uma claim ou reivindicação é uma declaração feita sobre uma identidade de uma autoridade. Uma autoridade como o Active Directory ou a autenticação da conta do Microsoft Live faz reivindicações(claims) sobre os usuários - por exemplo, a reivindicação do nome do usuário, reivindicações sobre a quais grupos o usuário pertence, ou uma reivindicação sobre a idade. Ex: O usuário já tem 21 anos ou mais e está qualificado a acessar recursos específicos.

No método a seguir estamos acessando uma coleção de claims(declarações) para exibir informações sobre o assunto, emissor, tipo de declaração, etc.
:

        public static void ExibeInfoClaims(IEnumerable<Claim> claims)
        {
            WriteLine("Declarações (Claims) ");
            foreach (var claim in claims)
            {
                WriteLine($"Assunto : {claim.Subject}");
                WriteLine($"Emissor : {claim.Issuer}");
                WriteLine($"Tipo : {claim.Type}");
                WriteLine($"Valor do Tipo : {claim.ValueType}");
                WriteLine($"Valor : {claim.Value}");
                foreach (var prop in claim.Properties)
                {
                   WriteLine($"\tProperty: {prop.Key} {prop.Value}");
                }
                WriteLine();
            }
        }

A seguire temos um trecho das declarações da conta do Microsoft Live, que fornece informações sobre o
nome, o ID principal e os identificadores de grupo:

Podemos adicionar declarações a uma identidade do Windows a partir do provedor de declarações.

Podemos também adicionar uma declaração a partir de um programa cliente como uma claim sobre idade:

identity.AddClaim(new Claim("Idade", "25"));

Usar uma declaração como a definida acima é apenas uma questão de confiar ou não na declaração.

Neste tipo de claração o emissor geralmente é o LOCALAUTHORITY e não o AD AUTHORITY(Active Directory) que é mais confiável.

O WindowsIdentity proveniente da classe base ClaimsIdentity oferece vários métodos de verificação para claims ou para recuperar claims. Para testar se uma reivindicação está disponível, você pode usar o método HasClaim
método:

bool TemNome = identity.HasClaim (c => c.Type == ClaimTypes.Name);

Para recuperar declarações específicas, o método FindAll precisa de um predicado para definir uma correspondência:

var groupClaims = identity.FindAll (c => c.Type == ClaimTypes.GroupSid);

Vimos assim como obter informações de segurança de usuários do Windows usando os recursos do WindowsIdentity.

Pegue o código projeto aqui: NetCore_Seguranca.zip

"Pensai nas coisas que são de cima, e não nas que são da terra;"
Colossenses 3:2

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti