If one is checking a user's roles to determine whether they may access a page, is it safe to put this check only inside an if (!Page.IsPostBack) { ... }
? Could it be possible for the client to cause Page.IsPostBack == true
independently of ASP.net; that is, the client POST's to the page and sets the right form fields? If that were possible, then I suppose best practice would be to check security on every page load, not just when Page.IsPostBack == false
.
问题:
回答1:
Easily. And it doesn't even have to be via an HTTP post.
IsPostBack checks for the ViewState and Event* hidden fields. If you supply those fields on the query string then IsPostBack will actually return true, so, for example, a client page which tries to load an image using that jerry-rigged query string will cause the code behind to believe it's a post back.
回答2:
As a concrete example, suppose your page has a button that only administrators are supposed to be able to see and click:
<asp:Button runat="server" ID="resetButton" Text="Reset" OnClick="resetButton_Click" />
Inside an if (!IsPostBack)
block in the code-behind, you hide the button if the user isn't an admin:
protected override void OnInit(EventArgs e)
{
if (!IsPostBack)
resetButton.Visible = IsAdmin();
}
private bool IsAdmin()
{
...
}
Question: Can a non-administrator cause the code in resetButton_Click
to execute?
Answer: YES, even if view state and event validation are enabled.
Someone can simply browse to your page with ?__VIEWSTATE=
or ?__EVENTTARGET=
appended to the URL (causing IsPostBack
to return true
) and then click the button.
Conclusion: Turning off sensitive functionality in if (!IsPostBack)
is NOT safe.
To fix the problem, remove the IsPostBack
check or add Visible="False"
to the button markup (secure by default).
回答3:
You'd be setting yourself up for some nasty form attacks if you didn't check in all cases; consider that a malicious user could just build the HTTP request to your server with all the proper form values to make ASP.NET think that its a postback. I would highly recommend checking for user role on every request, postback or not.
回答4:
So, as others point out this is a bad idea, you shouldn't build in holes into your authorization system. In fact, authorization shouldn't happen in pages at all but in HttpModules that look at the request before the page processes it.
To specifically answer the question, unless you use ViewState and you set a ViewStateUserKey value that is unique to your authenticated user then it is easy to make your system think IsPostback is true when it is not. It validates the viewstate and event fields, and the viewstate can be forged. Even if it is encrypted with a machinekey, it's insufficient because the attacker can go to the page themselves, copy the encrypted viewstate and use it in their malicious attack.
In review, peruse how authentication is usually done in the system and use something off the shelf and not hand-rolled.
* EDIT *
The long answer: when you're talking about security you want to break it down to the threats and enumerate them. Once you have an understanding of your threats, you can determine your risk from these threats. Once you have threats and risk, you can determine if it is worth mitigating the threat, and how to go about doing it. For example, many people wear seat belts because accidents are common but we do nothing to protect ourselves from alien invasion.
You appear to be concerned about the following threats:
- Unauthenticated users accessing your site
- Some user who is authenticated, but does not have a required privilege seeing some portion of your page
- Cross site request forgeries
To mitigate the first threat, look at the standard off-the-shelf components for doing authentication like the FormsAuthenticationModule which is part of Forms Authentication. These components are built in to ASP.Net and are well designed.
Once a user is properly authenticated to your app, to determine if they have a specific privilege look at Role Based authorizaton and the RoleManagerModule. This will give you the opportunity to associate a set of roles with each identity that was authorized by Forms Authentication.
Finally, you may be unaware but you've stumbled upon another security issue which is a malicious user may convince an authorized user of your application who is at some other location on the internet to cause a POST to occur against your page. The attacker could create a hidden form on a different web page and use javascript to submit it to your web page. If the authorized user is already logged in, then whatever action that should have occurred in the page will occur as the victim but all parameters of the POST are controlled by the attacker (who wrote the code for the form and its values).
To protect yourself against this, the easiest thing to do is use the ViewStateUserKey value I mentioned before and make sure that either EnableViewStateMAC or ViewStateEncryption is used. Encryption is preferred as the HMAC will only make sure an attacker can't tamper with the viewstate but the contents of it is still recoverable. Encryption provides confidentiality and integrity.
回答5:
It is absolutely possible to POST to a URL independently of ASP.Net. You don't even need a browser.
You should check out ASP.Net's built-in authentication, authorization, and membership features rather than trying to roll your own.
Here's a good place to start: http://www.4guysfromrolla.com/articles/120705-1.aspx and http://www.4guysfromrolla.com/articles/031204-1.aspx
回答6:
Sorry to all those who have answered already, but I don't agree that only checking for security authorization inside a Page.IsPostBack == false
block is necessarily insecure (as long as event validation and encrypted viewstate are turned on). I have explained why I think this here, but the short answer is: I don't think you can spoof a postback to a page without first loading it in a non-postback context to get the viewstate and eventvalidation form fields. The viewstate field returned will cause the content you've hidden inside your Page.IsPostBack == false
block to stay hidden in any postback which uses that viewstate, and because the viewstate is encrypted it can't be tampered with.