Защита ViewState от вмешательства

Простое средство защиты от подобных фальсификаций — использование машинного контроля аутентификации (machine authentication check) или MAC. Машинный контроль аутентификации разработан для обеспечения гарантии того, что полученные компьютером данные являются именно теми данными, которые были переданы, в частности, что они не были фальсифицированы. Это именно то, что нам требовалось для состояния отображения. Для состояния отображения ASP.NET LosFormatter осуществляет MAC путем хэширования сериализованных данных состояния отображения и добавления этого хэша в конец состояния отображения. (Хэш — это быстро вычисляемый дайджест, который обычно используется в сценариях симметричной безопасности для обеспечения целостности сообщения.) При возврате Web-страницы LosFormatter проводит проверку, чтобы гарантировать, что прикрепленный хэш совпадает с хэшированным значением десериализованного состояния отображения. Если он не совпадает, состояние отображения было изменено по дороге.

По умолчанию класс LosFormatter применяет MAC. Однако вы можете настроить то, проводить MAC или нет, через свойство EnableViewStateMac класса Page. По умолчанию оно имеет значение True, которое указывает, что MAC будет иметь место; значение False показывает, что MAC не должен проводиться. Вы можете далее настроить, какой алгоритм хэширования должен применяться при MAC. В файле machine.config найдите атрибут validation элемента <machineKey>. По умолчанию применяется алгоритм хэширования SHA1, но вы по желанию можете заменить его алгоритмом MD5. (Более подробная информация по SHA1 представлена в RFC 3174; более подробная информация по MD5 представлена в RFC 1321.)

Запись ViewState в файл
public class PersistViewStateToFileSystem : Page
{
  protected override void SavePageStateToPersistenceMedium(object viewState)
   {
     // сериализуем состояние отображения в строку, кодированную по основанию base-64
     LosFormatter los = new LosFormatter();
     StringWriter writer = new StringWriter();
     los.Serialize(writer, viewState);
     // сохраняем строку на диск 
     StreamWriter sw = File.CreateText(ViewStateFilePath);
     sw.Write(writer.ToString());
     sw.Close();
   }
  protected override object LoadPageStateFromPersistenceMedium()
   {
     // определяем, к какому файлу организовывать доступ 
     if (!File.Exists(ViewStateFilePath)) return null;
     else
       {
       // открываем файл 
       StreamReader sr = File.OpenText(ViewStateFilePath);
       string viewStateString = sr.ReadToEnd();
       sr.Close();
       // десериализуем строку 
       LosFormatter los = new LosFormatter();
       return los.Deserialize(viewStateString);
       }
   }
  public string ViewStateFilePath
   {
     get
     {
       string folderName = Path.Combine(Request.PhysicalApplicationPath, "PersistedViewState");
       string fileName = Session.SessionID + "-" + Path.GetFileNameWithoutExtension(Request.Path).Replace("/", "-") + ".vs";
       return Path.Combine(folderName, fileName);
     }
   }
}
Программный разбор состояния отображения
protected virtual void ParseViewStateGraph( object node, int depth, string label)
{
  tw.Write(System.Environment.NewLine);
  if (node == null)
   {
     tw.Write(String.Concat(Indent(depth), label, "NODE IS NULL"));
   }
  else if (node is Triplet)
   {
     tw.Write(String.Concat(Indent(depth), label, "TRIPLET"));
     ParseViewStateGraph( ((Triplet) node).First, depth+1, "First: ");
     ParseViewStateGraph( ((Triplet) node).Second, depth+1, "Second: ");
     ParseViewStateGraph( ((Triplet) node).Third, depth+1, "Third: ");
   }
  else if (node is Pair)
   {
     tw.Write(String.Concat(Indent(depth), label, "PAIR"));
     ParseViewStateGraph(((Pair) node).First, depth+1, "First: ");
     ParseViewStateGraph(((Pair) node).Second, depth+1, "Second: ");
   }
  else if (node is ArrayList)
   {
     tw.Write(String.Concat(Indent(depth), label, "ARRAYLIST"));
     // display array values
     for (int i = 0; i < ((ArrayList) node).Count; i++)
     ParseViewStateGraph( ((ArrayList) node)[i], depth+1, String.Format("({0}) ", i));
   }
  else if (node.GetType().IsArray)
   {
     tw.Write(String.Concat(Indent(depth), label, "ARRAY "));
     tw.Write(String.Concat("(", node.GetType().ToString(), ")"));
     IEnumerator e = ((Array) node).GetEnumerator();
     int count = 0;
     while (e.MoveNext())
     ParseViewStateGraph( e.Current, depth+1, String.Format("({0}) ", count++));
   }
  else if (node.GetType().IsPrimitive || node is string)
   {
     tw.Write(String.Concat(Indent(depth), label));
     tw.Write(node.ToString() + " (" + node.GetType().ToString() + ")");
   }
  else
   {
     tw.Write(String.Concat(Indent(depth), label, "OTHER - "));
     tw.Write(node.GetType().ToString());
   }
}
ViewState

