A secure implementation of Page.IsPostBack? - c#

Following my previous question as to whether ASP.net's default Page.IsPostBack implementation is secure (it's not; it can be faked... the HTTP verb doesn't even have to be POST!), I was thinking; surely there must be a better way to implement it? Can we come up with a Page.IsPostBack implementation which, when it is true, is almost guaranteed to indicate that the page is an actual ASP.net postback? This is important if one wants to do security checking only once (like whether some content is going to appear, based on the user's role(s)), and wants to do it only if we're NOT dealing with an ASP.net postback.
My first thoughts as to how to do this are to implement the checking code in a property, so I can write something like this inside Page_Load:
if (!_isPostBack)
{
// Do security check
if (userIsNotAuthorized)
{
btnViewReports.Visible = false;
btnEditDetails.Visible = false;
// etc.
}
}
Is there a way to securely implement _isPostBack? Perhaps storing something in the ViewState that would be hard or impossible to jerry-rig to fake a postback? A random string?

I had a project a couple of years ago where we had some penetration testing done on the code. They flagged up the fact that by default IsPostback doesn't check the http verb. To address this I created an abstract Page class with its' own implementation of IsPostback that shadows the default implmentation:
Public Class ProjectPage : System.Web.UI.Page
public new bool IsPostBack()
{
return (Page.IsPostBack && Request.HttpMethod.ToUpper() == "POST");
}
End Class
This allows you to do testing on the http verb, but you could easily extend the method to do other checks as well.

OK, here's what I think is the solution: Page.IsPostBack is already secure enough, as long as event validation is enabled. Let me explain my reasoning below and I'd be happy for anyone to add a comment if I've gotten something wrong.
In order for a spoof postback to be posted to ASP.net and trigger a control's OnClick event, with event validation enabled, the client has to send the __EVENTVALIDATION form field. This field contains a uniquely-generated string that basically tells ASP.net which controls a postback event for that page may have originated from. If you try to spoof a postback for a button which has had .Visibility = false set on it, you'll see an event validation error message. So, it looks like you can't directly spoof a click on a hidden control.
What about spoofing a postback of one of the existing buttons on the page that you has been rendered (ie. you do have permission to view/click on it)? Well, you can send the postback to the page, but you need to submit a valid __VIEWSTATE or you'll just get a 'state information invalid' error. In order to have a valid __VIEWSTATE, you already need to have loaded the page as a non-postback, right? That means that the security-checking code will have executed at least once, hiding the appropriate controls and recording that in the __VIEWSTATE. So, when you post the spoof postback, yes it will cause Page.IsPostBack to be true, but it doesn't matter because the submitted __VIEWSTATE will already have been generated on the previous non-postback page load to hide the content that you shouldn't have access to... so, you can spoof a postback, but only by passing a __VIEWSTATE that has been previously generated by a non-postback page load.
So, because of these facts, it should be safe to only put security-checking code inside a Page.IsPostBack == false block. This must always get run once before a valid postback can be submitted to the ASP.net server. Or am I missing something?

A cookie is a much better mechanism for your needs. The cookie is a token that could only have been generated by the server and vouches for the holder of the token certain claims such as having signed in recently and for having certain permissions and/or preferences. Some of these features are built into FormsAuthentication. You can implement your own cookie mechanism, but you should research secure cookie protocols because there are several non-obvious security considerations.
The benefit is that you don't have to go to the database on every request, you just trust it. This could also be a good strategy for weathering certain DoS attacks because you can tier your app such that a dedicated device in front of your app servers validates tokens too and throws out invalid requests.
If cookies aren't allowed, you can send the token as part of the url, as formsauth allows, or as a form field in your postback. But that's more work to manage then cookies, IMHO, once you've gone through the trouble of generating a proper token.

Related

Storing viewmodel data in Session creates problems with validation using FluentValidation

