O que é estado de exibição?
O estado de exibição são informações que são trocadas entre páginas de WebForms (.aspx) em um aplicativo ASP.NET. A marcação HTML para o campo __VIEWSTATE é semelhante à seguinte:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
Um exemplo de um item que pode ser armazenado no campo __VIEWSTATE é o texto de um controle Button. Se um usuário clicar no botão, o manipulador de eventos Button_Click poderá extrair o texto do Botão do campo de estado de exibição. Consulte o tópico Visão geral do estado de exibição de ASP.NET no site do Microsoft Developer Network (MSDN) para obter uma visão geral muito mais detalhada do estado de exibição ASP.NET.
Como o campo __VIEWSTATE contém informações importantes que são usadas para reconstruir a página no postback, certifique-se de que um invasor não possa adulterar esse campo. Se um invasor enviar uma carga __VIEWSTATE mal-intencionada, o invasor poderá enganar o aplicativo para executar uma ação que, de outra forma, não teria realizado.
Para evitar esse tipo de ataque de violação, o campo __VIEWSTATE é protegido por um código de autenticação de mensagem (MAC). ASP.NET valida o MAC enviado junto com a carga __VIEWSTATE quando ocorre um postback. A chave usada para calcular o MAC é especificada no elemento do aplicativo no arquivo Web.config. Como o invasor não pode adivinhar o conteúdo do <elemento machineKey> , o invasor não poderá fornecer um MAC válido se o invasor tentar adulterar a carga __VIEWSTATE. ASP.NET detectará que um MAC válido não foi fornecido e ASP.NET rejeitará a solicitação mal-intencionada.
O que causa erros de validação MAC?
Um erro de validação MAC será semelhante ao seguinte exemplo:
Observação
Erro do Servidor no Aplicativo '/'.
Falha na validação do viewstate MAC. Se esse aplicativo for hospedado por um web farm ou cluster, certifique-se de que <a configuração de machineKey> especifique a mesma validationKey e o mesmo algoritmo de validação. AutoGenerate não pode ser usado em um cluster.
Descrição: ocorreu uma exceção sem tratamento durante a execução da solicitação da Web atual. Examine o rastreamento de pilha para obter mais informações sobre o erro e onde ele se originou no código.
Detalhes da exceção: System.Web.HttpException: falha na validação do viewstate MAC. Se esse aplicativo for hospedado por um Web Farm ou cluster, certifique-se de que <a configuração machineKey> especifique a mesma validationKey e o mesmo algoritmo de validação. AutoGenerate não pode ser usado em um cluster.
Erro de origem: [Nenhuma linha de origem relevante]
Arquivo de origem: ... Linha: 0
Rastreamento de Pilha:
[ViewStateException: viewstate inválido.
IP do cliente: ::1
Porto: 40653
Referência: http://localhost:40643/MyPage.aspx
Caminho: /MyPage.aspx
Agente do usuário: Mozilla / 5.0 (compatível; MSIE 10.0; Windows NT 6.2; UAU64; Trident/6.0)
ViewState: ...]
[HttpException (0x80004005): falha na validação do MAC viewstate. Se esse aplicativo for hospedado por um Web Farm ou cluster, certifique-se de que <a configuração machineKey> especifique a mesma validationKey e o mesmo algoritmo de validação. AutoGenerate não pode ser usado em um cluster.
Consulte http://go.microsoft.com/fwlink/?LinkID=314055 para obter mais informações.]
System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError) +190
System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState) +46
System.Web.UI.ObjectStateFormatter.Deserialize(String inputString, Purpose purpose) +861
System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter2.Deserialize(String serializedState, Purpose purpose) +51
System.Web.UI.Util.DeserializeWithAssert(IStateFormatter2 formatter, String serializedState, Purpose purpose) +67
System.Web.UI.HiddenFieldPageStatePersister.Load() +444
System.Web.UI.Page.LoadPageStateFromPersistenceMedium() +368
System.Web.UI.Page.LoadAllState() +109
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +7959
System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +429
System.Web.UI.Page.ProcessRequest() +125
System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +48
System.Web.UI.Page.ProcessRequest(HttpContext context) +234
ASP.mypage_aspx. ProcessRequest(contexto HttpContext) em ... :0
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +1300
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +140
Causa 1: o aplicativo Web está sendo executado em um farm (ambiente de vários servidores)
ASP.NET gera automaticamente uma chave criptográfica para cada aplicativo e armazena a chave no hive do Registro HKCU. Essa chave gerada automaticamente será usada se não houver nenhum elemento machineKey> explícito <na configuração do aplicativo. No entanto, como essa chave gerada automaticamente é local para o computador que criou a chave, esse cenário causa um problema para aplicativos executados em um farm. Cada servidor do farm gerará sua própria chave local, e nenhum dos servidores do farm concordará sobre qual chave usar. O resultado é que, se um servidor gerar uma carga __VIEWSTATE que um servidor diferente consome, o consumidor experimentará uma falha de validação MAC.
Resolução 1a: criar um elemento machineKey> explícito <
Ao adicionar um elemento machineKey> explícito <ao arquivo Web.config do aplicativo, o desenvolvedor instrui ASP.NET a não usar a chave criptográfica gerada automaticamente. Consulte o Apêndice A para obter instruções sobre como gerar um <elemento machineKey> . Depois que esse elemento for adicionado ao arquivo Web.config, reimplante o aplicativo em cada servidor no farm.
Observação Alguns serviços de hospedagem na Web, como sites do Microsoft Azure, realizam etapas para sincronizar a chave gerada automaticamente de cada aplicativo em seus servidores back-end. Isso permite que os aplicativos que não especificaram um elemento machineKey> explícito <continuem trabalhando nesses ambientes, mesmo que o aplicativo esteja sendo executado em um farm. Se o seu aplicativo estiver em execução em um serviço de hospedagem de terceiros, entre em contato com seu provedor de hospedagem para determinar se essa situação se aplica a você.
Solução 1b: habilitar a afinidade no balanceador de carga
Se seus sites estiverem operando por trás de um balanceador de carga, você poderá habilitar a afinidade do servidor para contornar o problema temporariamente. Isso ajuda a garantir que um determinado cliente interaja apenas com um servidor físico atrás do balanceador de carga, de modo que todos os conteúdos criptográficos sejam gerados e consumidos pelo mesmo servidor.
Isso não deve ser considerado uma solução de longo prazo para o problema. Mesmo quando a afinidade do servidor está habilitada, a maioria dos balanceadores de carga redirecionará o cliente para um servidor físico diferente se o servidor original com o qual os balanceadores de carga foram afinidadedos ficar offline. Isso faz com que o novo servidor rejeite cargas criptográficas (como __VIEWSTATE, tíquetes de autenticação de formulários, MVCs, tokens antifalsificação e outros serviços) que o cliente tem atualmente.
O uso de um elemento machineKey> explícito <e a reimplantação do aplicativo devem ser preferidos em vez de habilitar a afinidade de servidor.
Causa 2: o processo de trabalho usa a identidade do pool de aplicativos do IIS 7.0
O IIS (Serviços de Informações da Internet) 7.0 (Windows Vista, Windows Server 2008) introduziu a identidade do pool de aplicativos, um novo mecanismo de isolamento que ajuda a aumentar a segurança para servidores que executam ASP.NET aplicativos. No entanto, os sites que estão sendo executados sob a identidade do pool de aplicativos não têm acesso ao Registro HKCU. É aqui que o runtime do ASP.NET armazena suas chaves machineKey> geradas <automaticamente. O resultado é que ASP.NET não pode persistir a chave gerada automaticamente quando o pool de aplicativos é redefinido. Portanto, sempre que w3wp.exe é redefinida, uma nova chave temporária é gerada.
Observação Esse não é um problema no IIS 7.5 (Windows 7, Windows Server 2008 R2) e versões posteriores. Nessas versões do IIS, ASP.NET pode persistir suas chaves geradas automaticamente em um local diferente que sobreviva às redefinições do pool de aplicativos.
Solução 2a: usar o utilitário aspnet_regiis
ASP.NET instalações contêm um utilitário, aspnet_regiis.exe. Esse utilitário permite que ASP.NET interface com o IIS execute as configurações necessárias para executar um aplicativo gerenciado. Uma dessas configurações cria as chaves necessárias no hive do Registro para habilitar a persistência de chaves de computador geradas automaticamente.
Primeiro, você precisa determinar qual pool de aplicativos seu site está usando. Isso pode ser determinado usando o utilitário inetmgr incluído no IIS. Selecione seu site na exibição de árvore à esquerda, clique com o botão direito do mouse em Gerenciar Website clique em Configurações avançadas. A caixa de diálogo exibida mostrará o nome do pool de aplicativos.
Para criar scaffold das chaves do Registro apropriadas para um pool de aplicativos do ASP.NET 4.0, siga estas etapas:
Abra um prompt de comando administrativo.
Localize o diretório apropriado, dependendo se o pool de aplicativos é de 32 bits ou 64 bits:
- Pool de aplicativos de 32 bits: cd /d %windir%\Microsoft.NET\Framework\v4.0.30319
- Pool de aplicativos de 64 bits: cd /d %windir%\Microsoft.NET\Framework64\v4.0.30319
Vá para o diretório, digite o seguinte comando e pressione Enter:
aspnet_regiis -ga "IIS APPPOOL\app-pool-name"
Se o pool de aplicativos for um pool de aplicativos ASP.NET 2.0 ou 3.5, siga estas etapas:
Abra um prompt de comando administrativo.
Localize o diretório apropriado, dependendo se o pool de aplicativos é de 32 bits ou 64 bits:
- Pool de aplicativos de 32 bits: cd /d %windir%\Microsoft.NET\Framework\v2.0.50727
- Pool de aplicativos de 64 bits: cd /d %windir%\Microsoft.NET\Framework64\v2.0.50727
Vá para o diretório, digite o seguinte comando e pressione Enter:
aspnet_regiis -ga "IIS APPPOOL\app-pool-name"
Por exemplo, se o pool de aplicativos for chamado Meu Pool de Aplicativos (como na imagem anterior), execute o seguinte comando:
aspnet_regiis -ga "IIS APPPOOL\My App Pool"Observação Os serviços do sistema APPHOSTSVC e WAS podem ter que estar em execução para que o utilitário aspnet_regiis resolve nomes APPPOOL\* do IIS adequadamente.
Resolução 2b: criar um elemento machineKey> explícito <
Ao adicionar um elemento machineKey> explícito <ao arquivo Web.config do aplicativo, o desenvolvedor instrui ASP.NET a não usar a chave criptográfica gerada automaticamente. Consulte o Apêndice A para obter instruções sobre como gerar um <elemento machineKey> .
Causa 3: o pool de aplicativos é configurado usando LoadUserProfile=false
Se o pool de aplicativos estiver sendo executado com uma identidade personalizada, o IIS pode não ter carregado o perfil de usuário para a identidade. Isso tem o efeito colateral de tornar o registro HKCU indisponível para ASP.NET mantenham a machineKey> gerada <automaticamente. Portanto, uma nova chave gerada automaticamente será criada sempre que o aplicativo for reiniciado. Consulte a seção Perfil de usuário no site da Microsoft para obter mais informações.
Solução 3a: usar o utilitário aspnet_regiis
As instruções para isso são as mesmas da Resolução 2a. Consulte essa seção para obter mais informações.
Solução 3b: usar uma machineKey explícita <>
Ao adicionar um elemento machineKey> explícito <ao arquivo Web.config do aplicativo, o desenvolvedor instrui ASP.NET a não usar a chave criptográfica gerada automaticamente. Consulte o Apêndice A para obter instruções sobre como gerar um <elemento machineKey> .
Resolução 3c: provisionar manualmente as chaves do Registro HKCU necessárias
Se você não puder executar o utilitário aspnet_regiis, poderá usar um script Windows PowerShell para provisionar as chaves do Registro apropriadas em HKCU. Consulte o Apêndice B para obter mais informações.
Resolução 3d: Defina LoadUserProfile=true para este pool de aplicativos
Você também pode habilitar o carregamento do perfil de usuário dentro desse pool de aplicativos. Isso torna o hive do Registro HKCU, a pasta temporária e outros locais de armazenamento específicos do usuário disponíveis para o aplicativo. No entanto, isso pode causar maior uso de disco ou memória para o processo de trabalho. Consulte o elemento para obter mais informações sobre como habilitar essa configuração.
Causa 4: a propriedade Page.ViewStateUserKey tem um valor incorreto
Os desenvolvedores de software podem decidir usar a propriedade Page.ViewStateUserKey para adicionar proteção contra falsificação de solicitação entre sites ao campo __VIEWSTATE. Se você usar a propriedade Page.ViewStateUserKey, ela normalmente será definida como um valor como o nome de usuário do usuário atual ou o identificador de sessão do usuário. Os modelos de projeto para aplicativos WebForms no Microsoft Visual Studio 2012 e versões posteriores contêm exemplos que usam essa propriedade. Consulte o tópico da propriedade Page.ViewStateUserKey no site do Microsoft Developer Network (MSDN) para obter mais informações.
Se a propriedade ViewStateUserKey for especificada, seu valor será gravado em __VIEWSTATE em tempo de geração. Quando o campo __VIEWSTATE é consumido, o servidor verifica a propriedade ViewStateUserKey da página atual e a valida em relação ao valor que foi usado para gerar o campo __VIEWSTATE. Se os valores não corresponderem, a solicitação será rejeitada como potencialmente mal-intencionada.
Um exemplo de falha relacionada a ViewStateUserKey seria um cliente que tem duas guias abertas no navegador. O cliente está conectado como Usuário A e, na primeira guia, uma Página é renderizada com um __VIEWSTATE cuja propriedade ViewStateUserKey contém "Usuário A". Na segunda guia, o cliente faz logoff e logon novamente como Usuário B. O cliente volta para a primeira guia e envia o formulário. A propriedade ViewStateUserKey pode conter "Usuário B" (porque é isso que diz o cookie de autenticação do cliente). No entanto, o campo __VIEWSTATE que o cliente enviou contém "Usuário A". Essa incompatibilidade causa a falha.
Solução 4a: verificar se ViewStateUserKey está definido corretamente
Se o aplicativo usar a propriedade ViewStateUserKey, verifique se o valor da propriedade é o mesmo quando o estado de exibição é gerado e quando é consumido. Se você estiver usando o nome de usuário do usuário conectado atualmente, verifique se o usuário ainda está conectado e se a identidade do usuário não foi alterada no momento do postback. Se você estiver usando o identificador de sessão do usuário atual, verifique se a sessão não expirou.
Se você estiver executando em um ambiente de farm, verifique se os <elementos machineKey> correspondem. Consulte o Apêndice A para obter instruções sobre como gerar esses elementos.
Apêndice A: Como gerar um <elemento machineKey>
Observação
Aviso de segurança
Existem muitos sites que gerarão um <elemento machineKey> para você com o clique de um botão. Nunca use um <elemento machineKey> obtido de um desses sites. É impossível saber se essas chaves foram criadas com segurança ou se estão sendo gravadas em um banco de dados secreto. Você só deve usar <os elementos de configuração machineKey> que você mesmo criou.
Para gerar um <elemento machineKey> por conta própria, você pode usar o seguinte script do Windows PowerShell:
# Generates a <machineKey> element that can be copied + pasted into a Web.config file.
function Generate-MachineKey {
[CmdletBinding()]
param (
[ValidateSet("AES", "DES", "3DES")]
[string]$decryptionAlgorithm = 'AES',
[ValidateSet("MD5", "SHA1", "HMACSHA256", "HMACSHA384", "HMACSHA512")]
[string]$validationAlgorithm = 'HMACSHA256'
)
process {
function BinaryToHex {
[CmdLetBinding()]
param($bytes)
process {
$builder = new-object System.Text.StringBuilder
foreach ($b in $bytes) {
$builder = $builder.AppendFormat([System.Globalization.CultureInfo]::InvariantCulture, "{0:X2}", $b)
}
$builder
}
}
switch ($decryptionAlgorithm) {
"AES" { $decryptionObject = new-object System.Security.Cryptography.AesCryptoServiceProvider }
"DES" { $decryptionObject = new-object System.Security.Cryptography.DESCryptoServiceProvider }
"3DES" { $decryptionObject = new-object System.Security.Cryptography.TripleDESCryptoServiceProvider }
}
$decryptionObject.GenerateKey()
$decryptionKey = BinaryToHex($decryptionObject.Key)
$decryptionObject.Dispose()
switch ($validationAlgorithm) {
"MD5" { $validationObject = new-object System.Security.Cryptography.HMACMD5 }
"SHA1" { $validationObject = new-object System.Security.Cryptography.HMACSHA1 }
"HMACSHA256" { $validationObject = new-object System.Security.Cryptography.HMACSHA256 }
"HMACSHA385" { $validationObject = new-object System.Security.Cryptography.HMACSHA384 }
"HMACSHA512" { $validationObject = new-object System.Security.Cryptography.HMACSHA512 }
}
$validationKey = BinaryToHex($validationObject.Key)
$validationObject.Dispose()
[string]::Format([System.Globalization.CultureInfo]::InvariantCulture,
"<machineKey decryption=`"{0}`" decryptionKey=`"{1}`" validation=`"{2}`" validationKey=`"{3}`" />",
$decryptionAlgorithm.ToUpperInvariant(), $decryptionKey,
$validationAlgorithm.ToUpperInvariant(), $validationKey)
}
}
Para aplicativos ASP.NET 4.0, basta chamá-Generate-MachineKey sem parâmetros para gerar um <elemento machineKey> da seguinte maneira:
PS> Generate-MachineKey
<machineKey decryption="AES" decryptionKey="..." validation="HMACSHA256" validationKey="..." />
ASP.NET aplicativos 2.0 e 3.5 não suportam HMACSHA256. Em vez disso, você pode especificar SHA1 para gerar um elemento machineKey> compatível <da seguinte maneira:
PS> Generate-MachineKey -validation sha1
<machineKey decryption="AES" decryptionKey="..." validation="SHA1" validationKey="..." />
Assim que você tiver um <elemento machineKey> , poderá colocá-lo no arquivo Web.config. O <elemento machineKey> só é válido no arquivo Web.config na raiz do seu aplicativo e não é válido no nível da subpasta.
<configuration>
<system.web>
<machineKey ... />
</system.web>
</configuration>
Para obter uma lista completa de algoritmos com suporte, execute a ajuda Generate-MachineKey no prompt Windows PowerShell.
Apêndice B: Provisionando o registro para persistir chaves geradas automaticamente
Por padrão, porque o ASP. As chaves geradas automaticamente pelos NETs são mantidas no Registro HKCU, essas chaves poderão ser perdidas se o perfil do usuário não tiver sido carregado no processo de trabalho do IIS e, em seguida, o pool de aplicativos for reciclado. Esse cenário pode afetar os provedores de hospedagem compartilhada que estão executando pools de aplicativos como contas de usuário padrão do Windows.
Para contornar essa situação, ASP.NET permite persistir as chaves geradas automaticamente no Registro HKLM em vez do Registro HKCU. Isso geralmente é feito usando o utilitário aspnet_regiis (consulte as instruções na seção "Resolução 2a: usar o utilitário aspnet_regiis"). No entanto, para administradores que não desejam executar esse utilitário, o seguinte script do Windows PowerShell pode ser usado:
# Provisions the HKLM registry so that the specified user account can persist auto-generated machine keys.
function Provision-AutoGenKeys {
[CmdletBinding()]
param (
[ValidateSet("2.0", "4.0")]
[Parameter(Mandatory = $True)]
[string] $frameworkVersion,
[ValidateSet("32", "64")]
[Parameter(Mandatory = $True)]
[string] $architecture,
[Parameter(Mandatory = $True)]
[string] $upn
)
process {
# We require administrative permissions to continue.
if (-Not (new-object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Error "This cmdlet requires Administrator permissions."
return
}
# Open HKLM with an appropriate view into the registry
if ($architecture -eq "32") {
$regView = [Microsoft.Win32.RegistryView]::Registry32;
} else {
$regView = [Microsoft.Win32.RegistryView]::Registry64;
}
$baseRegKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView)
# Open ASP.NET base key
if ($frameworkVersion -eq "2.0") {
$expandedVersion = "2.0.50727.0"
} else {
$expandedVersion = "4.0.30319.0"
}
$aspNetBaseKey = $baseRegKey.OpenSubKey("SOFTWARE\Microsoft\ASP.NET\$expandedVersion", $True)
# Create AutoGenKeys subkey if it doesn't already exist
$autoGenBaseKey = $aspNetBaseKey.OpenSubKey("AutoGenKeys", $True)
if ($autoGenBaseKey -eq $null) {
$autoGenBaseKey = $aspNetBaseKey.CreateSubKey("AutoGenKeys")
}
# Get the SID for the user in question, which will allow us to get his AutoGenKeys subkey
$sid = (New-Object System.Security.Principal.WindowsIdentity($upn)).User.Value
# SYSTEM, ADMINISTRATORS, and the target SID get full access
$regSec = New-Object System.Security.AccessControl.RegistrySecurity
$regSec.SetSecurityDescriptorSddlForm("D:P(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;$sid)")
$userAutoGenKey = $autoGenBaseKey.OpenSubKey($sid, $True)
if ($userAutoGenKey -eq $null) {
# Subkey didn't exist; create and ACL appropriately
$userAutoGenKey = $autoGenBaseKey.CreateSubKey($sid, [Microsoft.Win32.RegistryKeyPermissionCheck]::Default, $regSec)
} else {
# Subkey existed; make sure ACLs are correct
$userAutoGenKey.SetAccessControl($regSec)
}
}
}
O exemplo a seguir mostra como provisionar as entradas do Registro HKLM apropriadas para um pool de aplicativos executado como usuário example@contoso.com (esse é o UPN da conta de usuário do Windows). Esse pool de aplicativos é um pool de aplicativos de 32 bits que executa o CLR v2.0 (ASP.NET 2.0 ou 3.5).
PS> Provision-AutoGenKeys -FrameworkVersion 2.0 -Architecture 32 -UPN "example@contoso.com"
Se, em vez disso, o pool de aplicativos for um pool de aplicativos de 64 bits executando o CLR v4.0 (ASP.NET 4.0 ou 4.5), o comando será o seguinte:
PS> Provision-AutoGenKeys -FrameworkVersion 4.0 -Architecture 64 -UPN "example@contoso.com"
Embora as chaves geradas automaticamente sejam armazenadas no HKLM, a subchave do Registro que contém o material criptográfico secreto de cada conta de usuário é adicionada a uma ACL (lista de controle de acesso) para que o material criptográfico não possa ser lido por outras contas de usuário.
Apêndice C: Criptografando o <elemento machineKey> em arquivos de configuração
Os administradores do servidor podem não querer que informações altamente confidenciais, como o material da <chave machineKey> , estejam em formato de texto simples nos arquivos de configuração. Se esse for o caso, os administradores poderão optar por aproveitar um recurso do .NET Framework conhecido como "configuração protegida". Esse recurso permite criptografar determinadas seções dos arquivos .config. Se o conteúdo desses arquivos de configuração for divulgado, o conteúdo dessas seções ainda permanecerá secreto.
Você pode encontrar uma breve visão geral da configuração protegida no site do MSDN. Ele também contém um tutorial sobre como proteger os <elementos connectionStrings> e <machineKey> do arquivo Web.config.