Compartilhar via


Considerações de segurança para dados

Ao lidar com dados no WCF (Windows Communication Foundation), você deve considerar várias categorias de ameaças. A lista a seguir mostra as classes de ameaças mais importantes relacionadas ao processamento de dados. O WCF fornece ferramentas para atenuar essas ameaças.

  • Negação de serviço

    Ao receber dados não confiáveis, os dados podem fazer com que o lado receptor acesse uma quantidade desproporcional de vários recursos, como memória, threads, conexões disponíveis ou ciclos de processador, causando cálculos longos. Um ataque de negação de serviço contra um servidor pode causar uma falha e não conseguir processar mensagens de outros clientes legítimos.

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

    A entrada de dados não confiáveis faz com que o lado receptor execute o código que ele não pretendia.

  • Divulgação de informações

    O invasor remoto força a parte receptora a responder às suas solicitações de forma a divulgar mais informações do que pretende.

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

Diversas partes na infraestrutura do Windows Communication Foundation (WCF) executam 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 nenhuma vulnerabilidade de segurança exista. 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 set acessador, alocar uma matriz com base no valor da propriedade, você exporá a possibilidade de um ataque de negação de serviço se uma mensagem mal-intencionada contiver um valor extremamente grande para esse membro de dados. Em geral, evite alocações baseadas 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. A segurança da implementação do serviço é de sua responsabilidade. É fácil criar implementações de operação inseguras inadvertidamente 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 que está sendo passada for apenas uma única letra, seu código poderá tentar criar uma mensagem maior do que toda a memória disponível, fazendo com que todo o serviço falhe. (Um OutOfMemoryException não é recuperável no .NET Framework e sempre resulta no encerramento do seu aplicativo.)

Você deve garantir que nenhum código mal-intencionado esteja conectado aos vários pontos de extensibilidade. Isso é especialmente relevante ao executar sob confiança parcial, lidar com tipos de assemblies parcialmente confiáveis ou criar componentes utilizáveis por 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 dá suporte apenas a um subconjunto limitado do modelo de programação de contrato de dados , por exemplo, não há suporte para membros de dados privados ou tipos que usam o SerializableAttribute atributo. Para obter mais informações, consulte Confiança Parcial.

Observação

O CAS (Code Access Security) foi preterido em todas as versões do .NET Framework e do .NET. As versões recentes do .NET não honram as anotações CAS e geram erros se as APIs relacionadas ao CAS forem usadas. Os desenvolvedores devem buscar meios alternativos para realizar tarefas de segurança.

Evitando a divulgação de informações não intencionais

Ao criar tipos serializáveis com segurança em mente, a divulgação de informações é uma possível preocupaçã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 aplicando o DataMemberAttribute atributo 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 expor um conjunto de dados quando usado com o DataContractSerializer e outro conjunto de dados quando usado com o XmlSerializer. Usar acidentalmente o serializador errado pode levar à divulgação de informações.

  • 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.

Prevenção de ataques de negação de serviço

Cotas

Fazer com que o lado receptor aloque uma quantidade significativa de memória é um possível ataque de negação de serviço. Embora esta seção se concentre em problemas de consumo de memória decorrentes de mensagens grandes, outros ataques podem ocorrer. Por exemplo, as mensagens podem usar uma quantidade desproporcional de tempo de processamento.

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

A situação de cota excedida é 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 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 MaxReceivedMessageSize cota (encontrada em várias classes) for definida como 128 KB, isso 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 discuta algumas dessas cotas, essas cotas são descritas detalhadamente em Cotas de Transporte.

Vulnerabilidade de hashtable

Existe uma vulnerabilidade quando os contratos de dados contêm tabelas hash ou coleções. O problema ocorre se um grande número de valores for inserido em uma tabela hash, onde um grande número desses valores gera o mesmo valor de hash. Isso pode ser usado como um ataque de DOS. Essa vulnerabilidade pode ser atenuada definindo a 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 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, não transmitido, 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 dentro de um fator constante. Por exemplo, se o MaxReceivedMessageSize tiver 1 MB e uma mensagem de 1 MB for recebida e depois desserializada, a memória adicional será necessária para conter o grafo do objeto desserializado, resultando em consumo total de memória bem acima 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 privados pode ser instanciado com a construção XML "<MyContract/>". Esse XML faz com que a memória seja acessada para 150 campos. Observe que os componentes 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 é mitigada definindo o limite MaxDepth para restringir o nível de aninhamento XML, conforme discutido posteriormente na seção "Usando XML com segurança" no tópico.