Существует большое количество хороших ресурсов, из которых вы можете многое узнать о состоянии отображения ASP.NET. Пол Вилсон (Paul Wilson) предлагает такие источники как View State: All You Wanted to Know и Page View State Parser. Дино Еспосито (Dino Esposito) написал статью для MSDN Magazine в феврале 2003 под названием The ASP.NET View State, которая обсуждает методы сохранения состояния отображения в файловой системе Web-сервера. Статья Taking a Bite Out of ASP.NET View State, написанная Сьзен Воррен (Susan Warren), обеспечивает хороший высокоуровневый обзор состояния отображения, включая обсуждение его кодирования. На форуме Скотта Гэлловея (Scott Galloway) тоже есть несколько хороших сообщений о том, как работать с состоянием отображения ASP.NET.

Кэш файлов
автоматически сгенерированный класс вместе с откомпилированным экземпляром класса хранятся в папке: WINDOWS\Microsoft.NET\Framework\version\Temporary ASP.NET Files
Machine.config
WINDOWS\Microsoft.NET\Framework\version\CONFIG\Machine.config
Configuration
MSDN
<configuration>

	<system.web>
		...
	</system.web>
	
	<appSettings> 
		<add key="ConnectionString" value="server=localhost;datasource=DemoBase;login=sa">
		</add>
	</appSettings>
	
</configuration>

public static SqlConnection Connection
{
  get
  {
   return new 				SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"]);
  }
}
<configuration>
	<configSections>
		<sectionGroup name="z_setup">
			<section name="z_Url2DBmapping" type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" />
			<section name="z_UserRegions" type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" />
		</sectionGroup>
	</configSections>

	<system.web>
		...
	</system.web>
	
	<z_setup>
		<z_UserRegions>
			<add key="rus" value="id=ru;encoding=windows-1251;dateformat=DMY.:" />
			<add key="eng" value="id=en;encoding=windows-1251;dateformat=MDY/:" />
		</z_UserRegions>
	</z_setup>
</configuration>

NameValueCollection vc = 
	(NameValueCollection) System.Configuration.ConfigurationSettings.GetConfig("z_setup/z_UserRegions");

<?xml version="1.0" encoding="Windows-1252"?>

<configuration>
    <configSections>
        <section name="FavoriteUrls" type="System.Configuration.NameValueSectionHandler" />
		<section name="htmleditor" type="HtmlEditor.config,HtmlEditor" />
    </configSections>

    <appSettings>
	    ...
    </appSettings>

    <FavoriteUrls>
        <add key="Microsoft" value="http://www.microsoft.com/" />
        <add key="MSD2D" value="http://www.msd2d.com/" />
    </FavoriteUrls>
	
	<htmleditor>
	
	</htmleditor>
	
</configuration>


NameValueCollection section = 
	(NameValueCollection)ConfigurationSettings.GetConfig("FavoriteUrls");
config cfg = (config)System.Configuration.ConfigurationSettings.GetConfig("htmleditor");
				
	
public class config : System.Configuration.IConfigurationSectionHandler
{
		public object Create(object parent, object configContext, System.Xml.XmlNode section)
		{
			...
			return this;		
		}	
}
Аутентфикация

Аутентификация формой с использованием файла конфигурации

<configuration>

<location path="articles">
	<system.web>
		<authorization>
			<deny users="?" />
		</authorization>
	</system.web>
</location>

<system.web>
	<authentication mode=”Forms”>
	<forms name=”ASP_XML_Form” loginUrl=”login.aspx” protection=”All” timeout=”30” path=”/” 
		requireSSL=”false” slidingExpiration=”true”>
		<credentials passwordFormat=”Clear”>
			<user name=”John” password=”one”/>
			<user name=”Mike” password=”two”/>
			<user name=”Bill” password=”three”/>
		</credentials>
	</forms>
	</authentication>
</system.web>