I'm currently working on a large project involving Sitecore CMS (7.2). For viewmodel validation we are using FluentValidations. Because of the combination of Sitecore and FluentValidations I seem to be running in some kind of technical deadlock. I sort-of found a solution myself, but I'm not sure whether this is the right approach or not. Here's the problem:
Situation
There is a Sitecore component which contains a HTML form. Via the modelbinder each value of this form is binded to it's corresponding field in the (complex) viewmodel. This is standard .NET MVC approach.
However, some values in the viewmodel are NOT part of the form. For instance, a date at which the mutation will be applied is calculated by the application. The user can only see this date-value as plain text, and thus can not edit it. It's still part of the viewmodel though. To make sure this value is being posted back to the model in code, one would normally use a hidden field. But if I use a hidden field, it means that users are able to spoof that date and because some validations depend on this value, they are able to spoof the entire validity of the form.
Moreover, in the same viewmodel I have a list of complex objects that I can't simply put in a hidden field (or I should serialize it to JSON, which I don't want).
The conclusion is that I need to store this data somewhere else. Somewhere the user can't spoof it, but I'm still able to validate user input with FluentValidations. I therefore decided to put the entire viewmodel in the users Session, and delete it directly after a succesful mutation.
Problem
By using session data I run into problems. Let's first see these steps:
(GET) Viewmodel is created. Calculated date is set and list of complex types is retrieved once from a (slow) webservice.
Entire viewmodel is stored as session data.
Form is shown to the user, who fills the form. Some data is only shown as readonly, like the date and list of complex types.
User submits form, FluentValidations kicks in to validate the data (POST).
That's where I run into problems. The validation done by FluentValidations kicks in before it reaches the POST controller method. That's exactly the way we want it, because that means validation errors are automatically added to the ModelState. However, because of security reasons I don't want to add this data as hidden fields to the cshtml file, which means they are empty at the time FluentValidations is going to validate the form.
This is creating problems because some of the form validations rely on the missing data. What I basically want is to merge the viewmodel that is stored in the session with the viewmodel that was posted to the controller method. But I have to do that before FluentValidations is going to do it's work.
My current solution
Gladly, I learned about FluentValidation's IValidatorInterceptor: an interface that can be used to 'do stuff' before or after the validations process kicks in. I used the BeforeMvcValidation method to do my merging process. The code is as follows:
public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
{
if (controllerContext.HttpContext.Session == null)
return validationContext;
var sessionData = controllerContext.HttpContext.Session["some_identifier"];
if (sessionData == null)
return validationContext;
var mergedObjectToValidate = Utils.MergeViewModelData(sessionData, validationContext.InstanceToValidate);
// Unfortunately, we have to do this..
var privateSetterProperty = validationContext.GetType().GetProperty(ValidationContextInstancePropertyName);
if (privateSetterProperty == null)
return validationContext;
privateSetterProperty.SetValue(validationContext, mergedObjectToValidate);
return validationContext;
}
Basically this interceptor method allows me to do my merging-process before validation. So I thought I had the solution here, but as you can see I am using reflection to set a property. That is because the property InstanceToValidate in the ValidationContext object has a private setter. I simply can not set it without using reflection. Which is, obviously, a bit dirty.
It does work exactly as I want though! :) I do not need any hidden fields that can be spoofed (which is horrible for straight-trough-processing) and I can still use FluentValidations exactly as I always did before. Also, the MVC modelbinding-process is left untouched, which I prefer.
The actual question
So the above solution works exactly as you want so what are your questions?! Well, simple:
I'm using reflection to set a private property in a 3rd party library (FluentValidations). The obvious answer is: don't go that way. But in this case it works flawlessly. If the InstanceToValidate-property had a public setter, I wouldn't even be posting this question at all: I would feel like I nailed it. But unfortunately it is private, so are there any real reasons why I shouldn't do this, maybe someone being an expert in FluentValidations behaviour?
Let's say there is a genuine reason why I shouldn't go this way; is there another approach which has the same effect? Can I hook in even earlier, so before FluentValidations kicks in, perhaps some kind of 'hook' just after the MVC model-binding process but before validation kicks in?
Is this entire approach simply wrong and should I tackle it in a completely different way?
Thanks!

A potentially dangerous Request.Form value was detected from the client for audit trails

I know this asked many times but I have different use case. Here it goes.
I have audit trails implemented and I do log every single request. When user submits a post I log user data as well, whatever she/he posts in the form.
Then i get back this error.
I do have [AllowHtml] annotation also i have done stuff that s recommended on other questions but i dont want to disable request validation. Request passed through my ActionResult.
I have this code that breaks it.
return Json.Encode(new { request.Cookies, request.Headers, request.Files, request.Form, request.QueryString, request.Params });
How can i do this?

Problem grasping webpart communication