Definir cotas MaxReceivedMessageSize adicionais é especialmente importante ao usar a codificação XML binária. Usar a codificação binária é um pouco equivalente à compactação: um pequeno grupo de bytes na mensagem de entrada pode representar muitos dados. Assim, até mesmo uma mensagem que se encaixa no MaxReceivedMessageSize limite pode ocupar muito mais memória de forma totalmente expandida. Para atenuar 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 como MaxReceivedMessageSize um valor extremamente grande, esperando que quase nenhum dado seja armazenado em buffer na memória e a mensagem seja transmitida 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 protegerá mais contra a mensagem que acessa toda a memória disponível.

Para atenuar 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 acontece 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 com MaxReceivedMessageSize, lembre-se 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 guarda em buffer os cabeçalhos SOAP e os erros SOAP, bem como todas as partes MIME encontradas que não estão na ordem de leitura natural em uma mensagem MTOM (Mecanismo de Otimização de Transmissão de Mensagem). Essa configuração limita a quantidade de buffer 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 da classe CreateMessage aceitam um parâmetro Message. O WCF passa o MaxBufferSize valor para esse parâmetro para limitar a quantidade de buffer de cabeçalho SOAP. É importante definir esse parâmetro ao usar a Message classe 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 MaxBufferSize de transporte. No entanto, ao usar o elemento de associação do codificador de mensagens MTOM para construir uma associação personalizada, é importante definir a MaxBufferSize propriedade 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. Isso é feito para que namespaces e atributos sejam 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 mitigados 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.

Combinando 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 mensagens grandes agora também podem ser enviadas para a segunda operação e o desserializador armazena 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 baseadas em fluxo e não baseadas em fluxo no mesmo contrato. Se você precisar misturar absolutamente os dois modelos de programação, use as seguintes precauções:

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

  • Defina a propriedade MaxItemsInObjectGraph de DataContractSerializer para um valor seguro. Essa cota também está disponível no ServiceBehaviorAttribute atributo 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. Ao desserializar matrizes, cada entrada de matriz é contada como um objeto separado.

  • Defina todas as cotas de leitor XML para 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.

  • Examine a lista de tipos conhecidos, tendo em mente que qualquer um deles pode ser instanciado 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 IXmlSerializable interface que armazena muitos dados em buffer. Não adicione esses tipos à lista de tipos conhecidos.

  • Não use as matrizes XmlElement, XmlNode e Byte, ou tipos que implementem ISerializable em um contrato.

  • Não use as matrizes XmlElement, XmlNodeByte 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 com e sem streaming no mesmo serviço se você estiver usando o XmlSerializer, porque ele não tem a proteção da MaxItemsInObjectGraph quota.

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 lento ou receptor de dados. Enquanto aguarda o envio ou recebimento de dados, recursos, como threads e conexões disponíveis, se esgotam. Essa situação pode surgir como resultado de um ataque mal-intencionado ou de um remetente/receptor legítimo em uma conexão de rede lenta.

Para atenuar esses ataques, defina o tempo 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 conjunto de informações 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 possível fonte 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, que são descritas na seção a seguir.

