Privilege Elevation in an MVC3 web application with Windows authentication - asp.net-mvc

I have a requirement to implement user privilege elevation in an MVC3 web app, for both Forms and Windows authentication, but this question is critical for Windows auth. This is for a higher privileged user to give assistance to a lower privileged user, e.g. when a clerical user is performing a task and requires an admin user to do a task before the clerical user can continue, the admin user should be able to elevate the same session to their privilege level, perform the admin task, and restore the lower privilege to the session. I don't see a way here without the clerical user logging off and the admin user logging on, given that we want to achieve this on the desktop of the clerical user alone. Maybe user switching is tidier than a whole new session, but I would very much like a "run as" equivalent for Windows authenticated web apps.
Is this even possible, and if so, how can I achieve this? I have no idea where to even begin looking.

Allow the "power user" to temporary set a specific role for other users and for example setting also an expiration of the role with a DateTime.

You could put an anchor somewhere on your site:
#Html.ActionLink("elevate to admin", "SwitchToAdmin", "Home")
and then have a controller action which will allow for inputting the administrator credentials:
public ActionResult SwitchToAdmin()
{
// TODO: Adjust the role name that your administrators will have
if (!User.IsInRole(#"DOMAIN\Administrators"))
{
// The user is not currently an admin => popup a Logon box
// so that the administrator could authenticate himself
return new HttpUnauthorizedResult();
}
else
{
// After inputting the correct username and password for the
// admin, we can now redirect to the home action and start performing
// the admin tasks
return RedirectToAction("index", "home");
}
}
The revert process will be the inverse. You could have a link which will call a controller action that will throw 401 if the user is an admin allowing for the normal user to enter his username and password.

In order to use Windows authentication to do this I think you will need:
The run as command
A shortcut on the user's desktop to start the other logon
Either a batch script to prompt for the user's logon information or a separate desktop program to gather the information (the shortcut points to whichever of these you choose)
once the information for the run as commandline is ready you could either start a browser or perhaps a custom program with an embedded browser.
An advantage of the program with embedded browser approach is that it can have extra security precautions such as forcibly closing itself after a timeout.
Anyway that's one possible solution. You might also try to come up with a less complicated way to solve the business need. Perhaps a remote desktop session for the admin?

The equivalent of the run as command is using user impersonation. That is running the commands that requires higher privileges as another user.
It should work as follows:
1) User try to access privileged resources. The webapp detect this either because it has a kind of table of all task reuiring higher privileges, or by intercepting the security exception it gets trying to perform the operation.
2)When this is detected you throw a "RequiresPrivilegesElevationException"(an exception you have to define). This exception i catched by the controller, that now knows it must prompt the user for higher privileges
3) the controller prompt the user for the admin (or higher privileges user password)
4) when the user send the credentilas (via https) credentials are used to create an impersonation context, and all operations are done within this impersonation context.
The drawback of thos approach is that the credentials and the privilege elevetion last for just one trip to the server...for any other request the user is forced to re insert the credentials. THERE IS NO SAFE WAY TO AVOID THIS due to security browser limitations

Related

ActiveDirectoryMembershipProvider and Forms authentication