</configuration>
Как видно из вышеприведённого листинга, тэг credentials содержит один единственный атрибут – passwordFormat. Этот параметр определяет способ хранения пароля и принимает следующие значения:
  • Clear – пароль сохраняется без каких-либо изменений
  • SHA1 – пароль хэшируется методом SHA1 (Secure Hash Algorithm версии 1)
  • MD5 – пароль хэшируется методом MD5 (Message Digest версии 5)
Private Sub btnLogin_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnLogin.Click
    If FormsAuthentication.Authenticate(txtName.Text, txtPassword.Text) Then
        ' Если пользователь найден в разделе сертификатов, значит, регистрация проведена
        ‘ успешно
        FormsAuthentication.RedirectFromLoginPage(txtName.Text, False)
    Else
            ' Иначе – выводим сообщение об ошибке
        lbl.Visible = True
    End If
End Sub

Аутентификация формой с использованием собственных методов

System.Web.Security.FormsAuthentication.RedirectFromLoginPage("User", False)
Аутентификация

Аутентификация с помощью учетных записей Windows

Если вы планируете аутентифицировать пользователей по учетным записям, хранящимся на контроллере домена Microsoft Windows NT или в службе каталогов Microsoft Windows® 2000 Active Directory™, то должны использовать аутентификацию IIS в сочетании с провайдером Windows Authentication для ASP.NET, как показано на рис. 2. Тогда вам не придется писать никакого специфического кода для аутентификации. Когда аутентификация осуществляется этим способом, ASP.NET создает объект Windows Principal и связывает его с контекстом приложения для аутентифицированного пользователя. В результате ASP.NET-поток выполняется от имени аутентифицированного пользователя и может получить членство в его группе.

В некоторых случаях нужно обходить аутентификацию IIS и реализовать собственный способ. В ASP.NET это допустимо. Например, вы можете создать свой ISAPI-фильтр, проверяющий удостоверения пользователя в Active Directory, и самостоятельно создавать объект Windows Principal. Существуют и другие способы, но тогда придется писать больше кода, чем при прямом использовании .NET-провайдера.

Аутентификация без применения учетных записей Windows

Если вы планируете аутентифицировать пользователей на уровне приложения и при этом у пользователей нет учетных записей в Windows, то скорее всего вы настроите IIS на анонимную аутентификацию. В этом случае рассмотрите следующие аутентификационные модули .NET.

  • Без аутентификации. Применяйте, когда аутентификация пользователей вообще не проводится или когда она осуществляется специфическим модулем вашей разработки.
  • Формы. Применяйте, если хотите предоставить пользователям страницу для входа (logon page).
  • Passport. Применяйте при использовании служб Passport.

Олицетворение и делегирование

За счет олицетворения ASP.NET-приложения могут при необходимости выполняться под идентификацией клиента, от имени которого они действуют. Обычно олицетворение реализуется для управления доступом к ресурсам. Тщательно взвесьте, нужно ли оно в вашем случае, так как олицетворение расходуются дополнительные ресурсы сервера. Делегирование - более мощная форма олицетворения, позволяющая серверному процессу обращаться к удаленным ресурсам от имени клиента.

При включенном олицетворении ASP.NET получает соответствующий маркер (token) от IIS. ASP.NET-механизм олицетворения по сравнению с аналогичным механизмом в традиционном ASP позволяет добиться в Web-приложениях более тонкого контроля за олицетворением. Для этого в файле Web.config приложения поддерживается специальный параметр.

Этот параметр может принимать три значения.

  • Олицетворение разрешено. ASP.NET подменяет маркер, полученный от IIS и выданный либо аутентифицированному пользователю, либо анонимной учетной записи Интернета.
    <identity impersonate="true"/>
    
  • Олицетворение разрешено и задана конкретная идентификация. ASP.NET подменяет маркер, сгенерированный с использованием заданной идентификации. В этом случае маркер клиента не используется, даже если он есть.
    <identity impersonate="true" name="domain\user" password="pwd"/>
    
  • Олицетворение запрещено. Это значение предлагается по умолчанию для обратной совместимости с ASP. В данном случае ASP.NET-поток выполняется, используя маркер рабочего процесса приложения, который по умолчанию относится к системной учетной записи IIS независимо от применяемой вами комбинации средств аутентификации IIS и ASP.NET.
    <identity impersonate="false"/>
    

Когда приложение размещено на сетевом UNC-ресурсе, ASP.NET всегда подменяет (олицетворяет) UNC-маркер IIS для доступа к этому ресурсу, если только не используется специфически сконфигурированная учетная запись. В последнем случае ASP.NET использует ее вместо UNC-маркера IIS.

