views:

57

answers:

1

I have a single view that handles a lot of Models of type VoyagesViewModel, in that view the user can create a new voyage or edit all the active voyages, so i have different instances of the same object type in that view. each form contains something like:

<% = Html.Hidden("Voyages["+ i +"].VoyageDetails[" + i2 + "].Id", location.Id)%>
<% = Html.TextBox("Voyages[" + i + "].VoyageDetails[" + i2 + "].ArrivalDate", location.ArrivalDate, new { @class = "dates" })%><% = Html.ValidationMessage("Voyages[" + i + "].VoyageDetails[" + i2 + "].ArrivalDate", "*")%>
<% = Html.TextBox("Voyages[" + i + "].VoyageDetails[" + i2 + "].DepartureDate", location.DepartureDate, new { @class = "dates" })%><% = Html.ValidationMessage("Voyages[" + i + "].VoyageDetails[" + i2 + "].DepartureDate", "*")%>
<% = Html.Hidden("Voyages[" + i + "].VoyageDetails[" + i2 + "].LocationID", location.LocationID)%>
<% = Html.Hidden("Voyages[" + i + "].VoyageDetails[" + i2 + "].LocationName", location.LocationName)%>
<% = Html.Hidden("Voyages[" + i + "].VoyageDetails[" + i2 + "].VesselName", location.VesselName)%>
<% = Html.Hidden("Voyages[" + i + "].VoyageDetails[" + i2 + "].VesselID", location.VesselID)%>
<% = Html.CheckBox("Voyages[" + i + "].VoyageDetails[" + i2 + "].remove", location.remove)%> (remove)

I have 8 to 10 of this in that View, when the user POST the form(I have all this fields in a single form), i want to be able to detect if the user change something in a voyage to save it, so i don't have to save that specific voyage if no changes where made.

To check this i have a hidden field and i do this:

 <% = Html.Hidden("Voyages[" + i + "].hash", TamperProofing.GetExpiringHMAC(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model), DateTime.Now.AddMinutes(15)))%>

Im using HMAC to get a hashed version of the hole object, but first i serialize the object into a string format (JSON Format):

{"Id":22,"VesselName":"CAPTAIN P (CPP)","VesselID":8,"VoyageDetails":[{"Id":58,"ArrivalDate":"\/Date(1259298000000)\/","DepartureDate":"\/Date(1259384400000)\/","LocationID":404,"LocationHash":null,"LocationName":"Balboa, Panama (PABLB)","VesselName":"CAPTAIN P (CPP)","VesselID":8,"Order":0,"Comment":null,"remove":false},{"Id":60,"ArrivalDate":"\/Date(1260248400000)\/","DepartureDate":"\/Date(1260334800000)\/","LocationID":406,"LocationHash":null,"LocationName":"Colon Free Zone, Panama (PACFZ)","VesselName":"CAPTAIN P (CPP)","VesselID":8,"Order":0,"Comment":null,"remove":false},{"Id":61,"ArrivalDate":"\/Date(1260421200000)\/","DepartureDate":"\/Date(1260507600000)\/","LocationID":407,"LocationHash":null,"LocationName":"Cristobal, Panama (PACTB)","VesselName":"CAPTAIN P (CPP)","VesselID":8,"Order":0,"Comment":null,"remove":false},{"Id":62,"ArrivalDate":null,"DepartureDate":null,"LocationID":408,"LocationHash":null,"LocationName":"Manzanillo, Panama (PAMAN)","VesselName":"CAPTAIN P (CPP)","VesselID":8,"Order":0,"Comment":null,"remove":false},{"Id":59,"ArrivalDate":null,"DepartureDate":null,"LocationID":405,"LocationHash":null,"LocationName":"Coco Solo, Panama (PACSO)","VesselName":"CAPTAIN P (CPP)","VesselID":8,"Order":0,"Comment":null,"remove":true}],"newVoyageDetail":null,"isComplete":false,"Code":"A Code","position":0,"hash":null}

So when the user POST the form i check if changes where made to each voyage like this:

 var obj = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(_voyage);
     if (TamperProofing.VerifyChanges(obj, hash) == TamperProofing.HMACChanged.True)
    {
       //UPDATE
    }else
    {
       //DO Nothing, is the exact same object
    }

Serialize it again and compare both hashes, the one on the hidden against the recently calculated one, if it match it means nothing changed.

All works pretty well, i was just wondering, if there are any other options to do this?

And my other concern is the time that can take the serialization and all the HMAC thing with this large string generated by the Serialization against the time it could take just to Update unchanged object again to the DB.

EDIT: I don't need to know which field changed, just if something changed.

+1  A: 

If server performance is a concern, you might consider creating a "changed" field for each record and using Javascript to set it when one of the user-editable field values changes.

To ensure correctness in downlevel browsers or NoScript fringe cases, a common pattern is to use a tri-state value, with one state meaning "unknown" or "JS disabled". For example, you initialize the fields on the server to 0 (or empty), have your JS code set them all to 1 on load, and then to 2 when the field is modified. If your server sees a 0/null/empty on POST, then it falls back on the server-side method, in this case your hash-compare. If it's set to 1, then it ignores that record, and if it's set to 2, then it automatically triggers an update.

This way, you could avoid computing hashes in 99% of cases, and still correctly handle the outliers.

This doesn't necessarily prevent tampering but you weren't too specific about what kind of tampering you're trying to prevent. The most effective means of tamper-proofing is really to keep the object or information out of reach in the first place, i.e. in the session state or an encrypted cookie.

Does that help at all?

Aaronaught
How much overhead does the hash calculation carry compared to the database hit. Assuming there is a database here. Is it valuable to avoid the calculation?
Thomas Eyde
Without knowing the details of your implementation, the scale of your site, the database environment and what type of query you're issuing... it's completely impossible to say. It may or may not be worth optimizing; that's why you need to perf-test. At least stick it in a for-loop and see how many iterations it takes before there's a noticeable delay, then do the same thing against your database.
Aaronaught
I have not yet do any test, but i ll attach a performance module to track the time and the total query's executed and check it out, check my comment on the original question to see what involve an update.
Omar