I've got a ASP.NET MVC web app which uses forms authentication.
I'm using ActiveDirectoryMembershipProvider to validate users against our domain.
if (Membership.ValidateUser(m.Username, m.Password))
{
FormsAuthentication.SetAuthCookie(m.Username, true);
....
This means the user gets validated only when they log in.
Problem with that is ofcourse that if the user's password changes they still remain logged in. Or worse, user leaves our company with a grudge, and they still have access.
I would have thought such a simple use case would have an obvious answer but I've been stuck on this for a while now.
I could put the users password in the session and then validate it every time, but that doesn't feel right.
What is the suggested/correct way of handling this?
The typical solution is to force log out when users unsubscribes from the service or less commonly when they change password. Use this method:
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
If the user can be deactivated outside of the app (i.e. Active Directory), the typical practice is to rely on the session time-out and perhaps ask for the credentials once more for critical operations. If you absolutely cannot allow the deactivated user to work while the session is still active, then yes, you'll have to check the credentials on every request. Since storing the password in the app is a very bad idea, it means you'll have to ask for credentials on each request which arguably is an even worse idea.
As for the password change, it normally doesn't modify the user's permissions so it should be harmless to allow for them to continue working.
The answer is to periodically (every 30 minutes or so) check User.IsApproved and User.LastPasswordChangedDate to make sure the users credentials are still valid.
To do this you need to manually create the FormsAuthenticationTicket and cookie, rather than using FormsAuthentication.SetAuthCookie.
Put the date you validated the user inside UserData and compare this against LastPasswordChangedDate.
I've implemented this and it works perfectly.
More information here
Check if Active Directory password is different from cookie

ASP.NET MVC: A PreAuthorize-like event, Good throttle point for concurrent logons?

I want to allow a new login to 'kick' a prior login session by the same account, in ASP.NET MVC.
It seems pretty clear that I'll give each browser a cooking representing the session ID. I'll track the currently active session ID in a server-side cache. If an already-active user attempts to log in, I'll verify the business logic (username, password, has been at least 15 minutes since last activity), and then update the active session ID cached at the server.
Now my issue is, a browser is holding an invalid session ID. What is the best point for me to inject a rejection or redirect to sign-in for this scenario?
I could modify the AuthorizeAttribute, but it seems like there should be a cleaner place to do this that won't require me to search and replace all my Authorize attributes, for example via a Global.asax event, or a controller event (I've already extended Controller in my project).
For example, if PreAuthorize existed, I would write some code there to test the request's cookies for a valid user/session ID pair, and if it didn't exist, I could simply remove the authentication cookie from the request, which would result in a standard unauthorized redirection.
So after a bit of research, it seems that a custom AuthorizeAttribute would typically be the correct approach. However, in my case, since I already had a custom role provider implemented, it was just a line of code to do it there. This also benefited me because I only wanted session concurrency for a single role. A side effect is that any use of web.config to control access to static files by role is also in-effect for session concurrency.

When does the .NET FormAuthentication ticket get checked and how do I tap into this event?

We are attempting to integrate an ASP.NET MVC site with our client's SSO system using PingFederate. I would like to use the built in FormsAuthentication framework to do this. The way I've gone about it so far is:
Set up my Web.config so that my FormsAuthentication LoginURL goes to my site's "BeginAuthentication" action on a "Security" controller. From this action, I set up some session variables (what URL was being accessed, for example, since Ping won't send this info back to me), and then redirect to our client's login page on an external site (www.client.com/Login for example).
From here, the authentication takes place and a cookie is generated on the same domain as the one that our application is running on which contains the unique identifier of the authenticated user, I've set it up so that once this happens, the Ping server will redirect to my "EndAuthentication" action on my "Security" controller.
In this action, I call my membership class's "ValidateUser" method which takes this unique identifier from the cookie and loads in the user on our application that this ID refers to. I save that logged in user in our Session (Session["LoggedInAs"], for example) and expire the cookie that contains the id of the authenticated user that the SSO system provided for me.
All of this works well. The issue I'm wondering about is what happens after our user has already authenticated and manually goes back to our client's login page (www.client.com/login) and logs in as another user. If they do that, then the flow from #2 above to number 3 happens as normal - but since there already exists an authenticated user on our site, it seems as though the FormsAuthentication system doesn't bother kicking off anything so I don't get a chance to check for the cookie I'm looking for to login as this new user. What I'd like to do is, somewhere in my Global.asax file (probably FormsAuthenticate_OnAuthenticate), check to see if the cookie that the SSO system sends to me exists, and if so, sign out of the application using FormsAuthentication.SignOut().
Another issue that seems to be related is that if I let my Session expire, the FormsAuthentication still seems to think I am authenticated and it lets me access a page even though no currently logged in user exists in my Session, so the page doesn't render correctly. Should I tap into the Session_End event and do FormsAuthentication.SignOut() here as well?
Basically, I want to know when the authentication ticket created by
System.Web.Security.FormsAuthentication.SetAuthCookie(..) gets checked in the flow of a request so that I can determine whether I need to SignOut() and force revalidation or not.
Thanks for any help. Sorry for the length of this message, trying to be as detailed as possible.
Mustafa
Welcome to the small section of Hades that is mixing session with formsauth.
If your needs are as complex as presented, you would get more sleep if you implement a full provider stack to share amongst the participating sites. Easier said than done, I know.
But to address your question:
from http://www.codeproject.com/Articles/39026/Exploring-Web-config-system-web-httpModules.aspx
On the way in....Check ticket and set identity #
app.AuthenticateRequest += System.Web.Security.FormsAuthenticationModule.OnEnter-->OnAuthenticate
On the way out... set the ticket and redirect as necessary
app.EndRequest += System.Web.Security.FormsAuthenticationModule.OnLeave
Reflector is your friend. ;-)
I don't know about a specific event for when the cookie is checked, but you could place the appropriate logic in Application_BeginRequest() and check the user's authentication state there.
Another issue that seems to be related
is that if I let my Session expire,
the FormsAuthentication still seems to
think I am authenticated and it lets
me access a page even though no
currently logged in user exists in my
Session, so the page doesn't render
correctly.
The life of the cookie (how long until ASP.NET feels it needs to ask for a password again) and how you are managing state are unrelated. The ASP.NET authentication is cookie based so that, should a developer want to, he could turn off viewstate, session, use no query strings or hidden fields and authentication still works.
If you want to tie the interval at which you request the password to how you are persisting data, then you will want your session expiration to be roughly the same as the cookie expiration, but they will never quite match up. It would be better to have two policies (one for how fast you throw away a users session data and one for how long you are willing to wait before you need to reask for a password)

