Compartilhar via


Considerações de segurança para dados

Ao tratar os dados no WCF (Windows Communication Foundation), você deve considerar as diferentes categorias de ameaças. A lista a seguir mostra as classes de ameaças mais importantes relacionadas ao processamento de dados. O WCF oferece ferramentas para mitigar essas ameaças.

  • Negação de serviço

    Depois de receber dados não confiáveis, os dados podem levar o receptor a acessar uma quantidade desproporcional de vários recursos, como memória, threads, conexões disponíveis ou ciclos de processador, o que gera cálculos longos. Um ataque de negação de serviço contra um servidor pode gerar uma falha e impossibilitar o processamento de mensagens de outros clientes legítimos.

  • Execução de código mal-intencionado

    Dados não confiáveis de entrada levam o receptor a executar o código que ele não pretendia.

  • Divulgação de informações confidenciais

    O invasor remoto força o receptor a responder às suas solicitações a fim de divulgar mais informações do que pretendido.

Código fornecido pelo usuário e segurança de acesso ao código

Vários locais na infraestrutura do WCF (Windows Communication Foundation) executam o código fornecido pelo usuário. Por exemplo, o mecanismo de serialização DataContractSerializer pode chamar acessadores set e acessadores get de propriedade fornecidos pelo usuário. A infraestrutura de canal do WCF também pode chamar classes derivadas fornecidas pelo usuário da classe Message.

É responsabilidade do autor do código garantir que não há vulnerabilidade de segurança. Por exemplo, se você criar um tipo de contrato de dados com uma propriedade de membro de dados do tipo inteiro e, na implementação do acessador set alocar uma matriz com base no valor da propriedade, você mostra a possibilidade de um ataque de negação de serviço, se uma mensagem mal-intencionada contiver um valor extremamente alto para esse membro de dados. Em geral, evite alocações com base em dados de entrada ou processamento longo no código fornecido pelo usuário (especialmente se o processamento demorado puder ser causado por uma pequena quantidade de dados de entrada). Ao executar a análise de segurança do código fornecido pelo usuário, considere também todos os casos de falha (ou seja, todos os branches de código em que as exceções são geradas).

O exemplo final do código fornecido pelo usuário é o código dentro de sua implementação de serviço para cada operação. É sua responsabilidade ter a segurança da implementação do serviço. É fácil criar acidentalmente implementações de operação inseguras que podem resultar em vulnerabilidades de negação de serviço. Por exemplo, uma operação que usa uma cadeia de caracteres e retorna a lista de clientes de um banco de dados cujo nome começa com essa cadeia de caracteres. Se você estiver trabalhando com um banco de dados grande e a cadeia de caracteres passada tem apenas uma única letra, seu código pode tentar criar uma mensagem maior do que toda a memória disponível, o que gera falha em todo serviço. (OutOfMemoryException não é recuperável no .NET Framework e sempre resulta no encerramento do aplicativo.)

Você deve garantir que nenhum código mal-intencionado esteja conectado aos vários pontos de extensibilidade. Isso é importante principalmente ao executar com confiança parcial, lidar com tipos de assemblies parcialmente confiáveis ou criar componentes utilizáveis pelo código parcialmente confiável. Para obter mais informações, consulte "Ameaças de Confiança Parcial" em uma seção posterior.

Observe que, ao executar em confiança parcial, a infraestrutura de serialização do contrato de dados oferece suporte apenas a um subconjunto limitado do modelo de programação do contrato de dados, por exemplo, não há suporte para membros de dados particulares ou tipos com o atributo SerializableAttribute. Para obter mais informações, consulte Confiança Parcial.

Observação

O CAS (Segurança de Acesso do Código) foi preterido em todas as versões do .NET Framework e do .NET. As versões recentes do .NET não aceitam anotações de CAS e produzem erros caso as APIs relacionadas ao CAS sejam usadas. Os desenvolvedores devem buscar meios alternativos de realizar tarefas de segurança.

Evitando a divulgação de informações confidenciais não intencional

Ao criar tipos serializáveis com segurança, a divulgação de informações confidenciais é uma possível questão.

Considere os seguintes pontos:

  • O modelo de programação DataContractSerializer permite a exposição de dados particulares e internos fora do tipo ou assembly durante a serialização. Além disso, a forma de um tipo pode ser exposta durante a exportação de esquema. Lembre-se de entender a projeção de serialização do seu tipo. Se você não quiser que nada seja exposto, desabilite a serialização (por exemplo, não aplicar o atributo DataMemberAttribute no caso de um contrato de dados).

  • Lembre-se de que o mesmo tipo pode ter várias projeções de serialização, dependendo do serializador em uso. O mesmo tipo pode mostrar um conjunto de dados quando usado com DataContractSerializer e outro conjunto de dados quando usado com XmlSerializer. O uso acidental do serializador errado pode levar à divulgação de informações confidenciais.

  • O uso de XmlSerializer no modo RPC (chamada de procedimento remoto herdado)/codificado pode mostrar involuntariamente a forma do grafo de objeto no lado do remetente para o lado do receptor.

Prevenindo ataques de negação de serviço

Cotas

A alocação de uma quantidade significativa de memória pelo receptor é um potencial ataque de negação de serviço. Embora esta seção trate dos problemas de consumo de memória decorrentes de mensagens grandes, podem ocorrer outros ataques. Por exemplo, as mensagens podem usar uma quantidade desproporcional de tempo de processamento.

Ataques de negação de serviço geralmente são atenuados usando cotas. Quando uma cota é excedida, uma exceção QuotaExceededException é normalmente gerada. Sem a cota, uma mensagem mal-intencionada pode permitir que toda a memória disponível seja acessada, resultando em uma exceção OutOfMemoryException ou que todas as pilhas disponíveis serão acessadas, resultando em um StackOverflowException.

O cenário excedido de cota é recuperável. Se encontrada em um serviço em execução, a mensagem que está sendo processada no momento é descartada e o serviço continua em execução e processa mais mensagens. Os cenários excedentes de pilha e memória insuficiente, no entanto, não podem ser recuperados em nenhum lugar do .NET Framework. O serviço termina se encontrar essas exceções.

As cotas no WCF não envolvem nenhuma pré-alocação. Por exemplo, se a cota MaxReceivedMessageSize (encontrada em várias classes) for definida como 128 KB, não significa que 128 KB sejam alocados automaticamente para cada mensagem. O valor real alocado depende do tamanho real da mensagem de entrada.