Ao trabalhar diretamente com leitores XML (como ao escrever seu próprio codificador personalizado ou ao trabalhar diretamente com a Message classe), sempre use os leitores seguros do WCF quando houver uma 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. Eles não criam um leitor WCF. Em vez disso, um leitor é criado 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 lidos em uma única Read operação de leitura da tag inicial do elemento e seus atributos. (Em casos não transmitidos, 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 muitos atributos XML pode usar um tempo de processamento desproporcional porque os nomes de atributo precisam ser verificados quanto à exclusividade. MaxBytesPerRead atenua essa ameaça.

ProfundidadeMáxima

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

  • MaxDepth interage com MaxBytesPerRead: o leitor sempre mantém dados na memória para o elemento atual e todos os seus ancestrais, portanto, o consumo máximo de memória do leitor é 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. Existe uma correlação direta entre o aninhamento XML e o aninhamento de objeto, tanto para DataContractSerializer quanto para XmlSerializer. Use MaxDepth para atenuar essa ameaça.

MaxNameTableCharCount

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

MaxStringContentLength

Essa cota limita o tamanho máximo da cadeia de caracteres que o leitor XML retorna. Essa cota não limita o consumo de memória no próprio leitor XML, mas no componente que está usando o leitor. Por exemplo, quando o DataContractSerializer usa um leitor protegido com MaxStringContentLength, ele não desserializa cadeias de caracteres maiores que essa cota. Ao usar a XmlDictionaryReader classe diretamente, nem todos os métodos respeitam essa cota, mas apenas os métodos projetados especificamente para ler cadeias de caracteres, como o ReadContentAsString método. 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 primitivos que o leitor XML retorna, incluindo matrizes de bytes. Essa cota não limita o consumo de memória no próprio leitor XML, mas em qualquer componente que esteja usando o leitor. Por exemplo, quando o DataContractSerializer usa um leitor assegurado com MaxArrayLength, ele não desserializa matrizes de bytes maiores que essa cota. É importante definir essa cota ao tentar misturar modelos de programação em buffer e streaming em um único contrato. Tenha em mente que, ao usar a XmlDictionaryReader classe diretamente, apenas os métodos projetados 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

A codificação XML binária suportada pelo WCF inclui um recurso de cadeias de caracteres de dicionário. Uma cadeia de caracteres grande pode ser codificada usando apenas alguns bytes. Isso permite ganhos de desempenho significativos, mas introduz novas ameaças de negação de serviço que devem ser atenuadas.

Há dois tipos de dicionários: estáticos e dinâmicos. 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 que o WCF usa por padrão são suficientemente grandes para representar uma ameaça séria 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 introduzir 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 as 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 precisam 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 no dicionário estático.

A primeira ameaça que deve ser atenuada é 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 MaxReceivedMessageSize cota 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 o limite for excedido ao ler uma mensagem, o QuotaExceededException é lançado como de costume. Se a cota for excedida ao escrever uma mensagem, as cadeias de caracteres que causam a superação da cota serão gravadas como as-is, sem usar o recurso de dicionários dinâmicos.

Ameaças de expansão de dicionário

Uma classe significativa de ataques específicos do binário surge da expansão do dicionário. Uma mensagem pequena no formato binário 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 MaxSessionSize cota, pois nenhuma cadeia de caracteres de dicionário dinâmica excede o tamanho máximo de todo o dicionário.

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

Quando se utiliza codificação binária juntamente com a interface DataContractSerializer, a interface IExtensibleDataObject pode ser utilizada de forma incorreta para montar um ataque de expansão de dicionário. Essa interface essencialmente fornece armazenamento ilimitado para dados arbitrários que não fazem parte do contrato. Se as cotas não puderem ser definidas baixo o suficiente para que MaxSessionSize multiplicada por MaxReceivedMessageSize não represente um problema, desabilite o recurso IExtensibleDataObject ao usar a codificação binária. Defina a IgnoreExtensionDataObject propriedade como true no ServiceBehaviorAttribute atributo. Como alternativa, não implemente a IExtensibleDataObject interface. Para obter mais informações, consulte Forward-Compatible Data Contracts.

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
Transmitir mensagens grandes, texto ou codificação MTOM MaxBufferSize e todos ReaderQuotas
Transmitir mensagens grandes, 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 o XmlSerializer, consulte a documentação relevante.) O modelo de segurança para o XmlSerializer é semelhante ao do DataContractSerializer, diferenciando-se principalmente nos detalhes. Por exemplo, o XmlIncludeAttribute atributo é usado para inclusão de tipo em vez do KnownTypeAttribute atributo. No entanto, algumas ameaças exclusivas ao 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, independentemente de o tipo ser mal-intencionado ou apenas ter 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 do XmlSerializer, mas difere do comportamento do NetDataContractSerializer, BinaryFormattere do SoapFormatter. O acoplamento solto introduz um grau de segurança, pois o invasor remoto não pode indicar um tipo arbitrário a ser carregado apenas nomeando esse 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, o DataContractSerializer suporta polimorfismo. Um membro de dados pode ser declarado como Object, mas os dados de entrada podem conter uma Customer instância. Isso é possível apenas se o tipo Customer tiver sido "conhecido" pelo desserializador através de um destes mecanismos:

  • KnownTypeAttribute atributo aplicado a um tipo.

  • KnownTypeAttribute atributo 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.

