In part 1 of this post, you can see how you can encrypted your ViewState and ensure its integrity across postbacks. A few more things you can do to beef up your ViewState security.
ViewState validation with ViewStateUserKey
It is possible for someone who got a hand on your ViewState data and reuse it at some other time or different user session. This scenario is what we call ‘One-Click attacks‘, a variation of XSS. ASP.NET provides ViewStateUserKey as a way to ensure that ViewState data is tied to specific user session.
In short, ASP.NET can use ViewStateUserKey for encryption salt. So if we set ViewStateUserKey to current session ID or specific user information, this ViewState data will not be usable to other users or sessions. For more detail explanation, check out Scott Hanselman’s posts here and here.

Note that setting ViewStateUserKey should be called early in the OnInit method, before ViewState is encrypted.
Taking ViewState out of Page output
The ultimate measure to secure ViewState is to remove it from rendered page altogether and store it somewhere else. Apart from tighten up security, another benefit is reducing the size of output HTML page.
ASP.NET uses a page state persister class to take care of persisting ViewState data. By default, ASP.NET page uses HiddenFieldPageStatePersister, which as the name implies, persist ViewState to page’s hidden field. Another default persister is SessionPageStatePersister which keeps page ViewState in current session.
To switch to use session persister, add <sessionPageState> in Web.config file. You can also specify the size of maximum saved ViewState.

Custom page ViewState Persister
ASP.NET 2.0 allows you to create custom persister by overriding PageStatePersister class. In the code sample, I create a persister that save ViewState in a separate text file.

Subclassing System.Web.UI.PageStatePersister
Next, we have to override Save method. This method is called when ViewState data is persisted to storage medium. In this case, I persist ViewState data to Text file.

Generate GUID for ViewState persisting file name
Note that I keep GUID in a hidden field. We’ll need this GUID when we load back ViewState on postback.

Writing ViewState data to text file.
You’ll see on the output HTML that __VIEWSTATE hidden field is now empty! We only keep GUID in a hidden field to refer back when we load ViewState data. This helps reduce page size significantly if your page relies on heavy ViewState data.

Lastly, override Load method to get ViewState value back from persistence medium.

Now you need to tell your aspx page to use this custom persister. To do that, simply override PageStatePersister property on the page.

You can download the code for this class HERE.
Where should I put ViewState data
Putting ViewState in Session has some disadvantages. First, you can lose session ViewState if it reaches historySize limit (if you open multiple windows of the same web page). You may also run into a memory problem if ViewState is very large. Consider when you have a lot of users with large ViewState data, all these data is kept in web server memory. From the sample code, persisting ViewState to external text file is effective against large ViewState, yet it can incur significant file IO cost. With ability to create custom page ViewState persister, you can go as far as persist it to SQL database 