SelAromDotNet
SelAromDotNet

Reputation: 4815

ASP.NET MVC: Ensuring Item Edit posts the same Item ID

I've creating a set of CRUD pages for editing an item with MVC, but I'm confused about how I can ensure that the item a user edits is the same item that they post to update.

Currently I have a HiddenFor helper that stores the ItemID, which of course posts to the EditItem action, populating its model so I can update it...

But what happens if they go in and edit that hidden Id and change it to something else? I can certainly add an action filter to prevent them from editing an item they do not have access to, BUT what if they change the id to an item they DO have access to?

It's not a permission error but a data integrity error. I want to ensure that the item they retrieved from the GET for editing the item is the same one that they POST to update its content.

What is the correct way to do this?

Upvotes: 2

Views: 399

Answers (2)

dotnetstep
dotnetstep

Reputation: 17485

I may be too late to answer this question and I am also facing same problem and I avoided this problem following way.

  1. As you said you have ItemId and during post somebody change ItemId to different value so it can over write other item as this user has permission to edit that item as well. To avoid this problem I think that based on ItemId generate Id that is less predictable ( Like hash value of item id and this hash is based on key so that each page of your application has different key so that if itemid same then also it will generate different hash value).

  2. Now when form get post . First get ItemId and get its hash value based on key and compare with once that previously generated and if comparison fails somebody temper with either ItemId or hash value and we end up fail request.

Here is the small example.

public static class HtmlHelperExtentions
    {
        public static MvcHtmlString GenerateIntegrityToken(this HtmlHelper htmlHelper,
            string name , string valuefromwhichtokengenerate , string key)
        {
            var builder = new TagBuilder("input");            
            builder.MergeAttribute("type", "hidden");
            builder.MergeAttribute("name", "DataIntegrity_" + name);
            builder.MergeAttribute("value", DataIntegrityHelper.GetTokenValue(valuefromwhichtokengenerate,key));            
            return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
        }
    }

    public static class DataIntegrityHelper
    {
        public static bool IsValid(string value,string encryptedvalue,string key)
        {           
            string tempEncryptedValue = GetTokenValue(value, key);
            return string.Equals(tempEncryptedValue,encryptedvalue,StringComparison.InvariantCultureIgnoreCase);
        }

        public static string GetTokenValue(string value, string key)
        {
            if (string.IsNullOrEmpty(key))
                key = "MVCTokenForDataIntigrity4@5#6";
            else
                key = "1@2#3" + key;
            byte[] keys = System.Text.UTF8Encoding.UTF8.GetBytes(key);
            byte[] messageBytes = System.Text.UTF8Encoding.UTF8.GetBytes(value);
            HMAC mac = HMACSHA256.Create();
            mac.Key = keys;
            byte[] hashBytes = mac.ComputeHash(messageBytes);
            return ByteToString(hashBytes);
        }

        public static string ByteToString(byte[] buff)
        {
            string sbinary = "";

            for (int i = 0; i < buff.Length; i++)
            {
                sbinary += buff[i].ToString("X2"); // hex format
            }
            return (sbinary);
        }
    }

Example person Model.

public class Person
    {
        public int Id { get; set; }
        public string DataIntegrity_Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

In View (person.cshtml)

@using (Html.BeginForm())
{
    @Html.GenerateIntegrityToken("Id",Model.Id.ToString(),"Hello")
    @Html.HiddenFor(cc=>cc.Id)
    <br />    
    @Html.TextBoxFor(cc=>cc.FirstName)
    <br />
    @Html.TextBoxFor(cc => cc.LastName)
    <br />
    <input type="submit" />
}

Controller Actions

 public ActionResult Person()
        {
            return View(new Person());
        }

        [HttpPost]
        public ActionResult Person(Person person)
        {
            if (Extentions.DataIntegrityHelper.IsValid(person.Id.ToString(), person.DataIntegrity_Id, "Hello"))
            {
                return View(person);
            }
            else
            {
                return Content("Data integrity validation fails.");
            }
        }

Upvotes: 0

Arve Systad
Arve Systad

Reputation: 5479

If they have access to it, they are basically "hacking your application" to do something they already can do via a GUI (supposedly...). Basically, it would be the same thing as attempting to edit that other item. And if someone is modifying source code on the client side, weird things might indeed happen.

Of course, there should be server side access control and validation, so any invalid data is rejected. But if all they pass in is valid, why should they not be able to? There's not really much one can do. A request is a request - if it's valid, it's valid. No matter where it comes from.

What if I build a request in software like Fiddler? Then I can modify my data without even seeing your web application - not "editing" something in the first place at all.

Upvotes: 1

Related Questions