Muitas cotas estão disponíveis na camada de transporte. Essas são cotas impostas pelo canal de transporte específico em uso (HTTP, TCP e assim por diante). Embora este tópico aborde algumas dessas cotas, elas são descritas detalhadamente em Cotas de Transporte.

Vulnerabilidade de hashtable

Existe uma vulnerabilidade quando os contratos de dados contêm hashtables ou coleções. O problema ocorre se um número grande de valores for inserido em um hashtable em que um número grande desses valores gera o mesmo valor de hash. Isso pode ser usado como um ataque DOS. Essa vulnerabilidade pode ser reduzida pela definição da cota de associação MaxReceivedMessageSize. É necessário ter cuidado ao definir essa cota para evitar esses ataques. Essa cota coloca um limite superior no tamanho da mensagem de WCF. Além disso, evite usar hashtables ou coleções em seus contratos de dados.

Limitando o consumo de memória sem streaming

O modelo de segurança em torno de mensagens grandes depende se o streaming está em uso. No caso básico, sem streaming, as mensagens são armazenadas em buffer na memória. Nesse caso, use a cota MaxReceivedMessageSize em TransportBindingElement ou nas associações fornecidas pelo sistema para proteger contra mensagens grandes limitando o tamanho máximo da mensagem ao acesso. Observe que um serviço pode estar processando várias mensagens ao mesmo tempo, nesse caso, todas elas estão na memória. Use o recurso de limitação para atenuar essa ameaça.

Observe também que MaxReceivedMessageSize não coloca um limite superior no consumo de memória por mensagem, mas limita-o a um fator constante. Por exemplo, se MaxReceivedMessageSize for uma mensagem de 1 MB e uma mensagem de 1 MB for recebida e desserializada, será necessária uma memória adicional para conter o grafo de objetos desserializado, o que resulta no consumo total de memória com mais de 1 MB. Por esse motivo, evite criar tipos serializáveis que possam resultar em um consumo significativo de memória sem muitos dados de entrada. Por exemplo, um contrato de dados "MyContract" com 50 campos de membro de dados opcionais e mais 100 campos particulares pode criar uma instância com a construção XML "<MyContract/>". Esse XML faz com que a memória seja acessada por 150 campos. Observe que os membros de dados são opcionais por padrão. O problema é agravado quando esse tipo faz parte de uma matriz.

MaxReceivedMessageSize por si só não é suficiente para evitar todos os ataques de negação de serviço. Por exemplo, o desserializador pode ser forçado a desserializar um grafo de objeto profundamente aninhado (um objeto que contém outro objeto que contém outro e assim por diante) por uma mensagem de entrada. Os métodos de chamada DataContractSerializer e XmlSerializer de forma aninhada para desserializar esses grafos. Aninhamento profundo de chamadas de método pode resultar em um StackOverflowException irrecuperável. Essa ameaça é atenuada pela definição da cota MaxDepth para limitar o nível de aninhamento XML, conforme discutido na seção "Usando XML com segurança" posteriormente no tópico.

A definição de cotas adicionais para MaxReceivedMessageSize é importante principalmente ao usar a codificação XML binária. O uso da codificação binária é equivalente de alguma forma à compactação: um pequeno grupo de bytes na mensagem de entrada pode representar muitos dados. Assim, até mesmo uma mensagem que se ajusta ao limite MaxReceivedMessageSize pode ocupar muito mais memória em forma totalmente expandida. Para reduzir essas ameaças específicas de XML, todas as cotas de leitor XML devem ser definidas corretamente, conforme discutido na seção "Usando XML com segurança" mais adiante neste tópico.

Limitando o consumo de memória com streaming

Ao transmitir, você pode usar uma configuração pequena MaxReceivedMessageSize para proteger contra ataques de negação de serviço. No entanto, cenários mais complicados são possíveis com streaming. Por exemplo, um serviço de upload de arquivo aceita arquivos maiores do que toda a memória disponível. Nesse caso, defina MaxReceivedMessageSize como um valor extremamente grande, esperando que quase nenhum dado seja armazenado em buffer na memória e a mensagem transmita diretamente para o disco. Se uma mensagem mal-intencionada puder de alguma forma forçar o WCF a armazenar dados em buffer em vez de transmiti-los nesse caso, MaxReceivedMessageSize não será mais protegido contra a mensagem que acessa toda a memória disponível.

Para reduzir essa ameaça, existem configurações de cota específicas em vários componentes de processamento de dados do WCF que limitam o buffer. O mais importante deles é a propriedade MaxBufferSize em vários elementos de associação de transporte e associações padrão. Ao transmitir, essa cota deve ser definida levando em conta a quantidade máxima de memória que você está disposto a alocar por mensagem. Assim como MaxReceivedMessageSize, a configuração não coloca um máximo absoluto no consumo de memória, mas limita-a apenas a um fator constante. Além disso, como acontece com MaxReceivedMessageSize, saiba da possibilidade de várias mensagens serem processadas simultaneamente.

Detalhes do MaxBufferSize

A propriedade MaxBufferSize limita qualquer WCF de buffer em massa. Por exemplo, o WCF sempre armazena em buffer cabeçalhos SOAP e falhas SOAP, bem como todas as partes MIME encontradas não na ordem de leitura natural em uma mensagem MTOM (Mecanismo de Otimização de Transmissão de Mensagens). Essa configuração limita a quantidade de buffers em todos esses casos.

O WCF faz isso passando o valor MaxBufferSize para os vários componentes que podem ser armazenados em buffer. Por exemplo, algumas sobrecargas CreateMessage da classe Message têm um parâmetro maxSizeOfHeaders. O WCF passa o valor MaxBufferSize para esse parâmetro para limitar a quantidade de buffer de cabeçalho SOAP. É importante definir esse parâmetro ao usar a classe Message diretamente. Em geral, ao usar um componente no WCF que usa parâmetros de cota, é importante entender as implicações de segurança desses parâmetros e defini-los corretamente.

O codificador de mensagens MTOM também tem uma configuração MaxBufferSize. Ao usar associações padrão, isso é definido automaticamente para o valor de nível de transporte MaxBufferSize. No entanto, ao usar o elemento de associação do codificador de mensagem MTOM para criar uma associação personalizada, é importante definir a propriedade MaxBufferSize como um valor seguro quando o streaming é usado.

Ataques de streaming com base XML

