2

So I've created a simple "hello world" in MVC as proof of concept. The index view list some made up records that are followed by the typical "edit", "details", and "delete" ActionLinks.

However, depending on which AD group owns the record, I don't render some of those options. So for example, say I'm pulling up 5 records, and I'm a member of an Group\Role that only owns 1 of them. My Index page would look something like...

Name Price
Foo1 $10.00 Details
Foo2 $20.00 Details
Foo3 $30.00 Details | Edit | Delete
Foo4 $40.00 Details
Foo5 $50.00 Details
Foo6 $60.00 Details

This all works great. The Problem is, that as a user I can just type in a URL of /Home/Edit/Foo1 and it gives me Edit access to a record I do not own.

On first search, it would seem like I am supposed to implement something called a ChildActionOnly Attribute. From what I've read, it sounds like if my controller was set to..

[ChildActionOnly]
public ActionResult Edit(string id)
{
    return View(GetItem(id));
}

then it would not allow me to change my url and get there. However the moment I add that Attribute, the Action Link no longer works either.

@Html.ActionLink("Edit", "Edit", new { id = item.FooName })

I know I'm just missing something. I'm already implementing Authentication so unless you belong to the correct Role, it already blocks ALL access to that controller. However, once you have access... it doesn't mean you have access to change anything and everything.

5 Answers 5

4

First Create a class for filter: We have to call this feature under OnActionExecuting of Action filter.

using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace Customer.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PreventFromUrl : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.UrlReferrer == null ||
 filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
           {
            filterContext.Result = new RedirectToRouteResult(new
                                      RouteValueDictionary(new { controller = "Dashboard", action = "Index", area = "" }));
           }
    }
}
}

Then add [PreventFromUrl] attribute to the top of your action or controller:

 [PreventFromUrl]
 public ActionResult Index()
 {

 }
Sign up to request clarification or add additional context in comments.

1 Comment

this code fail when there is urlrewrite acess is there
2

There's no way to prevent a user to attempting to access any URL. The client (browser) merely makes a request. It is the server's responsibility to determine whether it should return something or not.

Hiding a URL to prevent access is what's called "security by obscurity" and it's basically the same as having no security at all. There is nothing so obscure that a persistent user can't figure it out. You need real security as in role or object-level permissions tied to account authentication.

For a record, you're looking at object-level permissions. The most basic implementation involves creating a foreign key on the object to a user that represents "ownership" of that object. Then, when retrieving the object, you additionally check whether the currently logged-in user is the "owner" and if not, deny access.

public ActionResult Edit(int id)
{
    var foo = db.Foos.Where(m => m.Id == id && m.OwnerId == User.Identity.GetUserId());
    if (foo == null)
    {
        return new HttpNotFoundResult();
    }

    // do whatever for authorized user
}

1 Comment

Thanks all.. I did mess around with creating that custom actionresult filter and although it does work, I'm not totally a fan because the filter would have to hit the database to validate ownership, and then either I have to pass that object back to the ActionResult, or the ActionResult would have to re-pull that same record to pass to the view....... so I'll go with the idea like many of you suggested in that I'll pull the record, but before I return the "Edit" view, I'll make sure the user is the owner and if not, send them elsewhere.
0

According to documentation:

ChildActionOnlyAttribute Class

Represents an attribute that is used to indicate that an action method should be called only as a child action.

A child action method renders inline HTML markup for part of a view instead of rendering a whole view.

Any method that is marked with ChildActionOnlyAttribute can be called only with the Action or RenderAction HTML extension methods.

For more information about using attributes, see .

5 Comments

So is the ChildActionOnly the proper fix for my issue? It looks like to use it I would have to change all my views to be PartialViews. Seems pretty drastic to fix a URL issue. But I don't know.. that's why I'm asking.
You can create an action filter and check that the user has permission to access that action. You can then either register it per action, per class or as a global filter
Thanks.. I'll look into this. I'm new to MVC. It sounds like I can create my own filter and use it just like i'm trying to use [ChildActionOnly]. I guess I would just have to figure out how to do that and how to determine which record they are trying to mess with. (because don't the filters apply before the Id is set?
Yes you can create your own filter. You will then be able to inspect the request, the user and then find permission. If not, you can take a look at @ChrisPratt's answer which would also be viable.
As a cross cutting concern I would suggest the action filter route. but that is just my personal opinion.
0

Nkosi is right with his answer. To restrict access to this items you have to perform a user validation inside the Edit action. For example:

public ActionResult Edit(string id)
{
    var user = UserManager.FindById(User.Identity.GetUserId());
    //Check if user can access item
}

3 Comments

I was thinking this, but figured there was something auto-magic already in MVC. I just have to change the way I think. In Web Forms, if I didn't want you do do something I just didn't provide you a way to do it (e.g. Didn't provide you a button you could click). In MVC, I guess I also have to check for backdoor links :(
@da_jokker: That was never secure either. The only difference between MVC and Web Forms in this instance is that Web Forms issued a post via JavaScript on button click, whereas MVC just provides the link to request at will. However, the Web Forms way could just as easily be backwards-engineered. Simply hiding the button or whatever is not security.
@da_jokker You don't have to check for backdoor links. You just have to implement a content based security in your actions. It's your choice if you do it inside the action itself or inside an action filter. Like Chris said: It is not secure to just hide the links.
0

For a more flexible approach you can create a custom AuthorizeAttribute for your Controller action method and then set it like this :

[MyAuthorize]    
public ActionResult Edit(string id)
{
    var user = UserManager.FindById(User.Identity.GetUserId());
    //Check if user can access item
}

Then with an extended version of an 'AuthorizeAttribute` you could do your check there:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    // could be used to set the AD group that you are checking for
    // i.e. [MyAuthorize(strSecurityGroup="domain\group")] in the controller
    public string strSecurityGroup { get; set; }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }
        var rd = httpContext.Request.RequestContext.RouteData;
        var id = rd.Values["id"];
        var userName = httpContext.User.Identity.Name;
        //Access the current record through a DataHandler class
        DataHandler dh = new DataHandler();
        // Gets a field from the database that holds the username for the owner of the record
        string createdBy = dh.GetCreatedBy(int.Parse(id.ToString()));
        bool isAuthorized = false;
        if (userName.ToString() == createdBy) isAuthorized = true;
        var User = System.Web.HttpContext.Current.User;
        if (User.IsInRole(@"domain\group")) isAuthorized = true;
        return isAuthorized;
    }
}

This is how I like to do Authorization to AD. You can create a class property in the AuthroizeAttribute to allow you to set the user group separately for each use of the attribute as well. If you are using the authorization mechanism to control actions throughout the site I think that this is cleaner than manually performing an authorization check each time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.