OverrideAuthentication attribute not working? ASP.NET MVC 5 - asp.net-mvc

I have a super simple Authentication Attribute that I'm trying to implement in an ASP.NET MVC 5 application and I'm having some trouble. I want the attribute to be applied globally, except for specific actions within a controller (for example the login form and the home page).
I've tried decorating the action with the [OverrideAuthentication] attribute with no luck. It gives me a redirect loop error because the application is still running the authentication on the login form, and keeps trying to redirect back to the login form over and over.
Has anyone else seen this behaviour? Any idea what I've stuffed up here?
By way of example, I've created a super simple filter that is currently unimplemented:
public class BasicAuthenticationAttribute
: ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
throw new NotImplementedException();
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
throw new NotImplementedException();
}
}
If I decorate my controller/action like this:
[BasicAuthentication]
public class AccountController : Controller
{
[HttpGet]
[OverrideAuthentication]
public ActionResult Login()
{
return View();
}
}
I get a not implemented exception when I navigate to the Login action, even though that action shouldn't be running the authentication code at all. Have I misunderstood how overrides are supposed to work?

I think you have confused authentication with authorization (as many people do). It doesn't make sense to make a [BasicAuthenticationAttribute] and register it globally, because authentication only happens upon login, not on every request.
Authorization is what takes place after the user has logged in to check whether the user has the required privileges to do a specific action, and it makes sense to do authorization globally. Authorization in MVC is handled by the [AuthorizeAttribute] and you can inherit it if you need to customize the way the authorization check is done. You can also register it as a global filter.
The [AllowAnonymousAttribute] works in conjunction with [AuthorizeAttribute], and basically tells it to skip the authorization check. It should also be noted that the [AllowAnonymousAttribute] will have no effect unless it is used with the [AuthorizeAttribute].

Related

Prevent Url Tampering to access another users data

I just wanted to gauge opinions on how I should approach this problem and ultimately looking for a quick win (wrong way to think about things nut time pressures mean I have to think and act quickly!
I've been given a website that has a bit of an issue.
I login using standard forms authentication as User1234 and my url is as follows:
www.mywebsite.co.uk/1234/Contact.
This will take me to User1234's details.
You can put two and two together and correctly assume that 1234 is a user id of some sort.
Once authenticated, I can access the views with [Authorize] attribute present, any anonymous/unathenticated users get redirected.
However, once logged in as User1234, I can then tinker with the url like so:
www.mywebsite.co.uk/1235/Contact.
So I am authenticated as User1234 but can see User1235's data. This is BAD for obvious reasons.
When I log in, I actively set the login ID in session so in theory, I could do a check whenever a user hits an ActionResult, I could cross check the ID present in the URL against the session login ID. However, it is a rather project with lots of action results and as such, I'm reluctant to spend my Saturday afternoon adding something to each and every ActionResult.
Is there an event in the global.asax I could use that is hit on each ActionResult request where I can compare Session login ID with url ID?
Alternatively, can anyone offer some suggestions about how I can achieve this or restrict URL tampering?
You can try and do a base controller
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Do your stuff here
base.OnActionExecuted(filterContext);
}
}
I assume that you don't want to change your URL routes, as you could retrieve the user id also from the session. A quick solution would be to use an ActionFilter which you can place on the affected controllers or action methods:
public class VerifyUserIdAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var sessionUserId = filterContext.HttpContext.Session["UserId"];
var routeUserId = filterContext.RouteData.Values["UserId"];
if (routeUserId != null && sessionUserId == routeUserId)
filterContext.Result = new RedirectResult("<<url to redirect to>>");
}
}
I don't understand why the URL contains a data entry point. This appears to be a design flaw. I would remove all code that uses a URL parameter and instead make sure the controller looks up what the ID is based on the logged in user.

What is Webform's "UrlAuthorizationModule.CheckUrlAccessForPrincipal" equivalent for MVC?

I got a problem as i am writing a custom SSO solution for my company. To mkae it simple, i've made a custom authentication httpmodule that intercepts all requests so as to check user authentication state. If not authenticated, user is redirected to my custom sso login page.
The thing is, when user is not authenticated, i'd like to check if he can access the requested page/resource... With Webforms, no problem, i add an authorization block in web.config, and i use UrlAuthorizationModule.CheckUrlAccessForPrincipal with an anonymous user. Everything works fine...
But when i apply my module to an MVC (3) web site, this does not work anymore (for obvious reasons, like the possibility to access the same controller and/or action from differents urls when using routing, and because authorizations are made through controller attributes).
How can I achieve this ?? I've been searching all day long, didn't find anything about that :/
ASP.NET MVC 3 Internet Application template includes a basic AccountController which implements the following actions (along with the associated models and views):
LogOn
Register
ChangePassword / ChangePasswordSuccess
You simply need the [Authorize] attribute on the Actions or classes you wish to secure. But if you need something really custom you can do something like I've done.
I created a custom class to override security in my application.
public class AuthorizeActivityAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
HttpContext currentContext = HttpContext.Current;
//Do your custom authentication stuff here and return true or false depending on results
Return true;
}
}
And now in my Controller I have the following:
[AuthorizeActivity]
public ActionResult Index()
{
ViewBag.Message = "Welcome";
return View();
}
I had the same problem.
See solution here: MVC equivalent of Webforms "UrlAuthorizationModule.CheckUrlAccessForPrincipal"
You would have to read the information from the other controller. This
can be done by instantiating its context and the Descriptor, then
instantiating the AuthorizationContext for that controller and read
the filter info.