MaxBufferSize por si só não é suficiente para garantir que o WCF não possa ser forçado a fazer buffer quando espera-se o streaming. Por exemplo, os leitores XML do WCF sempre armazenam em buffer toda a marca de início do elemento XML ao começar a ler um novo elemento. Assim, os namespaces e atributos são processados corretamente. Se MaxReceivedMessageSize estiver configurado para ser grande (por exemplo, para habilitar um cenário de streaming de arquivos de grande porte direto para disco), uma mensagem mal-intencionada pode ser construída onde todo o corpo da mensagem é uma marca de início de elemento XML grande. Uma tentativa de lê-lo resulta em um OutOfMemoryException. Esse é um dos muitos possíveis ataques de negação de serviço baseados em XML que podem ser atenuados usando cotas de leitor XML, discutidos na seção "Usando XML com segurança" mais adiante neste tópico. Ao transmitir, é especialmente importante definir todas essas cotas.

Misturando modelos de programação de streaming e buffer

Muitos ataques possíveis surgem da combinação de modelos de programação de streaming e não streaming no mesmo serviço. Suponha que haja um contrato de serviço com duas operações: uma usa Stream e outra usa uma matriz de algum tipo personalizado. Imagine que MaxReceivedMessageSize também seja definido como um valor grande para habilitar a primeira operação a processar fluxos grandes. Infelizmente, isso significa que as mensagens grandes agora também podem ser enviadas para a segunda operação e o desserializador armazena os dados na memória como uma matriz antes da operação ser chamada. Esse é um possível ataque de negação de serviço: a cota MaxBufferSize não limita o tamanho do corpo da mensagem, que é com o que o desserializador trabalha.

Por esse motivo, evite misturar operações com base em streaming e não streaming no mesmo contrato. Se você precisar misturar os dois modelos de programação, use as seguintes precauções:

  • Desative o recurso IExtensibleDataObject definindo a propriedade IgnoreExtensionDataObjectde ServiceBehaviorAttribute como true. Isso garante que apenas os membros que fazem parte do contrato sejam desserializados.

  • Defina a propriedade MaxItemsInObjectGraph de DataContractSerializer como um valor seguro. Essa cota também está disponível no atributo ServiceBehaviorAttribute ou por meio da configuração. Essa cota limita o número de objetos desserializados em um episódio de desserialização. Normalmente, cada parâmetro de operação ou parte do corpo da mensagem em um contrato de mensagem é desserializado em um episódio. Na desserialização de matrizes, cada entrada de matriz é contada como um objeto separado.

  • Defina todas as cotas de leitor XML como valores seguros. Preste atenção a MaxDepth, MaxStringContentLength e MaxArrayLength e evite cadeias de caracteres em operações que não são de streaming.

  • Revise a lista de tipos conhecidos, tendo em mente que qualquer um deles pode criar uma instância a qualquer momento (consulte a seção "Impedindo que tipos não intencionais sejam carregados" mais adiante neste tópico).

  • Não use nenhum tipo que implemente a interface IXmlSerializable que armazena em buffer muitos dados. Não adicione esses tipos à lista de tipos conhecidos.

  • Não use as matrizes XmlElement, XmlNodematrizes Byte ou tipos que implementam ISerializable em um contrato.

  • Não use as matrizes XmlElement, XmlNodematrizes Byte ou tipos que implementam ISerializable em uma lista de tipos conhecidos.

As precauções anteriores se aplicam quando a operação não transmitida usa DataContractSerializer. Nunca misture modelos de programação de streaming e não streaming no mesmo serviço se você estiver usando o XmlSerializer, porque não tem a proteção da cota MaxItemsInObjectGraph.

Ataques de fluxo lento

Uma classe de ataques de negação de serviço de streaming não envolve o consumo de memória. Em vez disso, o ataque envolve um remetente ou receptor de dados lento. Enquanto aguarda os dados serem enviados ou recebidos, recursos como threads e conexões disponíveis são esgotados. Essa situação pode ocorrer como resultado de um ataque mal-intencionado ou de um remetente/receptor legítimo em uma conexão de rede lenta.

Para reduzir esses ataques, defina os tempos limite de transporte corretamente. Para obter mais informações, consulte Cotas de Transporte. Em segundo lugar, nunca use operações síncronas Read ou Write ao trabalhar com fluxos no WCF.

Usando XML com segurança

Observação

Embora esta seção seja sobre XML, as informações também se aplicam a documentos JSON (JavaScript Object Notation). As cotas funcionam da mesma forma, usando Mapeamento entre JSON e XML.

Leitores XML Seguros

O infoset XML forma a base de todo o processamento de mensagens no WCF. Ao aceitar dados XML de uma fonte não confiável, existem várias possibilidades de ataque de negação de serviço que devem ser atenuadas. O WCF fornece leitores XML especiais e seguros. Esses leitores são criados automaticamente ao usar uma das codificações padrão no WCF (texto, binário ou MTOM).

Alguns dos recursos de segurança nesses leitores estão sempre ativos. Por exemplo, os leitores nunca processam DTDs (definições de tipo de documento), que são uma fonte potencial de ataques de negação de serviço e nunca devem aparecer em mensagens SOAP legítimas. Outros recursos de segurança incluem cotas de leitor que devem ser configuradas e são descritas na seção a seguir.

Ao trabalhar diretamente com leitores XML (como ao gravar seu próprio codificador personalizado ou ao trabalhar diretamente com a classe Message), sempre use os leitores seguros do WCF se houver chance de trabalhar com dados não confiáveis. Crie os leitores seguros chamando uma das sobrecargas de método de fábrica estático de CreateTextReader, CreateBinaryReader ou CreateMtomReader na classe XmlDictionaryReader. Ao criar um leitor, passe valores de cota seguros. Não chame as sobrecargas do método Create. Elas não criam um leitor WCF. Em vez disso, é criado um leitor que não está protegido pelos recursos de segurança descritos nesta seção.

Cotas de Leitor

Os leitores XML seguros têm cinco cotas configuráveis. Normalmente, são configurados usando a propriedade ReaderQuotas nos elementos de associação de codificação ou associações padrão ou usando um objeto XmlDictionaryReaderQuotas passado ao criar um leitor.

MaxBytesPerRead

