Question

I've been tasked with the following by our client:

Make it so an authenticated user cannot modify another users information

Currently, the phone app sends our API the users username and password as so over HTTPS as a BASIC auth header: base64(username:password).

In the WebAPI I've created a BasicAuthenticationMessageHandler. In this handler I authenticate the user credentials against the customers LDAP.

This all works great.

I have a controller called Customer:

[Authorize]
public class CustomerController : BaseApiController<CustomerMapper>
{ ... }

I decorate it with the Authorize attribute as seen above.

I have a PUT method as so:

    public HttpResponseMessage Put(CustomerPutModel data)
    {
        if (ModelState.IsValid)
        {
            var c = customerService.GetByID(data.ID);

            if (c != null)
            {
                c = ModelMapper.Map<CustomerPutModel, Customer>(data, c);

                customerService.Update(c);

                return new HttpResponseMessage(HttpStatusCode.NoContent);
            }
        }
        throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
    }

Model:

public class CustomerPutModel
{
    public int ID{ get; set; }
    [Required]
    public string CustomerAccountID { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    [Required]
    public string City { get; set; }
    [Required]
    public string State { get; set; }
    public int Zipcode { get; set; }
}

This controller method works as expected.

Where I'm running into an issue is, how do I prevent the following scenario in my PUT method (and all other controllers/methods):

  1. User 1 has proper authentication credentials
  2. User 1 uses a proxy to snoop the request
  3. User 1 changes the request body's ID from their ID, to User 2's ID
  4. User 1 sends a PUT with their proper auth header, but in the body they pass another users ID

How do I prevent this at the method level? Since different parts of the app hit certain controller actions I assume I cannot protect all of them.

Whats the proper way to authorize that the user that was authenticated can actually do the action they requested? Does this need to be custom coded per method action??

Was it helpful?

Solution

You can change your Action method to something like:

public HttpResponseMessage Put(CustomerPutModel data)
{
    if (ModelState.IsValid)
    {
        var myID = userService.GetIDByUserName(HttpContext.Current.User.Identity.Name);
        if (myID != data.ID) 
        {
            ... wrong id, e.g. throw an exception or return a View indicating the error.
        }
        var c = customerService.GetByID(data.ID);

        if (c != null)
        {
            c = ModelMapper.Map<CustomerPutModel, Customer>(data, c);

            customerService.Update(c);

            return new HttpResponseMessage(HttpStatusCode.NoContent);
        }
    }
    throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
}

OTHER TIPS

I would recommend to create signatures to validate if the requests were not tampered with, that meaning that you use some one-way encryption algorithm based on the information the app is sending.

When you receive the request on the API, you just have to use the same algorithm to receive the information and see if the signatures match. If not, someone tampered the request and modified some information.

Just make some research on preventing tampering of requests.

Regarding making sure the user can only execute some methods, and not others, I would suggest you to look into claims based authorization, for example.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top