How to keep RoleProvider from overriding custom roles? - asp.net

I have an custom role provider that gets the roles a user belongs to from a database. I also have a custom authentication module registered in my web.config's httpModules which sniffs incoming HTTP requests and (if it's an OAuth signed request) sets the HttpContext.Current.User property to impersonate the user, and the IPrincipal that it sets includes all the user's roles, plus an extra one called "delegated".
The trouble is, after I set my custom IPrincipal, apparently ASP.NET still calls my custom role provider, and then resets the IPrincipal with one that has only the standard roles for that user.
If I set <roleManager enabled="false" ...> in my web.config file, the authentication module's assigned roles stick. Obviously though, I want the best of both worlds. How can I use the role provider, but "cancel" the role provider's effect when my authentication module decides to?

It turns out that in the authentication http module's Init method, I can find the RoleManager, and then hook an event that gives me veto power on whether it does its overriding work:
public void Init(HttpApplication context) {
var roleManager = (RoleManagerModule)context.Modules["RoleManager"];
roleManager.GetRoles += this.roleManager_GetRoles;
}
private void roleManager_GetRoles(object sender, RoleManagerEventArgs e) {
if (this.application.User is OAuthPrincipal) {
e.RolesPopulated = true; // allows roles set in AuthenticationRequest to stick.
}
}
private void context_AuthenticateRequest(object sender, EventArgs e) {
if (/*oauth request*/) {
HttpContext.Current.User = CreateOAuthPrincipal();
}
}

Related

Asp.net preventing HTTP Replay Attack

I have an asp.net webforms project. In there, an "Admin" has the ability to get to a screen to submit a form that will create a new user (CreateUser.aspx).
If that user's permission is changed later to be less than Admin they can't access that screen anymore to create new users. However, if they saved the exact web request to create a new user from when they were an admin, they can replay this and create new users still.
It is not possible for a random person to submit this form though unless they guess an active session id (that is the only thing checked on each form submit).
CreateUser is one of many examples of admin functions. Is the proper solution to this to validate that the user is an admin on each of these different form submits (not sure of a clean way to do this in asp.net)? Or is it acceptable to just expire the session on logout and never reuse session id's? Does asp.net have any other security features built-in to protect against this type of attack?
One way, is to have a base class(Which inturn inherits the class - "page") and all your aspx pages should inherit this base class. so that whenever a aspx page loads, your base pageLoad method is called. In this method you can check the authentication and authorization.
public class BasePage : System.Web.UI.Page
{
public BasePage()
{
this.Load += new EventHandler(BasePage_Load);
}
void BasePage_Load(object sender, EventArgs e)
{
//Check authentication/authorized
//if authenticated/authorized leave it.. If not redirect to error page
}
}
public partial class Forms_CreateUser : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
public partial class Forms_CreateRole : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
User web.config's Authorization settings to restrict access to CreateUser.aspx to users in the admin role: http://support.microsoft.com/kb/316871 (attribute name is roles instead of users).

Securing SignalR Calls