Essa cota limita o número de bytes que são lidos em uma única operação Readde leitura ao ler a marca inicial do elemento e seus atributos. (Em casos não streaming, o nome do elemento em si não é contado em relação à cota.) MaxBytesPerRead é importante pelos seguintes motivos:

  • O nome do elemento e seus atributos são sempre armazenados em buffer na memória quando estão sendo lidos. Portanto, é importante definir essa cota corretamente no modo de streaming para evitar buffer excessivo quando o streaming é esperado. Consulte a seção de cota MaxDepth para obter informações sobre a quantidade real de buffers.

  • Ter atributos XML demais pode consumir todo o tempo de processamento de maneira desproporcional porque a exclusividade dos nomes de atributo tem que ser verificada. O MaxBytesPerRead atenua essa ameaça.

MaxDepth

Essa cota limita a profundidade máxima de aninhamento dos elementos XML. Por exemplo, o documento "<A><B><C/></B></A>" tem uma profundidade de aninhamento de três. MaxDepth é importante pelos seguintes motivos:

  • O MaxDepth interage com o MaxBytesPerRead: o leitor sempre mantém dados na memória para o elemento atual e todos os seus ancestrais, para que o consumo máximo de memória do leitor seja proporcional ao produto dessas duas configurações.

  • Ao desserializar um grafo de objeto aninhado mais profundamente, o desserializador é forçado para acessar a pilha inteira e gerar um StackOverflowException irrecuperável. Uma correlação direta existe entre aninhamento de XML e aninhamento de objeto para DataContractSerializer e XmlSerializer. Use MaxDepth para reduzir essa ameaça.

MaxNameTableCharCount

Essa cota limita o tamanho do nametable do leitor. O nametable contém algumas cadeias de caracteres (como namespaces e prefixos) que são encontrados ao processar um documento XML. Como essas cadeias de caracteres são armazenadas em buffer na memória, defina essa cota para evitar o armazenamento em buffer excessivo quando o streaming é esperado.

MaxStringContentLength

Essa cota limita o tamanho máximo da cadeia de caracteres que o leitor de XML retorna. Essa cota não limita o consumo de memória no próprio leitor de XML, mas no componente que esteja usando o leitor. Por exemplo, quando o DataContractSerializer usa um leitor protegido com MaxStringContentLength, ele não desserializa as cadeias de caracteres maiores do que essa cota. Ao usar a classe XmlDictionaryReader diretamente, nem todos os métodos respeitam essa cota, mas apenas os métodos criados especificamente para ler cadeias de caracteres, como o método ReadContentAsString. A propriedade Value no leitor não é afetada por essa cota e, portanto, não deve ser usada quando a proteção fornecida por essa cota for necessária.

MaxArrayLength

Essa cota limita o tamanho máximo de uma matriz de primitivas que o leitor de XML retorna, inclusive matrizes de bytes. Essa cota não limita o consumo de memória no próprio leitor de XML, mas em qualquer componente que esteja usando o leitor. Por exemplo, quando o DataContractSerializer usa um leitor protegido com MaxArrayLength, ele não desserializa as matrizes de bytes maiores do que essa cota. É importante definir essa cota ao tentar misturar modelos de programação em buffer e streaming em um único contrato. Lembre-se que, ao usar a classe XmlDictionaryReader diretamente, apenas os métodos criados especificamente para ler matrizes de tamanho arbitrário de determinados tipos primitivos, como ReadInt32Array, respeitam essa cota.

Ameaças específicas à codificação binária

O WCF de codificação XML binária é compatível com um recurso de cadeias de caracteres de dicionário. Uma cadeia de caracteres grande pode ser codificada usando apenas alguns bytes. Assim, permite ganhos significativos de desempenho, mas mostra novas ameaças de negação de serviço que devem ser reduzidas.

Há dois tipos de dicionários: estático e dinâmico. O dicionário estático é uma lista interna de cadeias de caracteres longas que podem ser representadas usando um código curto na codificação binária. Essa lista de cadeias de caracteres é corrigida quando o leitor é criado e não pode ser modificado. Nenhuma das cadeias de caracteres no dicionário estático usadas pelo WCF por padrão são suficientemente grandes para representar uma grave ameaça de negação de serviço, embora ainda possam ser usadas em um ataque de expansão de dicionário. Em cenários avançados em que você fornece seu próprio dicionário estático, tenha cuidado ao apresentar cadeias de caracteres de dicionário grandes.

O recurso dicionários dinâmicos permite que as mensagens definam suas próprias cadeias de caracteres e associem a códigos curtos. Esses mapeamentos de cadeia de caracteres para código são mantidos na memória durante toda a sessão de comunicação, de modo que as mensagens subsequentes não precisem reenviar as cadeias de caracteres e podem utilizar códigos que já estão definidos. Essas cadeias de caracteres podem ter um comprimento arbitrário e, portanto, representam uma ameaça mais séria do que aquelas do dicionário estático.

A primeira ameaça que deve ser reduzida é a possibilidade do dicionário dinâmico (a tabela de mapeamento de cadeia de caracteres para código) se tornar muito grande. Esse dicionário pode ser expandido ao longo de várias mensagens e, portanto, a cota MaxReceivedMessageSize não oferece proteção porque se aplica apenas a cada mensagem separadamente. Portanto, existe uma propriedade separada MaxSessionSize no BinaryMessageEncodingBindingElement que limita o tamanho do dicionário.

Ao contrário da maioria das outras cotas, essa cota também se aplica ao gravar mensagens. Se ela for excedida ao ler uma mensagem, QuotaExceededException será lançada como de costume. Se for excedida ao gravar uma mensagem, todas as cadeias de caracteres que fazem com que a cota seja excedida serão gravadas como aparecem, sem usar o recurso dicionários dinâmicos.

Ameaças de expansão de dicionário

Uma classe significativa de ataques binários específicos surge da expansão do dicionário. Uma pequena mensagem em forma binária pode se transformar em uma mensagem muito grande em forma textual totalmente expandida se fizer uso extensivo do recurso de dicionários de cadeia de caracteres. O fator de expansão para cadeias de caracteres de dicionário dinâmico é limitado pela cota MaxSessionSize, pois nenhuma cadeia de caracteres de dicionário dinâmico excede o tamanho máximo de todo o dicionário.

