2

Right now I have the following website structure:

Front end: website\user1, website\user2

Back end: website\account\user1, website\account\user2

Where website\account controller has generic [Authorize] attribute applied. Which is not enough since any authorized user can access other user's backed functionality simply by going to website\account\ url (if he knows his name).

What is the best way to resolve this issue?

I have two approaches so far:

  1. Create custom Authorize attribute, inspect controller context, extract user information from there and compare it against current authorized user in ASP.NET:

     var currerntUserId = (long)System.Web.Security.Membership.GetUser().ProviderUserKey;
     return ExtractCurrentUserId(filterContext) == currerntUserId;
    
  2. Remove part from back-end URLs and have all users access \account. Current user information will be provided by ASP.NET framework.
1
  • 2
    Why not have website\my-account on the backend, and just query the database based on the current Controller.User.Identity.Name? Commented Jan 27, 2014 at 19:33

3 Answers 3

2

How about this?

Route /website/my-account

[Authorize]
public class MyAccountController : Controller
{
    public ActionResult Index()
    {
        var userData = System.Web.Security.Membership.GetUser();
        // note you could also get this from db using this.User.Identity.Name
        return View(userData);
    }
}

It is much easier to control the authorization this way because we are not passing the userid to the action method via a route parameter. The only way someone can get to the backend for a particular user account is by being logged in as that user.

Reply to comments:

To answer your questions in comments about what is easier / harder / better / what my preference is, I am going to go ahead and make my final answer "It depends."

It depends on the sensitivity of the data, what things admins can do that users aren't allowed to (or vice versa), how many controller actions needed to be secured, how similar the views are for public / account / admin perspectives on the data, etc. Pretty much everything stated in your question and all the answers here are valid approaches. You can certainly do it with an ActionFilter and keep the user URL's, or you could do it directly in the action method (if there aren't a lot of them), change your URL schema, implement impersonation (or not), etc.

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

5 Comments

After spending so much time trying to secure username specific urls, I'm leaning to this approach and let ASP.NET handle authorization stuff for me.
So you need a username-specific URL for the self-management page? Is this so that admins can also edit user data, even when they are not signed in as the user?
I don't really need username-specific URL. I was just thinking it would be nice to have easy access to users data for admins (like you already said). Will be easier/better to have generic back-end url + ASP.NET Membership where I will have to create custom back-end for admins where they can view/edit users data ---- or ---- user-specific URLs with custom authorization code, but easy access for admins?
Another option for giving admins access would be to force them to sign on as the user they want to edit -- a.k.a. impersonation. You can do this by giving the admins a page that lets them select a user, get that user's auth cookie written to their browser, and then redirects to the user's account page. Problem with this is there is a lot of context switching (impersonate / revert back to admin account) if there are multiple users to edit.
That is interesting about impersonation. I will check it out. So, you would personally go that way instead of messing with custom authorization? Custom authorization makes me worry about security.
0

When you are retrieving a user's data from your datastore (most likely a database), you should only retrieve data for the username of the authenticated user. In your controller, this will give you the username of the currently authenticated user:

User.Identity.Name

So you could do something like:

return ExtractCurrentUserId(filterContext) == User.Identity.Name;

3 Comments

Is MembershipUser.ProviderUserKey the same as User.Identity.Name? It looks like the OP's is a long, but IIdentity.Name is a string.
No. one will return the (usually) GUID for the user. The other will return the username. I've seen the User.Identity.Name be preferred for authorization.
I use custom membership provider, so Identity.Name and ProviderUserKey are coming from the same entity any way.
0

If you use Role based authentication with SimpleMembership you can do something like this and give users roles that should be able to access certain controller actions:

public class MyAccountController : Controller
{
    [Authorize(Roles = "Admin")]
    public ActionResult User1()
    {
       // do user1 work
    }

    [Authorize]
    public ActionResult User2()
    {
       // do user2 work
    }
}

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.