Depois que um tipo conhecido estiver no escopo, ele poderá ser carregado a qualquer momento e instâncias do tipo poderão ser criadas, mesmo que o contrato proíba realmente usá-lo. Por exemplo, suponha 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 escrever um método que retorna uma lista de tipos conhecidos ou ao passar uma lista diretamente para o DataContractSerializer construtor, 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á seguro. Sempre use nomes fortes na configuração (especificando a chave pública do assembly assinado em que o tipo reside), 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ê especificar uma determinada versão na configuração, estará sujeito ao seguinte risco: um tipo pode apresentar uma vulnerabilidade de segurança que poderá ser corrigida em uma versão futura, mas a versão vulnerável ainda será carregada porque foi explicitamente definida na configuração.

Ter muitos tipos conhecidos tem outra consequência: o DataContractSerializer cria um cache de código de serialização/desserialização no domínio da aplicação, com uma entrada para cada tipo que ele precisa serializar e desserializar. Esse cache nunca é limpo desde que o domínio do aplicativo esteja em execução. Portanto, um invasor que esteja ciente de que um aplicativo usa muitos tipos conhecidos pode causar a desserialização de todos esses tipos, fazendo com 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 internas que devem ser impostas. Deve-se tomar 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 como esta, contornar 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>

Essa situação pode ser evitada por estar 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 desenvolva tipos de contrato de dados para depender de qualquer ordem específica em que os definidores de propriedade devem ser chamados.

  • Tome cuidado usando tipos herdados marcados com o SerializableAttribute atributo. 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 projetados 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, zeroou invalid.

  • Nunca confie em um grafo de objeto desserializado de uma fonte de dados não confiável sem validá-lo primeiro. 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 poderá 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. Isso é semelhante ao BinaryFormatter e ao 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 maneira fornecida de conectar esse mecanismo de serialização; o código personalizado deve ser escrito. 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 relevante em Serialização e Desserialização.

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

Mesmo quando usados com dados confiáveis, os dados de entrada podem especificar insuficientemente o tipo a ser carregado, especialmente se a AssemblyFormat propriedade estiver definida como Simple. Qualquer pessoa com acesso ao diretório do aplicativo ou ao cache de assembly global pode substituir um tipo malicioso por aquele que deveria 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 preocupação de segurança com a NetDataContractSerializer é uma negação de serviço, não uma ameaça de execução de código mal-intencionado. Ao usar a NetDataContractSerializercota, sempre defina a MaxItemsInObjectGraph cota 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.

Ameaças XmlSerializer-Specific

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

XmlSerializer gera assemblies de serialização em tempo de execução que contêm código responsável pela serialização e desserialização; 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, ele poderá 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 que isso aconteça.