As propriedades MaxNameTableCharCount, MaxStringContentLength e MaxArrayLength limitam apenas o consumo de memória. Normalmente, elas não são necessárias para atenuar as ameaças no uso de não streaming porque o uso de memória já é limitado por MaxReceivedMessageSize. No entanto, MaxReceivedMessageSize conta bytes de pré-expansão. Quando a codificação binária está em uso, o consumo de memória pode ser superior a MaxReceivedMessageSize, limitado apenas a um fator de MaxSessionSize. Por esse motivo, é importante sempre definir todas as cotas de leitor (especialmente MaxStringContentLength) ao usar a codificação binária.

Ao usar a codificação binária junto com a DataContractSerializer, a interface IExtensibleDataObject pode ser mal utilizada para montar um ataque de expansão de dicionário. Essa interface fornece armazenamento ilimitado para dados arbitrários que não fazem parte do contrato. Se as cotas não puderem ser definidas abaixo do suficiente, de modo que MaxSessionSize multiplicado por MaxReceivedMessageSize não represente um problema, desabilite o recurso IExtensibleDataObject ao usar a codificação binária. Defina a propriedade IgnoreExtensionDataObject como true no atributo ServiceBehaviorAttribute. Como alternativa, não implemente a interface IExtensibleDataObject. Para obter mais informações, consulte Contratos de dados compatíveis por encaminhamento.

Resumo de Cotas

A tabela a seguir resume as diretrizes sobre cotas.

Condição Cotas importantes a serem definidas
Mensagens pequenas com streaming ou sem streaming, texto ou codificação MTOM MaxReceivedMessageSize, MaxBytesPerRead, e MaxDepth
Mensagens pequenas com streaming ou sem streaming, codificação binária MaxReceivedMessageSize, MaxSessionSize e todos ReaderQuotas
Mensagens grandes com streaming, texto ou codificação MTOM MaxBufferSize e todos ReaderQuotas
Mensagens grandes com streaming, codificação binária MaxBufferSize, MaxSessionSize e todos ReaderQuotas
  • Os tempos limite de nível de transporte devem ser sempre definidos e nunca usar leituras/gravações síncronas quando o streaming estiver em uso, independentemente se você está transmitindo mensagens grandes ou pequenas com streaming.

  • Quando estiver em dúvida sobre uma cota, defina-a como um valor seguro em vez de deixá-la aberta.

Impedindo a execução de código mal-intencionado

As seguintes classes gerais de ameaças podem executar código e ter efeitos não intencionais:

  • O desserializador carrega um tipo mal-intencionado, não seguro ou sensível à segurança.

  • Uma mensagem de entrada permite que o desserializador construa uma instância de um tipo normalmente seguro de forma que tenha consequências não intencionais.

As seções a seguir discutem ainda mais essas classes de ameaças.

DataContractSerializer

(Para obter informações de segurança sobre XmlSerializer, consulte a documentação pertinente.) O modelo de segurança para XmlSerializer é semelhante ao de DataContractSerializer e difere principalmente em detalhes. Por exemplo, o atributo XmlIncludeAttribute é usado para inclusão de tipo em vez do atributo KnownTypeAttribute. No entanto, algumas ameaças exclusivas a XmlSerializer são discutidas posteriormente neste tópico.

Impedindo que tipos não intencionais sejam carregados

O carregamento de tipos não intencionais pode ter consequências significativas, independente se o tipo é mal-intencionado ou apenas tenha efeitos colaterais sensíveis à segurança. Um tipo pode conter vulnerabilidade de segurança explorável, executar ações sensíveis à segurança em seu construtor ou construtor de classe, ter um volume de memória grande que facilita ataques de negação de serviço ou pode gerar exceções não recuperáveis. Os tipos podem ter construtores de classe que são executados assim que o tipo é carregado e antes de qualquer instância ser criada. Por esses motivos, é importante controlar o conjunto de tipos que pode ser carregado pelo desserializador.

O DataContractSerializer desserializa de forma livre e acoplada. Nunca lê o tipo CLR (common language runtime) e os nomes de assembly dos dados de entrada. Isso é semelhante ao comportamento de XmlSerializer, mas difere do comportamento de NetDataContractSerializer, BinaryFormatter e SoapFormatter. O acoplamento solto apresenta um grau de segurança, pois o invasor remoto não pode indicar um tipo arbitrário a ser carregado apenas nomeando o tipo na mensagem.

DataContractSerializer permite sempre carregar um tipo que é esperado atualmente de acordo com o contrato. Por exemplo, se um contrato de dados tiver um membro de dados do tipo Customer, DataContractSerializer é permitido para carregar o tipo Customer quando desserializar esse membro de dados.

Além disso, DataContractSerializer oferece suporte ao polimorfismo. Um membro de dados pode ser declarado como Object, mas os dados de entrada podem conter uma instância Customer. Somente é possível se o tipo Customer for "conhecido" do desserializador por meio de um destes mecanismos:

  • Atributo KnownTypeAttribute aplicado a um tipo.

  • Atributo KnownTypeAttribute especificando um método que retorna uma lista de tipos.

  • Atributo ServiceKnownTypeAttribute.

  • A seção de configuração KnownTypes.

  • Uma lista de tipos conhecidos passados para DataContractSerializer durante a construção, se estiver usando o serializador diretamente.

Cada um desses mecanismos aumenta a área de superfície e apresenta mais tipos que pode carregar o desserializador. Controle cada um desses mecanismos para garantir que nenhum tipo mal-intencionado ou não intencional seja adicionado à lista de tipos conhecidos.

Quando um tipo conhecido está no escopo, ele pode ser carregado a qualquer momento e instâncias do tipo podem ser criadas, mesmo que o contrato proíba realmente o seu uso. Por exemplo, imagine que o tipo "MyDangerousType" seja adicionado à lista de tipos conhecidos usando um dos mecanismos acima. Isso significa que:

  • MyDangerousType é carregado e seu construtor de classe é executado.

  • Mesmo ao desserializar um contrato de dados com um membro de dados de cadeia de caracteres, uma mensagem mal-intencionada ainda pode fazer com que uma instância de MyDangerousType seja criada. O código em MyDangerousType, como setters de propriedade, pode ser executado. Depois que for feito, o desserializador tenta atribuir essa instância ao membro de dados da cadeia de caracteres e falha com uma exceção.

Ao gravar um método que retorna uma lista de tipos conhecidos ou ao passar uma lista diretamente para o construtor DataContractSerializer, verifique se o código que prepara a lista é seguro e opera apenas em dados confiáveis.

