Asp.net mvc 如何检测表单中GET和POST ViewModel值之间的差异?

Asp.net mvc 如何检测表单中GET和POST ViewModel值之间的差异?,asp.net-mvc,hmac,Asp.net Mvc,Hmac,我有一个单独的视图来处理许多类型为VoyagesViewModel的模型,在该视图中,用户可以创建新的航程或编辑所有活动航程,因此我在该视图中有相同对象类型的不同实例。 每个表单包含以下内容: <% = Html.Hidden("Voyages["+ i +"].VoyageDetails[" + i2 + "].Id", location.Id)%> <% = Html.TextBox("Voyages[" + i + "].VoyageDetails[" + i2 + "]

我有一个单独的视图来处理许多类型为VoyagesViewModel的模型,在该视图中,用户可以创建新的航程或编辑所有活动航程,因此我在该视图中有相同对象类型的不同实例。 每个表单包含以下内容:

<% = 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)
因此,当用户发布表单时,我会检查是否对每次航行进行了如下更改:

 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
    }
再次序列化它,并将两个哈希值(隐藏的哈希值与最近计算的哈希值)进行比较,如果匹配,则表示没有任何更改

所有的工作都很好,我只是想知道,是否有其他的选择可以做到这一点

我的另一个关注点是序列化和所有HMAC处理这个由序列化生成的大字符串所需的时间,而不是仅仅将未更改的对象再次更新到DB所需的时间


< P>编辑:我不需要知道哪个字段发生了变化,只是如果发生了什么变化。

< P>如果服务器性能是一个问题,您可以考虑为每个记录创建一个“已更改”的字段,并且当一个用户可编辑字段值发生变化时,使用JavaScript设置它。 为了确保底层浏览器或NoScript边缘情况下的正确性,常见的模式是使用三态值,其中一个状态表示“未知”或“JS禁用”。例如,您将服务器上的字段初始化为0(或空),让JS代码在加载时将它们全部设置为1,然后在修改字段时将它们设置为2。如果您的服务器在POST上看到0/null/empty,那么它将返回到服务器端方法,在本例中是哈希比较。如果设置为1,则忽略该记录;如果设置为2,则自动触发更新

通过这种方式,您可以在99%的情况下避免计算哈希,并且仍然可以正确处理异常值

这并不一定能防止篡改,但您并没有太具体地说明要防止哪种篡改。防篡改最有效的方法实际上是首先将对象或信息保持在接触不到的位置,即在会话状态或加密cookie中


这有帮助吗?

与数据库命中相比,哈希计算会带来多少开销。假设这里有一个数据库。避免计算有价值吗?不知道实现的细节、站点的规模、数据库环境以及发出的查询类型。。。这完全不可能说。它可能值得优化,也可能不值得优化;这就是为什么您需要执行测试。至少把它放在一个for循环中,看看在出现明显延迟之前需要多少次迭代,然后对您的数据库执行同样的操作。我还没有做任何测试,但我会附加一个性能模块来跟踪时间和执行的总查询并检查它,查看我对原始问题的评论,看看哪些内容涉及更新。如果您只保存所有航程,会出现什么问题?如果从html中去掉计算和序列化,那么减少的负载是否会超过数据库额外点击的成本?保存未更改的内容会导致无效的数据库状态吗?一次航行涉及4个表,另外还有一个审计条目,告诉我们上次更新日期以及更新中更改了什么,因此如果我在一次未更改的航行中保存假阳性更新,这将意味着审计表的假日志条目(此审计逻辑是自动的)1.我明白。如果是那样的话,我会坚持吃杂烩。但是我不明白为什么需要序列化,所有的值都将被发回。
{"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}
 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
    }