I'm having trouble understanding some web part communication code, and how to make it work as needed. I'm fairly new to web parts, so I figure I'm over complicating it.
I have
public interface IFormTypeRID
{
int FormTypeRID { get; }
}
For this page, I have a ListControl on the left which is a web part. When you select something with this list control, it posts back and sends this interface to the MainForm web part So I have this on the receiving end:
private IFormTypeRID theProvider;
[ConnectionConsumer("FormTypeRID Consumer", "FormTypeRIDConsumer")]
public void InitializeProvider(IFormTypeRID provider)
{
theProvider = provider;
FormTypeRID = theProvider.FormTypeRID;
}
Now, my problem is that InitializeProvider happens after Page_Load.
This means that when a post back happens from inside the MainForm webpart, the FormTypeRID is never sent in. Well, thats easy just store it in view state or a hidden field.
But theres the problem.
How do I act on FormTypeRID being set if it's after PageLoad. Currently, I just have a setter method that calls the appropriate functions. This is troublesome however because the code in the setter must be executed twice. This is because FormTypeRID must be set in Page_Load because it is unknown whether our provider will be giving us our FormTypeRID or not(because we do not know if the post back happened because of the other webpart, or because of the FormMain)
I know I explained that horribly. But basically, how do you tell if a postback happened(and therefore a page_load) from one webpart(the provider) or another?(the consumer)
The answer to all of this is non-trivial.
I ended up writing my own "webpart communication" which actually ended up being a lot cleaner than ASP.Net's and it will work during Init and Load and so on.

Alternative to Query Strings to Pass Data Between ASP.Net Pages?

I am currently using a number of query string parameters to pass some data from one page to a second page (the parameters hold confirmation/error messages to display in the second page), that due to a third party product can no longer work correctly in the production environment. The user completes an action on the first page, and is then transferred to the second page. What are the possible alternatives to use instead of a query string and GET - session variables, POST data, or something completely different?
Thanks, MagicAndi.
You could create public properties in a source page and access the property values in the target page when using a server transfer. You could also get control information in the target page from controls in the source page by referencing the Page.PreviousPage property.
Both of these methods are oulined here: http://msdn.microsoft.com/en-us/library/6c3yckfw.aspx
Both POST data and session variables would work just fine. POST data has the drawback that it can be changed by the client and session variables take up memory, so you can choose based on that. I personally don't think that you should pass such messages to the client for the reason stated above but I guess you are already doing that, so...
you can use this if you use window.open("openTheotherPage",...etc)
so form the opened page you can do something like this
var valuefromCallerPage = window.opener.document.FormNmae.textbox.value
or button or anything on the caller page

How to get the value of built, encoded ViewState?

I need to grab the base64-encoded representation of the ViewState. Obviously, this would not be available until fairly late in the request lifecycle, which is OK.
For example, if the output of the page includes:
<input type="hidden" name="__VIEWSTATE"
id="__VIEWSTATE" value="/wEPDwUJODU0Njc5MD...==" />
I need a way on the server-side to get the value "/wEPDwUJODU0Njc5MD...=="
To clarify, I need this value when the page is being rendered, not on PostBack. e.g. I need to know the ViewState value that is being sent to the client, not the ViewState I'm getting back from them.
Rex, I suspect a good place to start looking is solutions that compress the ViewState -- they're grabbing ViewState on the server before it's sent down to the client and gzipping it. That's exactly where you want to be.
Scott Hanselman on ViewState Compression (2005)
ViewState Compression with System.IO.Compression (2007)
See this blog post where the author describes a method for overriding the default behavior for generating the ViewState and instead shows how to save it on the server Session object.
In ASP.NET 2.0, ViewState is saved by
a descendant of PageStatePersister
class. This class is an abstract class
for saving and loading ViewsState and
there are two implemented descendants
of this class in .Net Framework, named
HiddenFieldPageStatePersister and
SessionPageStatePersister. By default
HiddenFieldPageStatePersister is used
to save/load ViewState information,
but we can easily get the
SessionPageStatePersister to work and
save ViewState in Session object.
Although I did not test his code, it seems to show exactly what you want: a way to gain access to ViewState code while still on the server, before postback.
I enabled compression following similar articles to those posted above. The key to accessing the ViewState before the application sends it was overriding this method;
protected override void SavePageStateToPersistenceMedium(object viewState)
You can call the base method within this override and then add whatever additional logic you require to handle the ViewState.

Categories