Se especificar tipos conhecidos na configuração, verifique se o arquivo de configuração está protegido. Sempre use nomes fortes na configuração (especificando a chave pública do assembly assinado onde fica o tipo), mas não especifique a versão do tipo a ser carregada. O carregador de tipo escolhe automaticamente a versão mais recente, se possível. Se você definir uma versão específica na configuração, há o seguinte risco: um tipo pode ter uma vulnerabilidade de segurança que pode ser corrigida em uma versão futura, mas a versão vulnerável ainda é carregada porque ela é especificada explicitamente na configuração.

Ter muitos tipos conhecidos tem outra consequência: DataContractSerializer cria um cache de código de serialização/desserialização no domínio do aplicativo, com uma entrada para cada tipo que deve serializar e desserializar. Esse cache nunca é limpo enquanto o domínio do aplicativo estiver em execução. Por isso, um invasor que saiba que um aplicativo usa muitos tipos conhecidos pode causar a desserialização de todos esses tipos, permitindo que o cache consuma uma quantidade desproporcionalmente grande de memória.

Impedindo que tipos fiquem em um estado não intencional

Um tipo pode ter restrições de consistência interna que devem ser impostas. É necessário ter cuidado para evitar quebrar essas restrições durante a desserialização.

O exemplo de um tipo abaixo representa o estado de uma câmara de ar em uma nave espacial e impõe a restrição de que as portas internas e externas não podem ser abertas ao mesmo tempo.

[DataContract]
public class SpaceStationAirlock
{
    [DataMember]
    private bool innerDoorOpenValue = false;
    [DataMember]
    private bool outerDoorOpenValue = false;

    public bool InnerDoorOpen
    {
        get { return innerDoorOpenValue; }
        set
        {
            if (value & outerDoorOpenValue)
                throw new Exception("Cannot open both doors!");
            else innerDoorOpenValue = value;
        }
    }
    public bool OuterDoorOpen
    {
        get { return outerDoorOpenValue; }
        set
        {
            if (value & innerDoorOpenValue)
                throw new Exception("Cannot open both doors!");
            else outerDoorOpenValue = value;
        }
    }
}
<DataContract()> _
Public Class SpaceStationAirlock
    <DataMember()> Private innerDoorOpenValue As Boolean = False
    <DataMember()> Private outerDoorOpenValue As Boolean = False

    Public Property InnerDoorOpen() As Boolean
        Get

            Return innerDoorOpenValue
        End Get
        Set(ByVal value As Boolean)
            If (value & outerDoorOpenValue) Then
                Throw New Exception("Cannot open both doors!")
            Else
                innerDoorOpenValue = value
            End If
        End Set
    End Property

    Public Property OuterDoorOpen() As Boolean
        Get
            Return outerDoorOpenValue
        End Get
        Set(ByVal value As Boolean)
            If (value & innerDoorOpenValue) Then
                Throw New Exception("Cannot open both doors!")
            Else
                outerDoorOpenValue = value
            End If
        End Set
    End Property
End Class

Um invasor pode enviar uma mensagem mal-intencionada desse tipo, ignorar as restrições e colocar o objeto em um estado inválido, o que pode ter consequências não intencionais e imprevisíveis.

<SpaceStationAirlock>
    <innerDoorOpen>true</innerDoorOpen>
    <outerDoorOpen>true</outerDoorOpen>
</SpaceStationAirlock>

Pode-se evitar essa situação se você estiver ciente dos seguintes pontos:

  • Quando DataContractSerializer desserializa a maioria das classes, os construtores não são executados. Portanto, não dependa de nenhum gerenciamento de estado feito no construtor.

  • Use retornos de chamada para garantir que o objeto esteja em um estado válido. O retorno de chamada marcado com o atributo OnDeserializedAttribute é útil principalmente porque é executado após a conclusão da desserialização e tem a chance de examinar e corrigir o estado geral. Para obter mais informações, consulte Retornos de chamada para Serialização tolerante a versão.

  • Não crie tipos de contrato de dados que dependam de qualquer ordem específica na qual os setters de propriedade devem ser chamados.

  • Tome cuidado com o uso de tipos herdados marcados com o atributo SerializableAttribute. Muitos deles foram criados para trabalhar com a comunicação remota .NET Framework para uso somente com dados confiáveis. Os tipos existentes marcados com esse atributo podem não ter sido criados com a segurança do estado em mente.

  • Não confie na propriedade IsRequired do atributo DataMemberAttribute para garantir a presença de dados no que diz respeito à segurança do estado. Os dados sempre podem ser null, zero ou invalid.

  • Nunca confie em um grafo de objeto desserializado de uma fonte de dados não confiável sem primeiro validar. Cada objeto individual pode estar em um estado consistente, mas o grafo de objeto como um todo pode não estar. Além disso, mesmo que o modo de preservação do grafo de objeto esteja desabilitado, o grafo desserializado pode ter várias referências ao mesmo objeto ou ter referências circulares. Para obter mais informações, consulte Serialização e Desserialização.

Usando o NetDataContractSerializer com segurança

NetDataContractSerializer é um mecanismo de serialização que usa acoplamento restrito a tipos. É semelhante a BinaryFormatter e SoapFormatter. Ou seja, determina o tipo para criar uma instância que lê o assembly .NET Framework e o nome do tipo dos dados de entrada. Embora faça parte do WCF, não há nenhuma forma de plug-in neste mecanismo de serialização. O código personalizado deve ser gravado. NetDataContractSerializer é fornecido principalmente para facilitar a migração da comunicação remota .NET Framework para o WCF. Para obter mais informações, consulte a seção Serialização e desserialização.

Como a mensagem em si pode indicar que qualquer tipo seja carregado, o mecanismo NetDataContractSerializer é inerentemente inseguro e deve ser usado apenas com dados confiáveis. Para saber mais, consulte o Guia de segurança do BinaryFormatter.

Mesmo quando usados com dados confiáveis, os dados de entrada podem não especificar de forma suficiente o tipo a ser carregado, especialmente se a propriedade AssemblyFormat for definida como Simple. Qualquer pessoa com acesso ao diretório do aplicativo ou ao cache de assembly global pode substituir um tipo mal-intencionado no lugar daquele que deve ser carregado. Sempre verifique a segurança do diretório do aplicativo e do cache de assembly global definindo corretamente as permissões.

Em geral, se você permitir acesso de código parcialmente confiável à sua instância NetDataContractSerializer ou controlar o seletor substituto (ISurrogateSelector) ou o associador de serialização (SerializationBinder), o código pode exercer um grande controle sobre o processo de serialização/desserialização. Por exemplo, ele pode injetar tipos arbitrários, levar à divulgação de informações confidenciais, adulterar o grafo de objeto resultante ou dados serializados ou exceder o fluxo serializado resultante.