В табл. 1 показан маркер потока, используемый ASP.NET для выполнения запроса в зависимости от трех вариантов настройки Web.config. Заметьте, что учетная запись IUSR_SERVER соответствует учетной записи, настроенной для анонимного доступа по текущему URL, и что такая запись не обязательно должна быть типа IUSR_. Учетная запись процесса - это учетная запись, под которой выполняется рабочий процесс приложения (по умолчанию - System Account).

Табл. 1. Маркер ASP-потока для конфигураций ASP и IIS

Олицетворение ASP.NET В IIS указан анонимный доступ В IIS не указан анонимный доступ Приложение размещено на сетевом UNC-ресурсе
Запрещено Учетная запись процесса Учетная запись процесса UNC-маркер IIS
Разрешено IUSR_SERVER Аутентифицированный пользователь UNC-маркер IIS
Разрешено, но задан пользователь "Jeff" "Jeff" "Jeff" "Jeff"

Выгрузка файла
private void Page_Load(object sender, System.EventArgs e)
{
	roaming.classes.page pg = new roaming.classes.page(Page);

	string sId = Page.Request.QueryString["id"];
	if( sId != null ) 
	{
		if( ((roaming.classes.user)pg.User).ID == 0 )
		{
			string s = Server.HtmlEncode("/roaming/login.aspx?return=/roaming/document.aspx?id="+sId);
			Response.Redirect(s,true);
		}

		unisite.util.fileitem fl = new unisite.util.fileitem();
		System.Data.SqlClient.SqlDataReader rs = null;
		System.Data.SqlClient.SqlCommand cmd = null;
		try 
		{
			cmd = new  System.Data.SqlClient.SqlCommand();
			cmd.Connection= pg.Connection;
			cmd.CommandType= System.Data.CommandType.Text;
			cmd.CommandText= "select id, fl_name, fl_type, fl_length, fl_data from ut_file where id=@id";
			cmd.Parameters.Add("@id",sId);
			rs= cmd.ExecuteReader();

			while( rs.Read() ) 
			{
				fl.ID = rs[0];
				fl.FileName = rs.GetString(1);
				fl.ContentType = rs.GetString(2);
				fl.ContentLength = rs.GetInt32(3);
				byte[] buf = new Byte[fl.ContentLength];
				rs.GetBytes(4,0,buf,0,fl.ContentLength);

				Response.Clear();
				Response.ContentType = fl.ContentType;
				Response.AddHeader("content-disposition","filename="+fl.FileName);
				Response.BinaryWrite(buf);	
			}
		}
		catch( System.Exception  )
		{	
			;
		}
		finally
		{
			if( rs != null ) rs.Close();
			cmd.Dispose();
		}

	}
	pg.Dispose();
}

Web сервисы ASP .NET
Server Controls Overview (MSDN)
Data Binding Overview
Подключение паспорта
Мультиязыковая поддержка в ASP.NET
private void Page_Load(object sender, System.EventArgs e)
{

   string _sCulture = "ru-RU"
   if (this.Request.QueryString["Culture"] != null)
       _sCulture = this.Request.QueryString["Culture"];
   
   //    Задаём культуру для текущего потока запроса пользователя 
   Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(_sCulture);
   
   //    Задаём культуру, используемую ResourceManager  для поиска 
   // локализованных ресурсов
   Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(_sCulture);

   //    Задаём кодировку контента ответа на запрос пользователя
   this.Response.ContentEncoding = Encoding.GetEncoding(Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage);         

   //    Создаём экземпляр класса ResourceManager для данного класса и данной сборки
   ResourceManager _resourceManager = new ResourceManager(this.GetType().BaseType.FullName,
                                                            this.GetType().BaseType.Assembly);

   //    Получаем локализованное значение из локализованной сборки ресурсов
   this.headerLabel.Text = _resourceManager.GetString("headerLabel");
}
Caching Dynamic Pages with ASP.NET
Темы и скины в ASP.NET2
Создание таблиц со сложной шапкой
Отображение номера ряда в DataGrid

The ItemDataBound of the DataGrid occurs when data is bound to a item in a DataGrid control. This article explains how to use this event and display serial numbers for rows in a DataGrid.

The first step is to create an empty TemplateColumn in the DataGrid control as follows:

<Asp:DataGrid runat="Server" id ="DGCategory">
	<columns>
		<Asp:TemplateColumn>
		</Asp:TemplateColumn>
		
		.................
	</columns>
</Asp:DataGrid>

The next step is to specify an event handler for the DataGrid's ItemDataBound using the following event signature.

