Can you trust ViewState to handle program control? - asp.net

I've read a lot about ViewState on MSDN and looked at these questions (among others):
Can malicious users modify viewstate?
How to modify viewstate of a label in ASP.net
I see that if you have EnableViewStateMac turned on, ViewState is signed with a calculated value called a MAC to detect if it's been tampered with during round-trips, and ASP.NET will throw an exception if it detects that the MAC does not match the client's ViewState data.
To me, this means that it is safe unless the private key used to sign the ViewState is somehow reverse-engineered, similar to how SSL cryptography works. Hopefully this is true, but correct me if it's not. I might be missing another piece to how ASP.NET works internally, but it seems to me that you should be able to rely on a control's state to control program execution and flow, since the only way to modify the control's state is in server code based on a postback containing valid changes to the client's form.
The question is: Practically, is it okay to use a control's state (if it is not supposed to be changeable by the user) for programmatic decisions, and what are the possible dangers and how could those cause a practical problem?
Here are two specific examples of what I'm wondering is safe from bypassing via ViewState tampering (this is a mock-up of what I'm doing):
Example 1
Public Sub SetPageState()
If User.IsLoggedIn() Then
MultiView1.ActiveViewIndex = 0 'user is logged in
Else
MultiView1.ActiveViewIndex = 1 'user is not logged in
End If
End Sub
Private Sub PersonalizePage()
If MultiView1.ActiveViewIndex = 0 Then
'Do logged-in stuff
ElseIf MultiView1.ActiveViewIndex = 1
'Do not-logged-in stuff
End If
End Sub
Example 2
Public Sub SetUserLoginControl()
Label1.Visible = User.IsLoggedIn()
End Sub
Private Sub DoLoginThings()
If Label1.Visible Then
'Do logged-in stuff
Else
'Do not-logged-in stuff
End If
End Sub
I realize the "correct" way would be to check for User.IsLoggedIn() (or whatever needs checked) in every place where that's what is supposed to be controlling it, but in some cases the function is computationally expensive, and it's much cheaper to check the state of a control that was modified based on the return value of the expensive function. I realize there are ways around this, such as storing a temporary copy of the function return value, etc., but this is more of a conceptual question than a "here's my problem, now solve it for me" question.

Your code when using controls is unreadable which is bad. You might want to cache state of User.IsLoggedIn() in HttpContect.Current.Items dictionary. It's somewhat a cache that lives for a single request only.

Related

allowing only one instance of page

i have a question about allowing only one pageinstance in all clientsessions. So if a client ask for the unique page and there is already a session with this page. The client which ask for the page should get a messagebox, which say "This page is already in use"
I read about this problem on forums. Many people say "it's impossible to get out if the client have close the browser (including the unique page)".
Is that true about the serverside?
Is there a way to handling this problem?
Is use Asp.net with Vb.net
i hope anyone understand me. My english is bad.
The problem with keeping track of whether the current user is still using the page can be managed by having an UpdatePanel with a Timer which then sets an application variable time every 500ms (or whatever interval you want)
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
Application["PageLastUsed"] = DateTime.Now;
End Sub
and redirect users if the page is in use
TimeSpan duration = DateTime.Now - (DateTime)Application["PageLastUsed"];
if (duration.TotalSeconds < 2)
{
Response.Redirect("InUsePage.aspx");
}
See timer info here http://msdn.microsoft.com/en-us/library/cc295400.aspx

use of timer causes HttpContext.Current to be null