Handling website access changes when using cookies

If I have a site that uses cookies for authorisation, so when the user returns they don't have to login again.
If for some reason the site admin cancel this users account what is the best way to check for this. I don't want to have to hit the database every time the user visits a page to make sure their account is still live.
So how should I handle this situation?
Several options. Set a shorter expiration on the cookie so they'd have to get authenticated afresh sooner.
Another alternative is to have any important action require that they be authenticated against the database. Thus you would only be hitting the database for more privileged actions that would more likely overlap with the high priority things you'd want a cancelled user be unable to do.

Setting up a private beta for a website

I'm trying to setup a "private beta" for a site that I'm working on. The site uses open id. I don't want anyone to even browse the pages if they aren't part of the beta. What's the best way to implement this? Any suggestions?
For example:
When the site goes live, users will go to http://www.mydomain.com which will not require them to log in.
For the beta I want to restrict access. Users that go to http://www.mydomain.com will be redirected to a login page. Anyone attempting to access ANY PART OF THE SITE who is not authenticated will be redirected back to the login page.
I could stick [Authorize] attributes all over my controller actions, but that seems stupid.
If you're using ASP.NET MVC, it comes with authentication/authorization out of the box. You should be able to use that to setup authentication on your site.
Alternatively you could setup app server settings - IIS lets you setup username/password on a specific site it's serving, regardless of what the actual application may do. If you have access to the app server this might be the best solution.
If you're using IIS6, you can setup authorization easily. Right-click on your site > Properties > Directory Security Tab > Authentication and Access Control > Edit, and enter a username/pwd of your choice. Done.
The real question is how are they being invited to the private beta?
You could setup a password which drops a cookie much like serverfault.com does.
OR
If you know who you are inviting: you could add them to the system before hand using the email/login information that you already know about them (assuming you are inviting them via email)
I have implemented a function in a web application a while ago where we go the possibility to block access to the full website unless the user was an administrator (which in our case meant that the user account was a member of a specific group in Active Directory).
It was based on two things. First, all pages in the web application inherited not directly from the Page class, but from a custom page class in our web application. Second, we had a value like this in the appSettings section of web.config file:
<add key="adminaccessonly" value="0" />
The custom page class would check that value when loading. If it was not 0 it would redirect to a page (that did not inherit the same custom page class, though) informing the user that "the site is not available right now". If the value was 0 the page would load as usual.
In that application we used this to be able to take the site "offline" when we deployed a new version, giving us some time to verify that all was good before we let in the users again.
Best way are invitation system (based on invitation code) or manually confirmation access after create profile in your system. imho
Or you could host the site on a private server, and set up a VPN to use it. Depending on your resources and needs this may be the easiest and most secure way to do what you want without modifying your codebase.
OR alternatively you could use Apache or IIS to force authentication on access to the website directory. Keeping the authentication info in .htaccess for a while.
Even though you use open id authentication, you may still need some form of authorization mechanism. The simplest form would be a user-roles system in your database that assigns different roles to users
In your case, just assign the private_beta role to your private beta invitees and ensure you your authorization mechanism that all users have private_beta privilege before they may continue.
If you don't want to provide authorization for the public site (where everyone can do everything, once authenticated), then, you may only need to do a quick-and-dirty post-processing (for private beta only) on your open_id authenticated users to check them off a short list (which you can store on a text file.

Resources