void eventhandlername(object Sender, DataGridItemEventArgs E)
{	
.........................
}

This event handler should be mapped to the OnItemDataBound property of the DataGrid control as follows:

<Asp:DataGrid runat="Server" id ="DGCategory" OnItemDataBound="eventhandler">

The DataGridItemEventArgs has a property called Item which is of type DataGridItem and gets the referenced item in the DataGrid when the event happens. DataGridItem has a property called DataSetIndex which gives the index number of the DataGridItem. We will have to display this number within the empty template column as follows:

E.Item.Cells[0].Text= E.Item.DataSetIndex + 1;

Since the DataSetIndex starts with zero one is added to it for displaying the serial number. Also we will have to check the ItemType and display serial number only if the item type is not a Header or a Footer.

if(E.Item.Itemtype != ListItemType.Header && E.Item.Itemtype != ListItemType.Footer)
{
E.Item.Cells[0].Text= E.Item.DataSetIndex + 1;
}
Разработка серверных элементов управления ASP.NET
Построение графиков с использованием OWC. Часть 1
  Построение графиков с использованием OWC. Часть 2
  Построение графиков с использованием OWC. Часть 3
Submit на другую страницу
<!-- UserForm.aspx -->
Context.Items("UserName") = UserName.text
Context.Items("UserPhone") = UserPhone.text
Server.Transfer("Result.aspx")
<!-- Result.aspx -->
Name= Context.Items("UserName")
Phone= Context.Items("UserPhone")
404.aspx
<customErrors mode="Off">
<error statusCode="404" redirect="/404.aspx" />
<error statusCode="403" redirect="/403.aspx" />
</customErrors>
POST на другую страницу
<script language="javascript">
function noPostBack(sNewFormAction)
{
    document.forms[0].action = sNewFormAction;
    document.forms[0].__VIEWSTATE.name = 'NOVIEWSTATE';
}
</script>
private void Page_Load(object sender, System.EventArgs e)
{
   Submit.Attributes.Add("onclick", "noPostBack('secondform.aspx');");
}
Обработчик ошибок
Step 1 - Updating the global.asax
protected void Application_Error(Object sender, EventArgs e)
{
// Get the exception
Exception LastException  = Server.GetLastError();
// Redirect to custom error page
Response.Redirect(String.Format("~/error.aspx?msg={0}", 
					Server.UrlEncode(LastException.Message)));
}
To fix IIS mappings for ASP.NET
"%windir%\Microsoft.NET\Framework\version\aspnet_regiis.exe" -i
regsvr32 %windir%\Microsoft.NET\Framework\version\aspnet_isapi.dll
HttpContext.RewritePath
файл Global.asax
void Application_BeginRequest (Object sender, EventArgs e) 
{ 
// TODO: превращение пути вида 
// .../quotes/page1.aspx в путь вида 
// .../rewritepath.aspx?id=1 
// 
HttpContext context = HttpContext.Current; 
string oldpath = context.Request.Path.ToLower (); 
string token = "/quotes/page"; 
int i = oldpath.IndexOf (token); 
int len = token.Length; 
if (i != -1) { 
int j = oldpath.IndexOf (".aspx"); 
if (j != -1) 
{ string id = oldpath.Substring (i + len, j - (i + len)); 
string newpath = oldpath.Replace (token + id + ".aspx", "/rewritepath.aspx?id=" + id); 
context.RewritePath (newpath); 
} } } 
Control State

Свойство ControlState класса PageStatePersister является аналогом свойств ViewState. Но, в отличии от последнего, не может быть отключено. Можно использовать это свойство для назначения своего обработчика в контроле.

Здесь приведен пример.

public class SampleControl : Control 
{ 
int _idx = 0; 
      
protected override void OnInit(EventArgs e) 
{ Page.RegisterRequiresControlState(this); 
  base.OnInit(e); 
} 
      
protected override void LoadControlState( object savedState) 
{ 
object[] rgState = (object[])savedState; 
base.LoadControlState(rgState[0]); _
idx = (int)rgState[1]; 
} 
      
protected override object SaveControlState() 
{ 
object[] rgState = new object[2]; 
rgState[0] = base.SaveControlState(); 
rgState[1] = _idx; return rgState; 
} 
// ... 
} 
Последовательность событий страницы
  • Page_PreInit;
  • Page_Init;
  • Page_InitComplete;
  • Page_PreLoad;
  • Page_Load;
  • Page_LoadComplete;
  • Page_PreRender;
  • Page_PreRenderComplete;
  • Page_SaveStateComplete;
-
-
Hosted by uCoz