I'm using the SignalR Javascript client and ASP.NET ServiceHost. I need the SignalR hubs and callbacks to only be accessible to logged in users. I also need to be able to get the identity of the currently logged in user from the Hub using the FormsIdentity from HttpContext.Current.User.
How do I secure the hub's so that only authenticated users can use SignalR?
How do I get the identity of the currently logged in user from the Hub?
You should use the this.Context.User.Identity that is available from the Hub. See a related question
EDIT: To stop unauthenticated users:
public void ThisMethodRequiresAuthentication()
{
if(!this.Context.User.Identity.IsAuthenticated)
{
// possible send a message back to the client (and show the result to the user)
this.Clients.SendUnauthenticatedMessage("You don't have the correct permissions for this action.");
return;
}
// user is authenticated continue
}
EDIT #2:
This might be better, just return a message
public string ThisMethodRequiresAuthentication()
{
if(!this.Context.User.Identity.IsAuthenticated)
{
// possible send a message back to the client (and show the result to the user)
return "You don't have the correct permissions for this action.");
// EDIT: or throw the 403 exception (like in the answer from Jared Kells (+1 from me for his answer), which I actually like better than the string)
throw new HttpException(403, "Forbidden");
}
// user is authenticated continue
return "success";
}
You can lock down the SignalR URL's using the PostAuthenticateRequest event on your HttpApplication. Add the following to your Global.asax.cs
This will block requests that don't use "https" or aren't authenticated.
public override void Init()
{
PostAuthenticateRequest += OnPostAuthenticateRequest;
}
private void OnPostAuthenticateRequest(object sender, EventArgs eventArgs)
{
if (Context.Request.Path.StartsWith("/signalr", StringComparison.OrdinalIgnoreCase))
{
if(Context.Request.Url.Scheme != "https")
{
throw new HttpException(403, "Forbidden");
}
if (!Context.User.Identity.IsAuthenticated)
{
throw new HttpException(403, "Forbidden");
}
}
}
Inside your hub you can access the current user through the Context object.
Context.User.Identity.Name
For part 1. of your question you could use annotations like below (This worked with SignalR 1.1):
[Authorize]
public class MyHub : Hub
{
public void MarkFilled(int id)
{
Clients.All.Filled(id);
}
public void MarkUnFilled(int id)
{
Clients.All.UnFilled(id);
}
}
Something missing from the other answers is the ability to use SignalR's built in custom auth classes. The actual SignalR documentation on the topic is terrible, but I left a comment at the bottom of the page detailing how to actually do it (Authentication and Authorization for SignalR Hubs).
Basically you override the Provided SignalR AuthorizeAttribute class
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthAttribute : AuthorizeAttribute
Then you decorate your hubs with [CustomAuth] above the class declaration. You can then override the following methods to handle auth:
bool AuthorizeHubConnection(HubDescriptor hubDesc, IRequest request);
bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubContext, bool appliesToMethod);
Since I'm on IIS servers and have a custom auth scheme, I simply return true from the AuthorizeHubConnection method, because in my Auth HttpModule I already authenicate the /signalr/connect and /signalr/reconnect calls and save user data in an HttpContext item. So the module handles authenticating on the initial SignalR connection call (a standard HTTP call that initiates the web socket connection).
To authorize calls on specific hub methods I check method names against permissions saved in the HttpContext (it is the same HttpContext saved from the initial connect request) and return true or false based on whether the user has permission to call a certain method.
In your case you might be able to actually use the AuthorizeHubConnection method and decorate your hub methods with specific roles, because it looks like you are using a standardized identity system, but if something isn't working right you can always revert to brute force with HttpModule (or OWIN) middle-ware and looking up context data in on subsequent websocket calls with AuthorizeHubMethodInvocation.

ASP.NET MVC 3 Custom Authentication/Authorization

I have searched all over the internet and SO, and I have found some good stuff on this topic, but I have a few questions that I am still unsure about:
1) I am using Forms Authentication with a custom Authentication provider. So I use the Authorize attribute and the section in the web.config still, but basically when the FormsAuthenticationTicket does not exist, I redirect to a login page (specified in the web.config) which then utilizes the custom Authentication Provider to auth the user against a db and then issues the FormsAuthenticationTicket. Is this correct?
2) Should I be using a custom Authorize attribute or should I just inject a GenericPrincipal into the HttpContext from the Application_AuthenticateRequest event handler in the global.asax page? Or should I be using User.IsInRole insode of the controller actions?
I just need role based authorization, and I think my Authentication Scheme is pretty good.
Any pointers/advice?
Thanks,
Sam
Edit
So from what I have read, the best option for this is to create a custom AuthorizeAttribute and override the AuthorizeCore.
So what I have done is this:
public class CustomAuthorize : System.Web.Mvc.AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity.IsAuthenticated)
{
var model = AdminUserViewModel.FromJsonString(((FormsIdentity)httpContext.User.Identity).Ticket.UserData);
httpContext.User = new GenericPrincipal(HttpContext.Current.User.Identity, model.SecurityGroups.Select(x => x.Name).ToArray());
}
return base.AuthorizeCore(httpContext);
}
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
//base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new System.Web.Mvc.RedirectResult("/Authentication/NotAuthorized", false);
}
}
Simply inject a new principal/identity with the roles that are stored in the FormsAuthenticationTicket UserData property. Then let the base do the rest.
Does this seem to be OK?
Edit #2
I am a little weary of using the Application_AuthenticateRequest in the global.asax with IIS7, because of the integrated pipeline, every request fires that event, images, css, js...
Is this correct?
1) I do the same thing.
2) I use Authorize attribute and Application_AuthenticateRequest event handler.
In Application_AuthenticateRequest event handler I do something like this:
string[] roles = authenticationTicket.UserData.Split(',');
if (Context.User != null)
Context.User = new GenericPrincipal(Context.User.Identity, roles);
And at controller or action level I do something like this:
[Authorize(Roles = "Admin, SuperAdmin")]