Ele 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 é ativado sempre que pode ser encontrado um conjunto de serialização adequado pelo XmlSerializer. 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 seus tipos serializáveis não estiver assinado, o XmlSerializer não poderá executar essa verificação e usa qualquer assembly que tenha o nome correto. Isso possibilita 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 MaxItemsInObjectGraph cota (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 preocupações a seguir em relação a 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, desserializando-o).

  • Ao usar componentes de serialização, nunca declare nenhuma permissão antes desse uso, mesmo que todo o cenário de serialização esteja dentro do escopo de sua declaração 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 (suportes), tipos sendo serializados ou por outros meios, o código parcialmente confiável pode fazer com que o serializador produza uma grande quantidade de dados no fluxo serializado, o que pode resultar em uma Negação de Serviço (DoS) ao destinatário desse fluxo. Se você estiver serializando dados destinados a um destino que seja sensível a ameaças do DoS, não serialize tipos parcialmente confiáveis ou 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 Securely".

  • 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 tipos em que você confia. 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 os dados neles) vierem de código parcialmente confiável, trate o objeto desserializado resultante como dados não confiáveis.

  • O fato de o ExtensionDataObject tipo não ter 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 ela 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, não é possível especificar que apenas uma determinada classe possa ter seus membros internos serializados. É claro que você ainda pode optar por não serializar um membro interno específico, simplesmente não adicionando um DataMemberAttribute atributo a esse membro. Da mesma forma, um desenvolvedor também pode optar por tornar um membro interno em vez de privado ou protegido, com pequenas preocupações de visibilidade.

    A segunda desvantagem é que ele ainda não dá suporte a membros privados ou protegidos.

    Para ilustrar o uso do InternalsVisibleToAttribute atributo 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 relação de confiança parcial. Agora, sem o InternalsVisibleToAttribute atributo, o aplicativo falhará, lançando uma SecurityException, o que indica que membros não públicos não podem ser serializados em ambiente de confiança parcial.

    No entanto, se adicionarmos 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 preocupações de Gerenciamento de Estado

Vale a pena mencionar algumas outras preocupações relacionadas ao gerenciamento de estado de objeto:

  • Ao usar o modelo de programação baseado em fluxo com um transporte de streaming, o processamento da mensagem ocorre à medida que a mensagem chega. O remetente da mensagem pode anular a operação de envio no meio do fluxo, deixando seu código em um estado imprevisível se mais conteúdo for esperado. 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 da transmissão (por exemplo, pode estar faltando uma tag de fechamento para o envelope SOAP, ou pode ter um segundo corpo de mensagem).

  • O uso do IExtensibleDataObject recurso pode fazer com que dados confidenciais sejam emitidos. Se você estiver aceitando dados de uma fonte não confiável em contratos de dados com IExtensibleObjectData e posteriormente reemitindo-os em um canal seguro em que as mensagens são assinadas, você potencialmente está atestando dados sobre os quais você não sabe nada. Além disso, ao levar em conta tanto os dados conhecidos quanto os desconhecidos, o estado geral que você está enviando pode ser inválido. Evite essa situação definindo seletivamente a propriedade de dados de extensão para null ou desabilitando seletivamente o IExtensibleObjectData recurso.

Importação de esquema

Normalmente, o processo de importação de esquema para gerar tipos ocorre somente em tempo de design, por exemplo, ao usar a Ferramenta de Utilitário de Metadados 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 em runtime. Lembre-se de que isso pode expô-lo a riscos de negação de serviço. Alguns esquemas podem levar muito tempo para serem importados. Nunca use o XmlSerializer componente de importação de esquema nesses cenários se os esquemas possivelmente forem provenientes de uma fonte não confiável.

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

Quando o usuário implementa WebScriptEnablingBehavior ou WebHttpBehavioro WCF expõe um ponto de extremidade que pode aceitar mensagens XML e JSON. No entanto, há apenas um conjunto de cotas de leitor, usadas 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 expor um proxy JavaScript no endpoint. Os seguintes problemas de segurança devem ser considerados:

  • Informações sobre o serviço (nomes de operação, nomes de parâmetro e assim por diante) podem ser obtidas examinando o proxy JavaScript.

  • Ao usar o endpoint JavaScript, informações confidenciais e privadas podem ser retidas no cache do navegador Web do cliente.

Uma observação sobre componentes

O WCF é um sistema flexível e personalizável. A maior parte do conteúdo 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. Em particular:

  • Quando você precisar usar leitores XML, use os leitores que a XmlDictionaryReader classe fornece em oposição a qualquer outro leitor. Leitores seguros são criados usando CreateTextReader métodos, CreateBinaryReader ou CreateMtomReader métodos. Não use o Create método. 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 o DataContractSerializer para desserializar dados potencialmente não confiáveis, sempre defina a MaxItemsInObjectGraph propriedade.

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

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

  • Ao usar um filtro de mensagem XPath, defina o valor de NodeQuota para limitar a quantidade de nós XML que o filtro visita. 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.

Consulte também