Designing MVC Crud with Modal problem (Not allow direct acess to Create action)

I´m have a Car View with a list of car... So, I have a Create button that opens a Modal (UI JQuery Dialog) with Site/Car/Create content...
All works fine... But I´d like to block direct access to : Site/Car/Create...
Is that possible? How?
Thanks
It's not really possible to block it completely, but you can do some things to make it more difficult. First, require that it come from a POST request. That will prevent someone from simply entering the URL with request parameters. Second, use the antiforgery token helper. That will help prevent a third-party from doing a POST to the url since they will also require both the token input and the token cookie. Third, you could potentially check if the request has the X-HTTP-REQUESTED-WITH header and only do the POST (or GET) via AJAX. It's not that hard to get around but it would prevent an accidental access if you do use GET. Fourth, and it probably should have been first, make sure that only authorized users have access to the action using the AuthorizeAttribute. Fifth, use SSL to prevent unauthorized access using FireSheep and protect your cookies and data from snooping.
In short, you won't be able to prevent a determined person with legitimate authorization from crafting a request to the action if they a really want to without using your interface. They can always craft a request that will look exactly like the one you would send. You can make it more difficult and prevent accidental access, though, using the above methods.
[AttributeUsage(AttributeTargets.Method)]
public class AjaxOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 404;
filterContext.Result = new HttpNotFoundResult();
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
And then stick on your Create action like this:
[AjaxOnly]
public ActionResult Create()
{
...etc
}

Logging custom actions in asp.net mvc using built-in forms authentication

I am using the built-in forms authentication that comes with asp.net mvc. I added all the necessary tables to Sql Server using aspnet_regsql wizard and I have it all integrated and working perfect. Now I can add users, log in, perform control Authorization and all these things.
The next step is to add some basic logs when a user performs an action. For example, I want to log who has deleted an user from the database using the UserAdministration, or when someone has visited a view with restricted information (these are just examples).
I've seen that with the authentication comes a table called aspnetWebEventEvents but seems that it is intended only to store exceptions occurred on the website and it is configured with web.config and it's all performed automatically. I would like just to call a class to log custom actions without having to fill manually all the fields:
Logger.Log("Action performed");
And store automatically the user, time and all these fields.
I'm sure there has to be an already implemented class to do this stuff and I don't want to rewrite something that it is already done.
I tried using SqlWebEventProvider, that seems to be the one that is called internally but this class seems to be internal so it is not intended to be used.
Maybe I'm wrong, this is not intended to log custom actions and I should better create my own table with custom actions...
I dont know if you can accomplish that with aspnetWebEventEvents, it sound like you want to log specific situations which i don't think is the intention of this buld in feature.
Have you tried ActionFilters?, you can do really specific logging with this, for example you can check the Result Type to see what happened when the user try to use that action, and you have access to the RouteData Values through the ActionExecutedContext:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
//Log - Exception Cases
}
else if(filterContext.Result.GetType() == typeof(RedirectToRouteResult))
{
//Log - Redirect to other Action
}
else
{
//Log the normal behavior
}
}
}
And then you just Decorate the actions you want to log with this attribute:
[LogActionFilter]
public ActionResult Edit(int id)
{
}

How do you implement a "Logout" link using ASP.NET MVC?

This seems like a pretty stupid question, but I'm trying to figure out the best way to do this. Would you simply redirect to a /Logout page and have the controller call the FormsAuthentication.SignOut function?
That was my first thought, but then I wondered if it could be abused by third party websites. Let's say someone just decides to post a link to your /Logout page. The user would get signed out of your application. Is there a good way to prevent that?
If you are concerned about a user getting accidentally logged out of you application through the use of a malicious link, you can check the Referrer to make sure that the logout is coming from your site (or is NULL in the case where the user simply types the URL in).
I actually don't worry about this since logging someone out is annoying but not necessarily a security risk.
Such a malicious link would be an example of a class of security vulnerabilities known as cross site request forgery, CSRF. A logout link is relatively harmless, but a remote site could set up a number of hidden forms and post them to your site to perform any action possible through POST.
The most common counter-measure is to include a challenge, a random hidden value in each form, and then check for that value. Checking the referer header could work, but note that some browsers don't send referer at all.
Read more: http://en.wikipedia.org/wiki/Cross-site_request_forgery
This is an old question, but here is a modern example with MVC:
[Authorize]
public RedirectResult Logout()
{
FormsAuthentication.SignOut();
return this.Redirect("/");
}
You can ensure that the Logout action is only able to be called by somebody who is logged in by applying the Authorize attribute to it.
This is what I use.
public ActionResult Logout()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
Seems to work fine.
Third party websites are only going to log themselves out. So they wouldn't be achieving anything different from actually clicking Logout.
The new ASP.net MVC Beta contains an AccountController, which may be worth looking at, as it essentially implements everything from Registration to Login/Logout to Forgot Password functionality. Not sure how good it is, but a good starting Point for sure.
Derive from ActionResult
public class LogoutResult : ActionResult
{
private readonly IAuthenticationService _authenticationService;
private readonly IWebContext _context;
public LogoutResult(IAuthenticationService authenticationService, IWebContext context)
{
_authenticationService = authenticationService;
_context = context;
}
public override void ExecuteResult(ControllerContext context)
{
_authenticationService.Logout();
_context.Abandon();
_context.Redirect("~/");
}
}
You should look for a cookie or something that identifies the client as the true user.

Resources