Custom Principal reverting to GenericPrincipal on new requests

I'm trying to implement a custom principal and custom identity in a .NET MVC website. I've created a custom principal class which inherits from IPrincipal and a custom identity which inherits from IIdentity.
When a user logs in I set both Thread.CurrentPrincipal and HttpContext.Current.User to my custom principal. When I view either through the debugger the values are set with all the properties.
However once the request is complete and I try and request any other pages both Thread.CurrentPrincipal and HttpContext.Current.User are of type System.Security.Principal.GenericPrincipal and not my custom principal.
Do I need to do anything "extra" to get my custom principal out of the thread or HttpContext?
Thanks
The values in Thread.CurrentPrincipal and HttpContext.Current.User are not persisted between requests, they are rebuilt on each request. The best place for you to do this is probably in the Global.asax; write a function with the prototype:
void Application_PostAuthenticateRequest(object sender, EventArgs e)
That should get called after a user is authenticated on each request, which will allow you to set the principal how you would like.
Overridding Principal in:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
Instead of
protected void Application_AuthenticateRequest(object sender, EventArgs e)
In Global.asax.cs worked for me in an ASP web application
I would like to expand on the accepted answer slightly, hopefully I can save somebody a little bit of time.
In my case, the principal I used contained claims that were populated from the results of an external service, so I wanted to cache the results at login time.
I created a simple cache interface, IUserPrincipalCache, and registered it with the MVC DependencyResolver. At login, I build up the principal and add it to the cache. (Since your implementation may vary, I'll leave all that out.)
I then implemented this in Global.asax.cs:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
var cache = DependencyResolver.Current.GetService<IUserPrincipalCache>();
var claimsPrincipal = cache.FindUser(User.Identity.Name);
if (claimsPrincipal != null)
{
Context.User = claimsPrincipal;
Thread.CurrentPrincipal = claimsPrincipal;
}
}
}
I think it is important to point out the check for IsAuthenticated, since I could bypass the cache check in many cases. You also may not need to update Thread.CurrentPrincipal, I guess that depends on how you're using it.

How does IPrincipal gets its roles?

I need to get know how SiteMapProvider.IsAccessibleToUser() works.
Built-in XmlSiteMapProvider calls HttpContext.User.IsInRole() which uses System.Security.Principal.GenericPrincipal in case of forms authentication.
Where does the current user gets its roles? Which provider loads this kind of information? I want to overload it and use custom logic.
You do this by implementing a RoleProvider. Check out these links:
http://msdn.microsoft.com/en-us/library/8fw7xh74.aspx
http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx
To use custom logic, you can create your own forms authentication cookie with roles and read it back in Global.asax.
See these:
private void SetAuthenticationCookie(int employeeID, List<string> roles)
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
http://weblogs.asp.net/rajbk/archive/2010/04/01/securing-an-asp-net-mvc-2-application.aspx

Resources