Что такое состояние представления?
Состояние представления — это информация, которая циклическая передача между страницами WebForms (.aspx) в приложении ASP.NET. Разметка HTML для поля __VIEWSTATE выглядит следующим образом:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="..." />
Одним из примеров элемента, который может храниться в поле __VIEWSTATE, является текст элемента управления Button. Если пользователь нажимает кнопку, обработчик событий Button_Click сможет извлечь текст кнопки из поля состояния представления. Более подробный обзор состояния ASP.NET см. в разделе Обзор состояния ASP.NET на веб-сайте Microsoft Developer Network (MSDN).
Так как поле __VIEWSTATE содержит важную информацию, которая используется для восстановления страницы при обратной отправке, убедитесь, что злоумышленник не может изменить это поле. Если злоумышленник отправил вредоносную __VIEWSTATE полезные данные, он потенциально может обмануть приложение, чтобы выполнить действие, которое в противном случае не было бы выполнено.
Чтобы предотвратить атаки такого рода, поле __VIEWSTATE защищено кодом проверки подлинности сообщения (MAC). ASP.NET проверяет MAC-файл, отправляемый вместе с полезными данными __VIEWSTATE при обратной отправке. Ключ, используемый для вычисления MAC, указывается в элементе приложения в файле Web.config. Так как злоумышленник не может угадать содержимое <элемента machineKey> , он не может предоставить допустимый MAC-код, если злоумышленник попытается изменить полезные данные __VIEWSTATE. ASP.NET обнаружит, что допустимый MAC-адрес не предоставлен, и ASP.NET отклонит вредоносный запрос.
Что вызывает ошибки проверки MAC?
Ошибка проверки MAC будет выглядеть примерно так:
Примечание
Ошибка сервера в приложении "/".
Сбой проверки MAC состояния представления. Если это приложение размещено в веб-ферме или кластере, убедитесь, что <конфигурация machineKey> указывает тот же алгоритм проверки и проверки. AutoGenerate нельзя использовать в кластере.
Описание: во время выполнения текущего веб-запроса возникло необработанное исключение. Дополнительные сведения об ошибке и ее происхождении в коде см. в трассировке стека.
Сведения об исключении: System.Web.HttpException: сбой проверки mac состояния представления. Если это приложение размещено в веб-ферме или кластере, убедитесь, что <конфигурация machineKey> указывает тот же алгоритм проверки и проверки. AutoGenerate нельзя использовать в кластере.
Ошибка источника: [Нет соответствующих исходных строк]
Исходный файл: ... Строка: 0
Трассировка стека:
[ViewStateException: недопустимое состояние представления.
IP-адрес клиента: ::1
Порт: 40653
Реферер: http://localhost:40643/MyPage.aspx
Путь: /MyPage.aspx
User-Agent: Mozilla/5.0 (совместимо; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
ViewState: ...]
[HttpException (0x80004005): сбой проверки mac состояния представления. Если это приложение размещено в веб-ферме или кластере, убедитесь, что <конфигурация machineKey> указывает тот же алгоритм проверки и проверки. AutoGenerate нельзя использовать в кластере.
Дополнительные сведения см. в http://go.microsoft.com/fwlink/?LinkID=314055.]
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, Назначение) +861
System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter2.Deserialize(String serializedState, Назначение) +51
System.Web.UI.Util.DeserializeWithAssert(IStateFormatter2 formatter, String serializedState, Назначение) +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) +48
System.Web.UI.Page.ProcessRequest(контекст HttpContext) +234
ASP.mypage_aspx. ProcessRequest(контекст HttpContext) в ... :0
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +1300
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, boolean& completedSynchronously) +140
Причина 1. Веб-приложение работает в ферме (многосерверная среда)
ASP.NET автоматически создает криптографический ключ для каждого приложения и сохраняет ключ в кусте реестра HKCU. Этот автоматически созданный ключ используется, если в конфигурации приложения нет явного <элемента machineKey> . Однако, так как этот автоматически созданный ключ является локальным для компьютера, создавшего ключ, этот сценарий вызывает проблемы для приложений, работающих в ферме. Каждый сервер в ферме создаст собственный локальный ключ, и ни один из серверов в ферме не будет согласовывать, какой ключ следует использовать. В результате, если один сервер создает __VIEWSTATE полезные данные, которые использует другой сервер, потребитель будет испытывать сбой проверки MAC.
Решение 1a. Создание явного <элемента machineKey>
Добавляя явный <элемент machineKey> в файл Web.config приложения, разработчик сообщает ASP.NET не использовать автоматически созданный криптографический ключ. Инструкции по созданию <элемента machineKey> см. в приложении A. После добавления этого элемента в файл Web.config повторно разверните приложение на каждом сервере фермы.
Примечание. Некоторые службы веб-размещения, такие как веб-сайты Microsoft Azure, принимают меры по синхронизации автоматически созданного ключа каждого приложения на серверных серверах. Это позволяет приложениям, которые не указали явный <элемент machineKey> , продолжать работу в этих средах, даже если приложение выполняется в ферме. Если приложение работает в сторонней службе размещения, обратитесь к поставщику услуг размещения, чтобы определить, относится ли эта ситуация к вам.
Решение 1b. Включение сходства в подсистеме балансировки нагрузки
Если сайты работают за подсистемой балансировки нагрузки, можно включить сопоставление серверов, чтобы временно обойти проблему. Это позволяет гарантировать, что любой клиент взаимодействует только с одним физическим сервером за подсистемой балансировки нагрузки, чтобы все полезные данные шифрования создавались и потреблялись одним сервером.
Это не следует рассматривать как долгосрочное решение проблемы. Даже если сопоставление серверов включено, большинство подсистем балансировки нагрузки перенаправляют клиент на другой физический сервер, если исходный сервер, с которым были сопоставлены подсистемы балансировки нагрузки, переходит в автономный режим. Это приводит к тому, что новый сервер отклоняет криптографические полезные данные (такие как __VIEWSTATE, билеты проверки подлинности с форм, маркеры защиты от подделки MVC и другие службы), которые есть у клиента в настоящее время.
Использование явного <элемента machineKey> и повторное развертывание приложения должны быть предпочтительнее, чем включение сопоставления серверов.
Причина 2. Рабочий процесс использует удостоверение пула приложений IIS 7.0
Службы IIS 7.0 (Windows Vista, Windows Server 2008) представили удостоверение пула приложений— новый механизм изоляции, который помогает повысить безопасность серверов, работающих с ASP.NET приложениями. Однако сайты, работающие под удостоверением пула приложений, не имеют доступа к реестру HKCU. Здесь среда выполнения ASP.NET хранит автоматически созданные <ключи machineKey> . В результате ASP.NET не может сохранить автоматически созданный ключ при сбросе пула приложений. Поэтому при каждом сбросе w3wp.exe создается новый временный ключ.
Примечание. Это не проблема в IIS 7.5 (Windows 7, Windows Server 2008 R2) и более поздних версиях. В этих версиях IIS ASP.NET может сохранять автоматически созданные ключи в другом расположении, которое сохраняется при сбросе пула приложений.
Решение 2a. Использование служебной программы aspnet_regiis
ASP.NET установки содержат служебную программуaspnet_regiis.exe. Эта служебная программа позволяет ASP.NET интерфейс со службами IIS для выполнения конфигураций, необходимых для запуска управляемого приложения. Одна из этих конфигураций создает необходимые разделы в кусте реестра, чтобы обеспечить сохранение автоматически созданных ключей компьютера.
Сначала необходимо определить, какой пул приложений используется сайтом. Это можно определить с помощью служебной программы inetmgr, входящей в состав IIS. Выберите свой сайт в представлении дерева слева, щелкните правой кнопкой мыши Управление Website и выберите пункт Дополнительные параметры. В появившемся диалоговом окне отобразится имя пула приложений.
Чтобы создать шаблон соответствующих разделов реестра для пула приложений ASP.NET 4.0, выполните следующие действия.
Откройте командную строку администратора.
Найдите соответствующий каталог в зависимости от того, является ли пул приложений 32-разрядным или 64-разрядным:
- Пул 32-разрядных приложений: cd /d %windir%\Microsoft.NET\Framework\v4.0.30319
- Пул 64-разрядных приложений: cd /d %windir%\Microsoft.NET\Framework64\v4.0.30319
Перейдите в каталог, введите следующую команду и нажмите клавишу ВВОД:
aspnet_regiis -ga "IIS APPPOOL\app-pool-name"
Если пул приложений является пулом приложений ASP.NET 2.0 или 3.5, выполните следующие действия.
Откройте командную строку администратора.
Найдите соответствующий каталог в зависимости от того, является ли пул приложений 32-разрядным или 64-разрядным:
- Пул 32-разрядных приложений: cd /d %windir%\Microsoft.NET\Framework\v2.0.50727
- Пул 64-разрядных приложений: cd /d %windir%\Microsoft.NET\Framework64\v2.0.50727
Перейдите в каталог, введите следующую команду и нажмите клавишу ВВОД:
aspnet_regiis -ga "IIS APPPOOL\app-pool-name"
Например, если пул приложений называется My App Pool (как показано на предыдущем рисунке), выполните следующую команду:
aspnet_regiis -ga "IIS APPPOOL\My App Pool"Примечание. Системные службы APPHOSTSVC и WAS, возможно, должны быть запущены для служебной программы aspnet_regiis, чтобы соответствующим образом разрешить имена IIS APPPOOL\*.
Решение 2b. Создание явного <элемента machineKey>
Добавляя явный <элемент machineKey> в файл Web.config приложения, разработчик сообщает ASP.NET не использовать автоматически созданный криптографический ключ. Инструкции по созданию <элемента machineKey> см. в приложении A.
Причина 3. Пул приложений настраивается с помощью LoadUserProfile=false
Если пул приложений работает с пользовательским удостоверением, службы IIS, возможно, не загрузили профиль пользователя для удостоверения. Это имеет побочный эффект, делая реестр HKCU недоступным для ASP.NET для сохранения автоматически созданного <machineKey>. Таким образом, при каждом перезапуске приложения будет создаваться автоматически созданный ключ. Дополнительные сведения см. в разделе Профиль пользователя на веб-сайте Майкрософт.
Решение 3a. Использование служебной программы aspnet_regiis
Инструкции для этого совпадают с резолюцией 2a. Дополнительные сведения см. в этом разделе.
Решение 3b. Использование явного <machineKey>
Добавляя явный <элемент machineKey> в файл Web.config приложения, разработчик сообщает ASP.NET не использовать автоматически созданный криптографический ключ. Инструкции по созданию <элемента machineKey> см. в приложении A.
Решение 3c. Подготовка необходимых разделов реестра HKCU вручную
Если вы не можете запустить служебную программу aspnet_regiis, можно использовать скрипт Windows PowerShell для подготовки соответствующих разделов реестра в HKCU. Дополнительные сведения см. в приложении B .
Решение 3d: задайте LoadUserProfile=true для этого пула приложений
Вы также можете включить загрузку профиля пользователя в этом пуле приложений. Это делает куст реестра HKCU, временную папку и другие расположения хранилища, зависящие от пользователя, доступными для приложения. Однако это может привести к увеличению использования диска или памяти для рабочего процесса. Дополнительные сведения о включении этого параметра см. в элементе .
Причина 4. Свойство Page.ViewStateUserKey имеет неправильное значение
Разработчики программного обеспечения могут использовать свойство Page.ViewStateUserKey для добавления защиты от подделки межсайтовых запросов в поле __VIEWSTATE. Если вы используете свойство Page.ViewStateUserKey, ему обычно присваивается значение, например имя пользователя текущего пользователя или идентификатор сеанса пользователя. Шаблоны проектов для приложений WebForms в Microsoft Visual Studio 2012 и более поздних версиях содержат примеры, использующие это свойство. Дополнительные сведения см. в разделе Свойство Page.ViewStateUserKey на веб-сайте Microsoft Developer Network (MSDN).
Если указано свойство ViewStateUserKey, его значение сохраняется в __VIEWSTATE во время создания. При использовании поля __VIEWSTATE сервер проверяет текущее свойство Page ViewStateUserKey и проверяет его по значению, которое использовалось для создания поля __VIEWSTATE. Если значения не совпадают, запрос отклоняется как потенциально вредоносный.
Примером сбоя, связанного с ViewStateUserKey, может быть клиент, у которого в браузере открыты две вкладки. Клиент входит в систему как пользователь A, и на первой вкладке отображается страница с __VIEWSTATE, свойство Которого ViewStateUserKey содержит "Пользователь A". На второй вкладке клиент выходит из системы, а затем снова входит в систему от имени пользователя B. Клиент возвращается на первую вкладку и отправляет форму. Свойство ViewStateUserKey может содержать user B (так как это то, что говорит файл cookie проверки подлинности клиента). Однако поле __VIEWSTATE, отправленное клиентом, содержит "Пользователь A". Это несоответствие приводит к сбою.
Решение 4a. Убедитесь, что viewStateUserKey задан правильно
Если приложение использует свойство ViewStateUserKey, убедитесь, что значение свойства совпадает как при создании состояния представления, так и при его использовании. Если вы используете имя пользователя, вошедшего в систему, убедитесь, что пользователь по-прежнему вошел в систему и что удостоверение пользователя не изменилось во время обратной передачи. Если вы используете идентификатор сеанса текущего пользователя, убедитесь, что время ожидания сеанса не истекло.
Если вы работаете в среде фермы, убедитесь, что <элементы machineKey> совпадают. Инструкции по созданию этих элементов см. в приложении А .
Приложение А. Создание <элемента machineKey>
Примечание
Предупреждение системы безопасности
Существует множество веб-сайтов, которые будут создавать <элемент machineKey> для вас нажатием кнопки. Никогда не используйте <элемент machineKey> , полученный с одного из этих сайтов. Невозможно узнать, были ли эти ключи созданы безопасно или записываются в секретную базу данных. Вы должны использовать <только элементы конфигурации machineKey> , созданные вами самостоятельно.
Чтобы создать <элемент machineKey> самостоятельно, можно использовать следующий скрипт 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)
}
}
Для приложений ASP.NET 4.0 можно просто вызвать Generate-MachineKey без параметров, чтобы создать <элемент machineKey> следующим образом:
PS> Generate-MachineKey
<machineKey decryption="AES" decryptionKey="..." validation="HMACSHA256" validationKey="..." />
приложения ASP.NET 2.0 и 3.5 не поддерживают HMACSHA256. Вместо этого можно указать SHA1 для создания совместимого <элемента machineKey> следующим образом:
PS> Generate-MachineKey -validation sha1
<machineKey decryption="AES" decryptionKey="..." validation="SHA1" validationKey="..." />
Как только у вас есть <элемент machineKey> , его можно поместить в файл Web.config. Элемент <machineKey> действителен только в файле Web.config в корне приложения и недопустим на уровне вложенной папки.
<configuration>
<system.web>
<machineKey ... />
</system.web>
</configuration>
Чтобы получить полный список поддерживаемых алгоритмов, запустите справку Generate-MachineKey из командной строки Windows PowerShell.
Приложение Б. Подготовка реестра для сохранения автоматически созданных ключей
По умолчанию, так как ASP. Автоматически созданные ключи NET сохраняются в реестре HKCU. Эти ключи могут быть потеряны, если профиль пользователя не был загружен в рабочий процесс IIS, а затем пул приложений перезапускает. Этот сценарий может повлиять на поставщиков общего размещения, использующих пулы приложений в качестве стандартных учетных записей пользователей Windows.
Чтобы обойти эту ситуацию, ASP.NET позволяет сохранять автоматически созданные ключи в реестре HKLM вместо реестра HKCU. Обычно это выполняется с помощью служебной программы aspnet_regiis (см. инструкции в разделе "Решение 2a: использование служебной программы aspnet_regiis"). Однако для администраторов, которые не хотят запускать эту служебную программу, можно использовать следующий скрипт Windows PowerShell:
# 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)
}
}
}
В следующем примере показано, как подготовить соответствующие записи реестра HKLM для пула приложений, example@contoso.com который выполняется от имени пользователя (это имя участника-пользователя учетной записи пользователя Windows). Этот пул приложений является 32-разрядным пулом приложений, в котором выполняется среда CLR версии 2.0 (ASP.NET 2.0 или 3.5).
PS> Provision-AutoGenKeys -FrameworkVersion 2.0 -Architecture 32 -UPN "example@contoso.com"
Если пул приложений является 64-разрядным пулом приложений, в котором выполняется среда CLR версии 4.0 (ASP.NET 4.0 или 4.5), выполните следующую команду:
PS> Provision-AutoGenKeys -FrameworkVersion 4.0 -Architecture 64 -UPN "example@contoso.com"
Несмотря на то, что автоматически созданные ключи хранятся в HKLM, подраздел реестра, содержащий секретный криптографический материал каждой учетной записи пользователя, добавляется в список управления доступом (ACL), чтобы криптографический материал не считывался другими учетными записями пользователей.
Приложение В. Шифрование <элемента machineKey> в файлах конфигурации
Администраторы сервера могут не захотеть, чтобы конфиденциальные сведения, такие как <материал ключа machineKey> , были в виде открытого текста в файлах конфигурации. В этом случае администраторы могут решить воспользоваться преимуществами функции платформа .NET Framework, известной как "защищенная конфигурация". Эта функция позволяет шифровать определенные разделы файлов .config. Если содержимое этих файлов конфигурации когда-либо будет раскрыто, содержимое этих разделов по-прежнему будет оставаться секретным.
Краткий обзор защищенной конфигурации можно найти на веб-сайте MSDN. Он также содержит руководство по защите <элементов connectionStrings> и <machineKey> файла Web.config.