Scott Hanselman

ViewStateUserKey and "Invalid_Viewstate" when posting back during Forms Authentication

February 9, '05 Comments [7] Posted in ASP.NET | ViewState
Sponsored By

From my point of view, I like ASP.NET because it's at least consistent. Anytime some crazy crap happens, at least it makes sense once the dust clears.

We've got a "Workflow" ASP.NET Server Control that i s very snazzy. It's a templated user control that handles all the state transitions one has to deal with when showing and hiding different panels during a Wizard-style operation.

So, you can setup Next and Previous or whatever buttons you like, hook each up to any WorkflowStep and there you go. It's a simpler thing that the UIP. Anyway, as with all ASP.NET 1.1 Postback-like things, it postsback to the current page.

Today someone who was creating an Enrollment and Signon workflow got this Exception during a PostBack:

Exception: System.Web.HttpException
Message: Invalid_Viewstate
Source: System.Web
   at System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
   at System.Web.UI.Page.LoadPageViewState()
   at System.Web.UI.Page.ProcessRequestMain()

When you get this exception the standard list of things to check are:

  • If this is a cluster, edit <machineKey> configuration so all servers use the same validationKey and validation algorithm.  AutoGenerate cannot be used in a cluster. 
  • Viewstate can only be posted back to the same page. 
  • The viewstate for this page might be corrupted.

However, this isn't an error that you'd likely see on a single development ox, so something had to be up. The only interesting and different thing about this situation was that the user, in step 4 of this 7-step process, gets logged into Forms Authentication. When I say "gets logged in" I mean, a cookie is sent to them.

Here's the flow of what happened and why it's a problem:

  1. Un-Forms-Authenticated Client (no cookies) does a GET and receives some standard ViewState.
  2. Client fills out form, POSTs back. ViewState is decoded, life goes on.
  3. During Page processing, user is "logged on" and a Forms Authentication cookie is set out (queued up more like it).
  4. Additionally more ViewState is sent back, not encrypted.
  5. Now Forms-Authenticated Client (cookies) fills out form, POSTs back. ViewState from step 4 is returned to the server.
  6. In Global.AuthenticateRequest(), the cookie from this client is cracked open and a valid SecurityPrincipal is put on the current Thread. Now, User.Identity.IsAuthenticated is true, and User.Identity.Name is set to some string.
  7. In the Page.Init() this line executes, which I've mentioned before. This adds another layer of protection to the User's ViewState.:
    if (User.Identity.IsAuthenticated){ViewStateUserKey = User.Identity.Name;}
  8. Then, after Page.Init() but before Page.Load(), the internal method LoadViewStateFromPersistanceMedium() starts to open up the ViewState that was passed to us. This was the non-protected non-encrypted viewstate from step #4.
  9. Exception occurs, because we now are trying to decrypt ViewState with a key that wasn't present when the ViewState was originally generated.

Moral: Don't change ViewStateUserKey when there is pending ViewState that hasn't been posted back and cracked open yet.

However, you can ONLY change Page.ViewStateUserKey in Page.OnInit, so we do a Response.Redirect after the Forms Authentication Cookie is sent out. This avoids any potential trouble with logging a user in on a postback, and cleans up the workflow considerably.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, February 10, 2005 6:54:22 AM UTC
Hey Scott,

Nice post. I think using the Session ID for the ViewStateUserKey would solve your problem. It may cause you others though - would you ever want the viewstate to persist between sessions?
Thursday, February 10, 2005 7:04:57 AM UTC
Good question. ViewState has no explicit lifetime, while sessions do...the guy could wait, expire the session, the postback...that'd cause this error also, no?
Scott Hanselman
Thursday, February 10, 2005 7:15:36 AM UTC
I guess it would if not handled. Perhaps there's a nice way to handle it in the session start to avoid the error. I'm guessing as I haven't checked this out, but the postback would send back the expired sessionid in a cookie or something right? Even if a new session is starting, there might be a way to grab that old session id and use it to decode the viewstate.
Thursday, February 10, 2005 2:08:20 PM UTC
Ah, but remember the order of things. Init(), LoadViewState(), Load(). Other than Page.OnError, there's nowhere to "handle" the ViewState not loading, and if it doesn't load, you're screwed. You (the developer) can't catch a failed load of viewstate and continue loading the page.
Thursday, February 10, 2005 11:16:53 PM UTC
A session start happens before the Init doesn't it? After posting the previous comment I started wondering if a browser would pass back an expired session cookie anyway - it's not something I'd want to rely on. So maybe it would be impossible to reliably retrieve the expired session ID. I'm sure you could take the approach of putting the key in another cookie variable or something, but it's starting to sound pretty ugly. I suspect the way you describe it in your post above might be the best way to manage ViewStateUserKey.

BTW, I did a little research last night, and stumbled across a sample chapter from a security book. It suggested using Session ID or Username for the key if the user is logged in, and using a random number (presumably stored somewhere) if the user isn't authenticated. It strongly recomended avoiding unencrypted viewstates.
Thursday, February 10, 2005 11:19:47 PM UTC
Your comment spam control just bounced me a couple of times. I'm pretty sure I read it correctly the second time. Would it bounce me if I wrote in caps (like it's suggesting) rather than small letters? It worked the third time when I used small letters, even though it was showing caps.
Monday, February 14, 2005 6:19:49 PM UTC
Scott,

That shed some light on a problem I'm having: I've got a form that people sometimes sit on for an hour before submitting. They have to be logged in (FormsAuthentication) to access the page -- but their authentication expires before they submit the form. Now the viewstate info is invalid, because the key has changed. Can you think of a good way to avoid this?

Thanks!

PS: I also found that the comment-spam test requires lowercase entry, even though the picture shows all caps.
Jesse
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.