Partilhar via


Cordas de propósito no ASP.NET Core

Os componentes que consomem IDataProtectionProvider devem passar um parâmetro único de propósitos ao CreateProtector método. O parâmetro de finalidades é inerente à segurança do sistema de proteção de dados, pois proporciona isolamento entre consumidores criptográficos, mesmo que as chaves criptográficas raiz sejam as mesmas.

Quando um consumidor especifica um propósito, a cadeia de propósitos é usada juntamente com as chaves criptográficas raiz para obter subchaves criptográficas únicas para esse consumidor. Isto isola o consumidor de todos os outros consumidores criptográficos na aplicação: nenhum outro componente consegue ler as suas cargas úteis, nem pode ler as cargas úteis de qualquer outro componente. Este isolamento também torna inviáveis categorias inteiras de ataque contra o componente.

Exemplo de Diagrama de Propósito

No diagrama acima, IDataProtector as instâncias A e B não conseguem ler as cargas úteis uma da outra, apenas as suas próprias.

A linha de propósito não tem de ser secreta. Deve ser simplesmente único no sentido de que nenhum outro componente bem comportado alguma vez fornecerá a mesma cadeia de propósito.

Sugestão

Usar o namespace e o nome do tipo do componente que consome as APIs de proteção de dados é uma boa regra prática, pois na prática esta informação nunca entra em conflito.

Um componente criado pela Contoso, responsável pela emissão de tokens portadores, pode usar Contoso.Security.BearerToken como sua cadeia de caracteres de propósito. Ou – ainda melhor – pode usar Contoso.Security.BearerToken.v1 como string de propósito. Adicionar o número de versão permite que uma versão futura use Contoso.Security.BearerToken.v2 como finalidade, e as diferentes versões ficariam completamente isoladas umas das outras no que toca a cargas úteis.

Como o parâmetro de propósitos do CreateProtector é um array de strings, acima poderia ter sido especificado como [ "Contoso.Security.BearerToken", "v1" ]. Isto permite estabelecer uma hierarquia de propósitos e abre a possibilidade de cenários de multi-arrendamento com o sistema de proteção de dados.

Advertência

Os componentes não devem permitir que a entrada não confiável do utilizador seja a única fonte de entrada para a cadeia de propósitos.

Por exemplo, considere um componente Contoso.Messaging.SecureMessage responsável por armazenar mensagens seguras. Se o componente de mensagens seguras chamar CreateProtector([ username ]), então um utilizador malicioso poderia criar uma conta com o nome de utilizador "Contoso.Security.BearerToken" numa tentativa de fazer o componente ligar CreateProtector([ "Contoso.Security.BearerToken" ]), fazendo inadvertidamente com que o sistema de mensagens seguras criasse cargas úteis que poderiam ser percebidas como tokens de autenticação.

Uma cadeia de propósitos melhor para o componente de mensagens seria CreateProtector([ "Contoso.Messaging.SecureMessage", $"User: {username}" ]), que proporciona isolamento adequado.

O isolamento fornecido por IDataProtectionProvider, IDataProtector e os comportamentos, bem como os propósitos, são os seguintes:

  • Para um dado IDataProtectionProvider objeto, o CreateProtector método criará um IDataProtector objeto ligado de forma única tanto ao IDataProtectionProvider objeto que o criou como ao parâmetro de propósitos que foi passado no método.

  • O parâmetro de propósito não pode ser nulo. (Se os propósitos forem especificados como um array, isso significa que o array não deve ter comprimento zero e todos os elementos do array devem ser não nulos.) Uma string vazia é tecnicamente permitida, mas é desencorajada.

  • Dois argumentos de propósito são equivalentes se e só se contêm as mesmas cadeias (usando um comparador ordinal) na mesma ordem. Um argumento de propósito único é equivalente ao array correspondente de propósitos de elemento único.

  • Dois IDataProtector objetos são equivalentes se, e somente se, forem criados a partir de objetos equivalentes IDataProtectionProvider com parâmetros e propósitos equivalentes.

  • Para um objeto IDataProtector específico, uma chamada a Unprotect(protectedData) devolverá o unprotectedData original se e somente se protectedData := Protect(unprotectedData) para um objeto IDataProtector equivalente.

Observação

Não estamos a considerar o caso em que um componente escolhe intencionalmente uma cadeia de propósito que se sabe entrar em conflito com outro componente. Tal componente seria essencialmente considerado malicioso, e este sistema não se destina a fornecer garantias de segurança caso já esteja a correr código malicioso dentro do processo de trabalho.