Outra questão com a segurança com NetDataContractSerializer é uma negação de serviço, não uma ameaça de execução de código mal-intencionada. Ao usar NetDataContractSerializer, sempre defina a cota MaxItemsInObjectGraph como um valor seguro. É fácil construir uma pequena mensagem mal-intencionada que aloca uma matriz de objetos cujo tamanho é limitado apenas por essa cota.

XmlSerializer-Specific Threats

O modelo de segurança XmlSerializer é semelhante ao DataContractSerializer. Algumas ameaças, no entanto, são exclusivas de XmlSerializer.

XmlSerializer gera assemblies de serialização no tempo de execução que contêm código que realmente serializa e desserializa. Esses assemblies são criados em um diretório de arquivos temporários. Se algum outro processo ou usuário tiver direitos de acesso a esse diretório, é possível substituir o código de serialização/desserialização com código arbitrário. Em seguida, XmlSerializer executa esse código usando seu contexto de segurança, em vez do código de serialização/desserialização. Verifique se as permissões estão definidas corretamente no diretório de arquivos temporários para impedir este evento.

XmlSerializer também tem um modo no qual usa assemblies de serialização pré-gerados em vez de gerá-los em tempo de execução. Esse modo é acionado sempre que XmlSerializer possa encontrar o assembly de serialização adequado. O XmlSerializer verifica se o assembly de serialização foi assinado ou não pela mesma chave que foi usada para assinar o assembly que contém os tipos que estão sendo serializados. Ele oferece proteção contra assemblies mal-intencionados que são disfarçados de assemblies de serialização. No entanto, se o assembly que contém os tipos serializáveis não estiver assinado, XmlSerializer não pode executar essa verificação e usa qualquer assembly com o nome correto. Assim, permite a execução de código mal-intencionado. Sempre assine os assemblies que contêm seus tipos serializáveis ou controle firmemente o acesso ao diretório do aplicativo e ao cache de assembly global para impedir a inserção de assemblies mal-intencionados.

XmlSerializer pode estar sujeito a um ataque de negação de serviço. O XmlSerializer não tem uma cota MaxItemsInObjectGraph (como está disponível no DataContractSerializer). Assim, desserializa uma quantidade arbitrária de objetos, limitada apenas pelo tamanho da mensagem.

Ameaças de confiança parcial

Observe as seguintes questões sobre ameaças relacionadas ao código em execução com confiança parcial. Essas ameaças incluem código parcialmente confiável mal-intencionado, bem como código parcialmente confiável mal-intencionado em combinação com outros cenários de ataque (por exemplo, código parcialmente confiável que constrói uma cadeia de caracteres específica e, em seguida, executa a desserialização).

  • Ao usar os componentes de serialização, nunca assegure nenhuma permissão antes desse uso, mesmo que todo o cenário de serialização esteja dentro do escopo de sua garantia e você não esteja lidando com dados ou objetos não confiáveis. Esse uso pode levar a vulnerabilidades de segurança.

  • Nos casos em que o código parcialmente confiável tem controle sobre o processo de serialização, por meio de pontos de extensibilidade (substitutos), tipos sendo serializados ou por outros meios, o código parcialmente confiável pode fazer com que o serializador gere uma grande quantidade de dados para o fluxo serializado, o que pode levar à Negação de Serviço (DoS) pelo receptor desse fluxo. Se você estiver serializando dados direcionados a um destino que seja sensível a ameaças do DoS, não serialize tipos parcialmente confiáveis ou, de outra forma, permita a serialização de controle de código parcialmente confiável.

  • Se você permitir acesso de código parcialmente confiável à sua instância DataContractSerializer ou controlar os Substitutos do Contrato de Dados, ele pode exercer um grande controle sobre o processo de serialização/desserialização. Por exemplo, ele pode injetar tipos arbitrários, levar à divulgação de informações confidenciais, adulterar o grafo de objeto resultante ou dados serializados ou exceder o fluxo serializado resultante. Uma ameaça equivalente NetDataContractSerializer é descrita na seção "Usando o NetDataContractSerializer com segurança".

  • Se o atributo DataContractAttribute for aplicado a um tipo (ou o tipo marcado como SerializableAttribute mas não for ISerializable), o desserializador pode criar uma instância desse tipo, mesmo que todos os construtores não sejam públicos ou protegidos por demandas.

  • Nunca confie no resultado da desserialização, a menos que os dados a serem desserializados sejam confiáveis e você tenha certeza de que todos os tipos conhecidos são de confiança. Observe que os tipos conhecidos não são carregados do arquivo de configuração do aplicativo (mas são carregados do arquivo de configuração do computador) ao serem executados em confiança parcial.

  • Se você passar uma instância DataContractSerializer com um substituto adicionado ao código parcialmente confiável, o código pode alterar as configurações modificáveis nesse substituto.

  • Para um objeto desserializado, se o leitor XML (ou seus dados) forem provenientes de um código parcialmente confiável, trate o objeto desserializado resultante como dados não confiáveis.

  • Só porque o tipo ExtensionDataObject não tem membros públicos, não significa que os dados dentro dele são seguros. Por exemplo, se você desserializar de uma fonte de dados com privilégios em um objeto no qual estão alguns dados e, em seguida, entregar esse objeto a um código parcialmente confiável, esse código pode ler os dados em ExtensionDataObject pela serialização do objeto. Considere a configuração IgnoreExtensionDataObject para true na desserialização de uma fonte de dados com privilégios em um objeto que é passado posteriormente para um código parcialmente confiável.

  • DataContractSerializer e DataContractJsonSerializer oferecem suporte à serialização de membros particulares, protegidos, internos e públicos em total confiança. No entanto, em confiança parcial, somente membros públicos podem ser serializados. Um SecurityException será gerado se um aplicativo tentar serializar um membro não público.

    Para permitir que membros internos ou internos protegidos sejam serializados em confiança parcial, use o atributo assembly InternalsVisibleToAttribute. Esse atributo permite que um assembly especifique que seus membros internos estão visíveis para algum outro assembly. Nesse caso, um assembly que deseja ter seus membros internos serializados especifica que seus membros internos estão visíveis para System.Runtime.Serialization.dll.

    A vantagem dessa abordagem é que não requer um caminho de geração de código elevado.

    Ao mesmo tempo, há duas grandes desvantagens.

    A primeira desvantagem é que a propriedade de aceitação do atributo InternalsVisibleToAttribute é em todo o assembly. Ou seja, você não pode especificar que apenas uma determinada classe tem seus membros internos serializados. É claro que você ainda pode optar por não serializar um membro interno específico. Basta não adicionar um atributo DataMemberAttribute a esse membro. Da mesma forma, um desenvolvedor também pode optar por tornar um membro interno em vez de particular ou protegido, com pequenas questões de visibilidade.

    A segunda desvantagem é que ainda não oferece suporte a membros particulares ou protegidos.

    Para ilustrar o uso do atributo InternalsVisibleToAttribute na confiança parcial, considere o seguinte programa:

        public class Program
        {
            public static void Main(string[] args)
            {
                try
                {
    //              PermissionsHelper.InternetZone corresponds to the PermissionSet for partial trust.
    //              PermissionsHelper.InternetZone.PermitOnly();
                    MemoryStream memoryStream = new MemoryStream();
                    new DataContractSerializer(typeof(DataNode)).
                        WriteObject(memoryStream, new DataNode());
                }
                finally
                {
                    CodeAccessPermission.RevertPermitOnly();
                }
            }
    
            [DataContract]
            public class DataNode
            {
                [DataMember]
                internal string Value = "Default";
            }
        }
    

    No exemplo acima, PermissionsHelper.InternetZone corresponde à PermissionSet para confiança parcial. Agora, sem o atributo InternalsVisibleToAttribute, o aplicativo não funcionará, indicando o SecurityException que membros não públicos não podem ser serializados em confiança parcial.

    No entanto, ao adicionar a linha a seguir ao arquivo de origem, o programa será executado com êxito.

    [assembly:System.Runtime.CompilerServices.InternalsVisibleTo("System.Runtime.Serialization, PublicKey = 00000000000000000400000000000000")]
    

