3

Please help with such a question and do not judge strictly because I'm a newbie in MVC: I've got a model for storing names of users by ID in my DB

public class Names
    {
public int NameId { get; set; }
public string Username { get; set; }
}

, a conrtoller

[HttpPost]
        public ActionResult EditforModel(Names Name)
        {
            if (ModelState.IsValid)
            {
                db.Entry(Name).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(Name);
        }

adding and editing view adding is working well, the question is about editing I use

    @using (Html.BeginForm())
    {
        @Html.ValidationSummary(true)
        <fieldset>
        <legend> legend </legend>
        @Html.EditorForModel()
        <p>
                <input type="submit" value="Save" />
            </p>
    </fieldset>
    }

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

to edit my model. when trying to go to this view I see an editor for both Id and Username, but if i fill Id - I've got error, because there is no Entry in DB with such Id. Ok.Let's look for attributes to hide an editor. [ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not. applaying it to my model I've got "0" id posting from my View.Try another attr. [ReadOnly(true)] makes a field a readonly-field. But at the same time I've got "0" in posting Id. Modifying a view I placed an editors for each field in model

@Html.HiddenFor(model => model.NameId)
@Html.EditorFor(model => model.Username)

but using it is dangerous because some user can post wrong Id throgh post-request.

I can't use [ScaffoldColumn(false)] with applying Id at [Httppost] action of the controller,by searching appropriate user-entry in DB, because the name was changed.. I can't believe @Html.HiddenFor is the only way out.But can't find one :(

versus
  • 95
  • 6
  • 1
    It's really not dangerous to post the id in the hidden input or action method parameter. What IS dangerous is to NOT verify the current user can actually modifiy the id that was passed. – dotjoe Dec 11 '13 at 21:02

1 Answers1

5

As you mentioned "[ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not", and [ReadOnly(true)] means that this property will be excluded by the default model binder when binding your model.

The problem is that the HTTP protocol is a stateless protocol, which means that when the user posts the edit form to the MVC Controller, this controller has no clue which object he was editing, unless you include some identifier to your object in the request received from the user, though including the real object Id isn't a good idea for the reason you mentioned (that someone could post another Id).

A possible solution might be sending a View Model with an encrypted Id to the View, and decrypting this Id in the controller.

A View Model for your object might look like this :

public class UserViewModel
{
    [HiddenInput(DisplayValue = false)]
    public string EncryptedId { get; set; }
    public string Username { get; set; }
}

So your HttpGet action method will be

    [HttpGet]
    public ActionResult EditforModel()
    {
        // fetching the real object "user"
        ...

        var userView = new UserViewModel
        {
            // passing the encrypted Id to the ViewModel object
            EncryptedId = new SimpleAES().EncryptToString(user.NameId.ToString()),
            Username = user.Username
        };

        // passing the ViewModel object to the View
        return View(userView);
    }

Don't forget to change the model for your View to be the ViewModel

@model UserViewModel

Now the HttpPost action method will be receiving a UserViewModel

    [HttpPost]
    public ActionResult EditforModel(UserViewModel Name)
    {
        if (ModelState.IsValid)
        {
            try
            {
                var strId = new SimpleAES().DecryptString(Name.EncryptedId);
                var id = int.Parse(strId);
                // select the real object using the decrypted Id
                var user = ...Single(p => p.NameId == id);
                // update the value from the ViewModel
                user.Username = Name.Username;
                db.Entry(user).State = EntityState.Modified;
            }
            catch (CryptographicException)
            {
                // handle the case where the encrypted key has been changed
                return View("Error");
            }

            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(Name);
    }

When the user tries to change the encrypted key, the decryption will fail throwing a CryptographicException where you can handle it in the catch block.

You can find the SimpleAES encryption class here (don't forget to fix the values of Key and Vector arrays): Simple insecure two-way "obfuscation" for C#

PS: This answer is based on the following answer by Henry Mori: Asp.net MVC 3 Encrypt Hidden Values

Community
  • 1
  • 1
Ahmad Ibrahim
  • 1,915
  • 2
  • 15
  • 32