views:

551

answers:

5

Any good strategies, code snippets etc for preventing URL manipulation?

For example I have this url; http://localhost/profile/edit/5 the id could easily be changed to anything and thus people could edit profiles they are not supposed too.

Here are a few ideas I thought of but they all have there drawbacks:

  1. Change my system to use GUID primary keys - makes it almost impossible to guess the keys - but people can still take the GUID from one part of app and use it in another url later.

  2. Use TempData to store the keys - prevents urls being sent around\used later.

  3. Preform checks in the controller before displaying page - means you have to do 'adminy' code everywhere to check operations.

Whats the best thing to do? One of these or something else?

+11  A: 

Number 3 is the correct thing to do. Server-Side Security Validation is always what you need, because this is the mechanism that you completely control and can rely on.

Number 1 is Security by Obscurity, and if someone accidentally posts his URL somewhere (like people often do with Session-IDs when they copy/paste links), your "Security" is broken.

Number 2 seems like a weak security - if you go through the hassle, better implement proper security. That also allows people to bookmark the page.

Michael Stum
Agreed. MVC comes with account creation pages by default, you may as well use them!
Mark Allen
You can make (3) much easier by implementing attributes that allow you to apply security as an aspect to different controllers/actions. See my response to this question for more info on how I do it.
tvanfosson
A: 

The url used to access your site is data from the client, and when security is concerned, you should always consider client provided data hostile.

Unfortunately there are no silver bullets to deal with this problem. You'll need to implement access restrictions throughout the application.

Internet Friend
+2  A: 

You shouldn't make your URLs "manipulation-proof" to protect underlying functionality. Besides: most websites make URLs more readable like http://stackoverflow.com/questions/741653/preventing-url-manipulation-attacks-with-mvc for instance - obfuscation would be a step backwards.

Rather check for permissions within your Controllers and raise an exception if the user is not allowed to edit profile 6. If you don't want to have the "checks" everywhere, maybe you could put them into an ActionFilter, or create some helper method like CurrentUser.FindProfileToEditById(profileId) (which throws an exception if the action is not allowed) instead of Profile.FindById(id).

If you want a generic service where you do not have a "current user", you might go with the GUID (so does Doodle for instance) - however this will always be a security threat in various ways (Facebook had this issue with their photo-albums).

Marcel J.
+1  A: 

I use custom authorization filters to implement role- and owner-based access control. The standard AuthorizationFilter will allow you to specify named roles or users that can have access to an action. I have extended this to allow you to specify that the current user may have access if they are the "owner" of the data. I have two additional filters, RoleOrOwnerAuthorizationFilter and RoleOrOwnerAssociatedAuthorizationFilter. The first checks that a configurable parameter (usually id) passed in the RouteData is the id of the current user in my users table or if the current user is in any of the listed roles. If so the check succeeds, if not, it returns an authorization error view.

The second allows me to specify a join table and the parameters to use to relate a parameter in the RouteData to a column in a join table and the current user to another column in the join table. If there is an entry matching both the parameter value and the user, I conclude that the user is related to the data and can have access. It also allows access if you are in a specified role. Between the three different attributes I have nearly all of my access control needs met, which means that I apply security simply by decorating with an appropriately configured attribute.

tvanfosson
+2  A: 

This is a good place to appy an ActionFilter. If the user parameter in the Url does not match the FormsAuthentication user you can take the appropriate action. Putting the logic in an ActionFilter allows to perform this check in other places in your application -- DRY.

Here is a sample you can tweak to your own needs that should give you an idea of what to do.

using System.Globalization;
using System.Web.Mvc;
using System.Web.Security;

public class RequiresAuthorizationAttribute : ActionFilterAttribute
{
    /// <summary>
    /// Checks user's authentication and authorization using FormsAuthentication
    /// and redirects failure to login page.
    /// </summary>
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;
        string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess);
        string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;

        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            string contextUserId = filterContext.RequestContext.HttpContext.User.Identity.Name;
            string urlUserId = filterContext.ActionParameters["id"].ToString();

            if (string.Compare(contextUserId, urlUserId, true, CultureInfo.InvariantCulture) != 0)
            {
                //if user is not authorized redirect to login page.
                filterContext.HttpContext.Response.Redirect(loginUrl, true); 
            }

        }
        else
        {
            //if user is not authenticated redirect to login page.
            filterContext.HttpContext.Response.Redirect(loginUrl, true); 
        }
    }
}

Then you would decorate your controller method with the RequiresAuthorizationAttribute like

[RequiresAuthorization]
    public ActionResult Edit()
    {
        // do editing.
    }
Ray Womack