Outras questões de gerenciamento de estado

Vale a pena mencionar algumas outras questões sobre o gerenciamento de estado do objeto:

  • Ao usar o modelo de programação baseado em fluxo com um transporte de streaming, o processamento da mensagem ocorre com o recebimento da mensagem. O remetente da mensagem pode anular a operação de envio no meio do fluxo, o que deixa seu código em um estado imprevisível caso seja esperado mais conteúdo. Em geral, não confie na conclusão do fluxo e não execute nenhum trabalho em uma operação baseada em fluxo que não possa ser revertida caso o fluxo seja anulado. Isso também se aplica à situação em que uma mensagem pode ser malformada após o corpo do streaming (por exemplo, pode estar faltando uma marca de fim para o envelope SOAP ou pode ter um segundo corpo da mensagem).

  • O uso do recurso IExtensibleDataObject pode fazer com que dados confidenciais sejam emitidos. Se você aceitar dados de uma fonte não confiável em contratos de dados com IExtensibleObjectData e posteriormente reemitir em um canal seguro em que as mensagens são assinadas, você provavelmente está atestando dados que não tem conhecimento. Além disso, o estado geral que você está enviando pode ser inválido se levar em conta os dados conhecidos e desconhecidos. Evite essa situação definindo seletivamente a configuração da propriedade de dados de extensão para null ou desabilitando seletivamente o recurso IExtensibleObjectData.

Importação de esquema

Normalmente, o processo de importação de esquema para gerar tipos ocorre somente no tempo de design, por exemplo, ao usar a Ferramenta de Utilitário de Metadados do ServiceModel (Svcutil.exe) em um serviço Web para gerar uma classe de cliente. No entanto, em cenários mais avançados, você pode processar o esquema no tempo de execução. Lembre-se de que essa ação pode gerar riscos de negação de serviço. Algum esquema pode levar muito tempo para ser importado. Nunca use o componente de importação de esquema XmlSerializer nesses cenários se os esquemas forem provenientes provavelmente de uma fonte não confiável.

Ameaças específicas à integração do AJAX ASP.NET

Quando o usuário implementa WebScriptEnablingBehavior ou WebHttpBehavior, o WCF mostra um ponto de extremidade que pode aceitar mensagens XML e JSON. No entanto, há apenas um conjunto de cotas de leitor usado tanto pelo leitor XML quanto pelo leitor JSON. Algumas configurações de cota podem ser apropriadas para um leitor, mas muito grandes para o outro.

Ao implementar WebScriptEnablingBehavior, o usuário tem a opção de mostrar um proxy JavaScript no ponto de extremidade. Os seguintes problemas de segurança devem ser considerados:

  • Informações sobre o serviço (nomes de operação, nomes de parâmetros e assim por diante) podem ser obtidas pela análise do proxy JavaScript.

  • Ao usar o ponto de extremidade JavaScript, informações confidenciais e particulares podem ser retidas no cache do navegador da Web do cliente.

Uma observação sobre componentes

O WCF é um sistema flexível e personalizável. A maioria dos conteúdos deste tópico se concentra nos cenários de uso mais comuns do WCF. No entanto, é possível compor componentes que o WCF fornece de várias maneiras diferentes. É importante entender as implicações de segurança do uso de cada componente. Especialmente:

  • Quando você precisa usar leitores XML, use os leitores da classe XmlDictionaryReader fornece em relação a qualquer outro leitor. Leitores seguros são criados pelos métodos CreateTextReader, CreateBinaryReader ou CreateMtomReader. Não use o método Create. Sempre configure os leitores com cotas seguras. Os mecanismos de serialização no WCF são seguros somente quando usados com leitores XML seguros do WCF.

  • Ao usar DataContractSerializer para desserializar dados potencialmente não confiáveis, sempre defina a propriedade MaxItemsInObjectGraph.

  • Ao criar uma mensagem, defina o parâmetro maxSizeOfHeaders se MaxReceivedMessageSize não oferece proteção suficiente.

  • Ao criar um codificador, sempre configure as cotas relevantes, como MaxSessionSize e MaxBufferSize.

  • Ao usar um filtro de mensagem XPath, defina NodeQuota para limitar a quantidade de nós XML feita pelo filtro. Não use expressões XPath que possam levar muito tempo para serem computadas sem visitar muitos nós.

  • Em geral, ao usar qualquer componente que aceite uma cota, entenda suas implicações de segurança e defina-a como um valor seguro.

Confira também