I have a function that parses an input file.
Private Function getSvSpelOdds(ByVal BombNo As Integer) As Boolean
Dim InputFileBase As String = HttpContext.Current.Application("InputFileBase")
strInputFile = InputFileBase & "PC_P7_D.TXT"
OddsReader = New StreamReader(strInputFile)
'some other code
End Function
If the file is not there (getSvSpelOdds returns False), I would like to retry after 30 seconds.
To achieve this I use a timer.
If Not getSvSpelOdds(y) Then
Timer1.Interval = 30000
End If
Private Sub Timer1_Elapsed(sender As Object, e As System.Timers.ElapsedEventArgs) Handles Timer1.Elapsed
getSvSpelOdds(y)
End Sub
Problem is that when timer fires the HttpContext.Current (used to get the value of gloal variable) is null.
Should I use some other approach to get this to work?
As already described HttpContext should be null as Timer_Elapsed is called in different thread. But you may use System.Web.HttpRuntime.Cache to pass filename, cache should be accessible from all threads.
HttpContext.Current only gives you the context you want when you call it on the thread that handles the incoming thread.
When calling it outside of such threads, you get null. That matches your case, as Timer1_Elapsed is executed on a new thread.
Should I use some other approach to get this to work?
Almost certainly, yes. 30 seconds is a long time to wait without giving any feedback to users.
It would probably be better to return a "no results are available yet, but we're still looking" page to the user. That page can be set to refresh automatically after 30 seconds, by adding a suitable meta-tag:
<META HTTP-EQUIV="refresh" CONTENT="30">
And you then get a fresh request/response cycle on the server. And haven't tied up server resources in the meantime.
Other answers seems to address the other part of your question (about why it doesn't work in the timer callback)
The Elapsed event on the Timer will run on a separate thread therefore its expected behaviour for the current context to be null.
You can only access it from the same thread.
Should I use some other approach to get this to work?
Yes, it's not generally a good idea to mix ASP.NET and threads given the complexity of how ASP.NET works. Like already mentioned its not a great UX to have no feedback for 30 seconds, its better to let the user know what's actually going on.
Also, you need to determine whether the timeout length is appropriate or whether a timeout is needed at all. I don't know the nature of your application but I assume there is some external means for the file to be generated and picked up by your site.

Proper way to skip page execution after Response.RedirectToRoute

I'm writing an asp.net 4.5 application using the new routing features. I have a page that displays some information about an item. In the Page_Load event I check the route data (item id) and user permissions, and if something isn't right (e.g. the id is for a deleted item) I use Response.RedirectToRoute to send them packing, right back to the home page. Do not pass GO, do not collect $200.
This made perfect sense until I tried to access a deleted item and instead of the home page I got an error page. I did some digging and discovered that even after I use RedirectToRoute (unlike the standard Redirect method) the rest of the page code continues to execute, which at the very least seems wasteful (since I'm just going to throw away the results) and throws errors when the necessary data doesn't exist.
I did a little more SO mining and discovered the incredible evil that is Response.End(). It does what I need, but even the MSDN page tells me that Response.End is the bastard child of an ancient accursed language and isn't fit to see the light of day. The primary objection seems to be the fact that Response.End throws an exception, and that's bad for performance. I'm not the most experienced developer, so I don't understand the issue entirely, but I have trouble believing that throwing an exception is more expensive than loading the entire web page. The workarounds seem rather complex and excessive for a task so simple, especially since most pages require some kind of validity check.
What am I supposed to do in this situation? Use Response.End and beg forgiveness for my insolence? Cobble together some ugly workaround? Or is my perspective on the problem all wrong to begin with? I'd really like to know.
Update: Now that I've thought it over a bit more, I wonder if I do have the wrong perspective on the problem. Perhaps an immediate redirect is the not the best response for the user experience. Would I be better off wrapping all the controls in a panel, and using something like this?
Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
'Validation Code
If notValid Then
ControlsPanel.Visible = false
ErrorPanel.Visible = true
End If
End Sub
RedirectToRoute is actually wraps Response.Redirect passing false for ending the request - hence, the request continues. You can use HttpApplication.CompleteRequest as immediate call to terminate the request so that next application events would not be invoked.
Response.End (and other Redirect variation) throws ThreadAbortException to abort the request processing thread which is really a bad way to stop request processing. In .NET world, exception processing is always considered expensive because CLR then needs to search up the stack all the way up for exception processing blocks, create stack trace etc. IMO, CompleteRequest was introduced in .NET 1.1 to avoid the same which actually relies on setting flag in ASP.NET infrastructure code to skip further processing except EndRequest event.
Yet another (and better) way is to use Server.Transfer and avoid client round-trip for setting redirect all together. Only issue is that client would not see the redirected URL in the browser address bar. I typically prefer this method.
EDIT
CompleteRequest wouldn't never work in page case where subsequent page events would be still invoked because page being a handler, all its events happens within a single (and current) application event ProcessRequest. So only way seems to be setting a flag and check that flag in overrides such as Render, PreRender, RaisePostBackEvent etc.
From maintenance perspective, it make sense to have such functionality in base page class (i.e. maintaining the flag, offering CompleteRequest method to subclasses and overriding life cycle event methods). For example,
internal class PageBase: System.Web.UI.Page
{
bool _requestCompleted;
protected void CompleteRequest()
{
Context.ApplicationInstance.CompleteRequest();
_requestCompleted = true;
}
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl,
string eventArgument)
{
if (_requestCompleted) return;
base.RaisePostBackEvent(sourceControl, eventArgument);
}
protected internal override void Render(HtmlTextWriter writer)
{
if (_requestCompleted) return;
base.Render(writer);
}
protected internal override void OnPreRender(EventArgs e)
{
if (_requestCompleted) return;
base.OnPreRender(e);
}
... and so on
}
I may be going out on a limb by not answering the question directly, but I liked seeing your update regarding user experience. I prefer your suggested approach.
I like to give a 410 error for id's that are not valid and extend it a bit with (translated from C#):
Protected Sub ItemDoesNotExist()
'item does not exist, serve up error page
ControlsPanel.Visible = False
ErrorPanel.Visible = True
'add meta tags for noindex
Dim mymeta As New HtmlMeta()
mymeta.Name = "robots"
mymeta.Content = "noindex"
Page.Header.Controls.Add(mymeta)
'RESPOND WITH A 410
Response.StatusCode = 410
Response.Status = "410 Gone"
Response.StatusDescription = "Gone"
Response.TrySkipIisCustomErrors = True
'important for IIS7, otherwise the Custom error page for 404 shows.
Page.Title = "item gone"
End Sub

Dictionary Behaves Strangely During Databinding

I was trying to do a little data access optimization, and I ran into a situation where a dictionary appeared to get out of sync in a way that should be impossible, unless I'm somehow getting into a multithreaded situation without knowing it.
One column of GridLabels binds to a property that does data access -- which is a tad expensive. However, multiple rows end up making the same call, so I should be able to head any problems off at the pass by doing a little caching.
However, elsewhere in the app, this same code is called in ways where caching would not be appropriate, I needed a way to enable caching on demand. So my databinding code looks like this:
OrderLabelAPI.MultiSyringeCacheEnabled = True
Me.GridLabels.DataBind()
OrderLabelAPI.MultiSyringeCacheEnabled = False
And the expensive call where the caching happens looks like this:
Private Shared MultiSyringeCache As New Dictionary(Of Integer, Boolean)
Private Shared m_MultiSyringeCacheEnabled As Boolean = False
Public Shared Function IsMultiSyringe(orderLabelID As Integer) As Boolean
If m_MultiSyringeCacheEnabled Then
'Since this can get hit a lot, we cache the values into a dictionary. Obviously,
'it goes away after each request. And the cache is disabled by default.
If Not MultiSyringeCache.ContainsKey(orderLabelID) Then
MultiSyringeCache.Add(orderLabelID, DoIsMultiSyringe(orderLabelID))
End If
Return MultiSyringeCache(orderLabelID)
Else
Return DoIsMultiSyringe(orderLabelID)
End If
End Function
And here is the MultiSyringeCacheEnabled property:
Public Shared Property MultiSyringeCacheEnabled As Boolean
Get
Return m_MultiSyringeCacheEnabled
End Get
Set(value As Boolean)
ClearMultiSyringeCache()
m_MultiSyringeCacheEnabled = value
End Set
End Property
Very, very rarely (unreproducably rare...) I will get the following exception: The given key was not present in the dictionary.
If you look closely at the caching code, that's impossible since the first thing it does is ensure that the key exists. If DoIsMultiSyringe tampered with the dictionary (either explicitly or by setting MultiSyringeCacheEnabled), that could also cause problems, and for awhile I assumed this had to be the culprit. But it isn't. I've been over the code very carefully several times. I would post it here but it gets into a deeper object graph than would be appropriate.
So. My question is, does datagridview databinding actually get into some kind of zany multithreaded situation that is causing the dictionary to seize? Am I missing some aspect of shared members?
I've actually gone ahead and yanked this code from the project, but I want to understand what I'm missing. Thanks!
Since this is ASP.NET, you have an implicit multithreaded scenario. You are using a shared variable (see What is the use of a shared variable in VB.NET?), which is (as the keyword implies) "shared" across multiple threads (from different people visiting the site).
You can very easily have a scenario where one visitor's thread gets to here:
'Since this can get hit a lot, we cache the values into a dictionary. Obviously,
'it goes away after each request. And the cache is disabled by default.
If Not MultiSyringeCache.ContainsKey(orderLabelID) Then
MultiSyringeCache.Add(orderLabelID, DoIsMultiSyringe(orderLabelID))
End If
' My thread is right here, when you visit the site
Return MultiSyringeCache(orderLabelID)
and then your thread comes in here and supercedes my thread:
Set(value As Boolean)
ClearMultiSyringeCache()
m_MultiSyringeCacheEnabled = value
End Set
Then my thread is going to try to read a value from the dictionary after you've cleared it.
That said, I am not sure what performance benefit you expect from a "cache" that you clear with every request. It looks like you should simply not make this variable shared- make it an instance variable- and any user request accessing it will have their own copy.

Is this code thread safe?

''' <summary>
''' Returns true if a submission by the same IP address has not been submitted in the past n minutes.
'' </summary>
Protected Function EnforceMinTimeBetweenSubmissions(ByVal minTimeBetweenRequestsMinutes as Integer) As Boolean
If minTimeBetweenRequestsMinutes = 0 Then
Return True
End If
If Cache("submitted-requests") Is Nothing Then
Cache("submitted-requests") = New Dictionary(Of String, Date)
End If
' Remove old requests. '
Dim submittedRequests As Dictionary(Of String, Date) = CType(Cache("submitted-requests"), Dictionary(Of String, Date))
Dim itemsToRemove = submittedRequests.Where(Function(s) s.Value < Now).Select(Function(s) s.Key).ToList
For Each key As String In itemsToRemove
submittedRequests.Remove(key)
Next
If submittedRequests.ContainsKey(Request.UserHostAddress) Then
' User has submitted a request in the past n minutes. '
Return False
Else
submittedRequests.Add(Request.UserHostAddress, Now.AddMinutes(minTimeBetweenRequestsMinutes))
End If
Return True
End Function
No. The ASP.NET Cache is not inherently thread-safe and it looks like you are creating objects in the Cache depending on whether they exist or not.
You need to lock the Cache when writing to it.
Let me word things a little differently. The code is, in fact, thread safe. The way you currently have it coded though could cause performance issues in multi-threaded situations.
In this case, multiple users would be running the same code simultaneously, theoretically accessing and modifying the same cache objects at the same time. As that scenario scales up, performance suffers.
Creating a lock will improve performance under heavy load (while imposing a slight overhead under light load) because you won't be fetching data neadlessly due to Caching issues.
The System.Web.Caching.Cache class is thread-safe according to the MSDN documenation. However, the documenation also shows an example where a read and a write are performed on the cache without locking. That cannot possibily be thread-safe since the write is dependent on the read. The code you posted basically looks like the example. I definitely recommend putting a lock around the entire method.

Resources