Client side state management in ASP.NET
For applications scaling to thousands of users should strongly consider using client to store application state. Removing this burden from server frees up resources. Techniques valuable for storing state information at client:
- View state - used to track values in controls between page requests. Can also add custom values to view state.
- Control state - allows persistence of info about control that is not part of view state. Useful co custom control developers. If view state disabled for control or page, control state still functions.
- Hidden fields - HTML hidden fields store data without displaying data to user. Data is presented back to server and so is available when form is processed.
- Cookies - cookie stores a value in users browser. Browser sends this value with every page request sent to same server. Best way to store state information that must be available to multiple webpages on a site.
- Query strings - value stored at end of URL. Visible to user through browsers address bar. Use when want user to be able to email URL to others.
Choosing client or server side state management
If state management info is stored at client, then it is submitted to server with each request.
Can store majority of data at server and use client side mechanisms purely for user tracking - i.e. tie user up with information stored server side.
Client side advantages:
- Scalable - each client connecting to server consumes resources. If site has hundred of simultaneous users the memory used can be a limiting factor. Client side storage of state information limits burden on server.
- Supports multiple web servers - allows incoming requests to be spread across multiple servers. Each client provides all info any web server needs to process request. With server side state management, if client changes servers in middle of session then new server may not have all information required. To get around this need intelligent load balancers (send client request to same server) or centralised state management.
Server side advantages:
- Security - client can change client-side state information. Thus should not store sensitive info such as passwords, access levels, authentication status, etc.
- Reduced bandwidth - sending state info back and forth to the client increases bandwidth requirements and so slows page load times. Should store large amounts of info (> 1kb) server side.
There is also persisted state - data stored in database. Could decide to store all info in database, but this may put too much pressure on database. In this case may be better to store real, transactional data in database and rely on other techniques for managing the transient state data.
Mechanism used by ASP.NET to store user specific request and response data between page requests. Not stored by server, instead saved in page's view state and sent in page's response back to user. This view state is then returned with their next request. When page processed ASP.NET uses the view state to reset property values of page and its controls.
Unless disabled, this is a part of every ASP.NET page.
The Page.ViewState property (of type StateBag) provides dictionary for retaining values between multiple requests for same page. When page is processed, current state of page and controls is hashed, compressed and encoded into string and saved in page as hidden HTML field called __ViewState. If data too long for single field (see Page.MaxPageStateFieldLength property) then ASP.NET performs view state chunking to split it across multiple hidden fields.
View State Security
View state can be tampered with as it is just a hidden html field.
View state contains message authentication code (MAC) that is used by ASP.NET to determine if it has been tampered with. Helps ensure integrity without having to encrypt view state contents - can rely on MAC and base64 encoding for obfuscation.
If do have very sensitive information to place in view state, can encrypt it using ViewStateEncryptionMode property of Page object.
<@ Page Language="C#" ViewStateEncryptionMode="Always" %>
Will secure view state, but decrease performance of page processing. Also increases size of data being sent between server and browser.
Can enable view state encryption for entire site by setting viewStateEncryptionMode attribute on pages element in web.config file:
<pages viewStateEncryptionMode="Always" />
It is more secure to store sensitive data on server and not send it to client.
Disabling View State
View state enabled by default for all pages and controls - includes label and literal controls that may never be needed to include as part of view state. Should always minimise size of view state. Disable view state for controls, or entire pages, where possible.
At page level use Page.EnableViewState and Page.ViewStateMode.
At control level use Control.EnableViewState and ControlViewStateMode
ViewStateMode only has effect if EnableViewState is true. By default ViewStateMode is Inherit which causes control to inherit pages view state.
If don't change ViewStateMode then setting EnableViewState to false for a page then all its controls view state are also disabled.
To disable control view state should set its ViewStateMode to disabled
|Disable view state for page, but enable for specific control||True||Disabled||True||Enabled|
|Disable view state for page, disable for specific control||True||Disabled||False||Disabled or Inherit|
|Enable view state for page, but disable for specific control||True||Enabled||False||Disabled|
|Enable view state for page and for a control||True||Enabled||True||Enabled or Inherit|
Can disable view state in web.config by setting enableViewState to false in pages element:
<pages enableViewState="false" />
For form with 20 controls, view state easily exceed 10KB.
Can compress viw state by enabling GZip compression on the web server.
Can compress view state without compressing rest of page by overriding Page.SavePageStateToPersistenceMedium and Page.LoadPageStateFromPersistenceMedium which serialize view state data. Within overriden methods use System.IO.Compression.GZipStream to compress / decompress view state data.
Custom View State Data
Can use view state to persist custom data between page requests - don't need to be associated with control.
Use ViewState object like a dictionary:
ViewState["lastvisit"] = DateTime.Now;
Note content of view state is for page only. View state does not transfer from one page to another (as a cookie does). Thus only use to store values between requests to same page.
Moving View State
Rendered at top of page.
Can place elsewhere by overriding Page.Render method and then use string manipulation to move hidden state field to elsewhere in page.
View State and Control State
Disabling view state for a control is problematic for control developers. A custom control may need view state like behaviour that cannot be disabled by a developer.
Control state allows storage of property value information for a specific control.
Cannot be turned off by developer, but should not be used in preference to view state - only where really necessary.
To use override OnInit method for control and call Page.RegisterRequiresControlState. Then implement SaveControlState and LoadControlState for the control - in these methods save and retrieve control state information.
ASP.NET uses HTML hidden fields to store data. Hidden fields are simply input fields embedded in page. Not displayed to user. Sent back during page post.
ASP.NET provides control for you to create own custom hidden fields. Store data in its Value property.
Only store information for single page. Not useful for storing session data used between page requests.
Have no built in compression, encryption, hashing or chunking - very vulnerable to user tampering.
Must submit pages to server using HTTP POST - cannot use HTTP GET (as when user clicks link on page).
Small amount of data stored at client and passed with requests to server.
Persistent cookies written to text file on client - survive event if browser closed and re-opened at later date.
Temporary cookies held in browsers memory - lost when browser closes.
Most common use is to identify user as they visit multiple pages within site. Can use them to store other information, e.g. user preferences.
Most flexible and reliable way of storing data at client, but users can delete them at any time.
Don't solve problem of users moving from computer to computer.
Reading and writing cookies
Web application creates cookie by sending it to client as HTTP header.
To add cookie to cookies collection call Response.Cookies.Add(new HttpCookies("userId", userId);
To retrieve cookie value sent back by browser use Request.Cookies("userId").Value;
To delete cookie, overwrite the cookie and set an expiration date in the past.
Controlling cookie scope
Cookies should be specific to website domain or directory within that domain. By default browsers will not send cookie to website with different host name.
Cookie scope determines which pages have access to information held in cookie. If limited to directory only pages in that directory have access to cookie, e.g.:
Response.Cookies["lastVisit"].Value = DateTime.Now.ToString();
Response.Cookies["lastVisit"].Expires = DateTime.Now.AddDays(1)
Response.Cookies["lastVisit"].Path = "/MyApplication";
To expand scope to entire domain...
Response.Cookies["lastVisit"].Path = "contoso.com";
Setting path to domain allows cookie to be used by any server within domain. Can restrict further by providing full host name, e.g. www.contoso.com
Multiple values in cookie
Cookie size dependent on browser, each can be up to 4k in length.
Limit of 20 cookies per site.
Can store multiple values in single cookie, e.g.
Response.Cookies["info"]["visit"] = DateTime.Now.ToString();
Response.Cookies["info"]["firstName"] = "Tony";
Response.Cookies["info"]["border"] = "blue";
Cookie properties such as Expires, Command and Path apply to all values in single cookie. Cannot control these at individual key level.
Access individual values of cookie using Request.Cookies in same way.
Appended to URL.
Store values for specific page.
Separated from URL by ? followed by parameter name, = and then value.
Values sent to page via query string can be retrieved via Page.Request.QueryString property, e.g.:
Provide simple, but limited way to maintain state information.
Easy way to pass info from one page to another.
Some browsers impose limit of 2083 characters on length or URL.
Must submit page using HTTP GET command to make values available at server.
Parameters and their values are visible to user in their address bar.
You should expect users to change data in query strings - always validate data provided to server.
Their data is included in bookmarks an dURLs sent via email - in fact only way to include state data when copying and pasting URLs to another user - for this reason use query strings for any info that uniquely identifies page even if using another state management technique as well.
To allow URL to be sent by email limit length to 70 characters (including http://).
To allow URL to be sent via IM limit length to 400 characters.
Common mistake is to allow users to navigate search results, but not to validate query string provided. Users can then set number of results per page to a very large value, e.g. 10000. Makes very easy for attacker to perform DOS attack!
Adding query string parameters to URL
Modify URL for any hyperlink user may click.
No tools built in to ASP.NET to simplify creation of query strings.
Reading query string parameters
Access the Request.QueryStrings collection, e.g.
Should always encode cookie or query string values via Server.HtmlEncode before displaying value in html webpage. This method replaces HTML code with special characters the browser cannot process.
To provide extra protection .NET will throw exception if it detects HTML or client-side scripting in a query string - but this feature can be disabled by system administrators so do not rely on it.