某專案使用[FromPartialBody]在ASP.NET MVC Action接收jQuery送來的物件陣列,初測無誤後進行正式測試,發現只要物件陣列的筆數一多,網頁就會爆炸:

System.InvalidOperationException: The JSON request was too large to be deserialized.
   於 System.Web.Mvc.JsonValueProviderFactory.EntryLimitedDictionary.Add(String key, Object value)
   於 System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
   於 System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
   於 System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
   於 System.Web.Mvc.JsonValueProviderFactory.AddToBackingStore(EntryLimitedDictionary backingStore, String prefix, Object value)
   於 System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
   於 System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
   於 System.Web.Mvc.ControllerBase.get_ValueProvider()
   於 System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
   於 System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
   於 System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState)

爬文很快找到解決方案,web.config裡有個aspnet:MaxJsonDeserializerMembers參數,預設值為1000,超過上限就會出現前述錯誤。留下一個疑點,測試資料筆數不可能超過400筆,MaxJsonDeserializerMembers上限1000的單位為何?

用F12開發者工具檢查Request內容,果然資料只有366筆。

依MSDN文件的MaxJsonDeserializerMembers說明:Specifies the limit of the maximum number of items that can be present in any dictionary deserialized by the JavaScriptSerializer type. 推測每組 PropName: "PropValue" 就算一個Item。換言之,data陣列有366個元素,每個各有CostCenter、JobType、UserId、Weight四個屬性,366x4 + 2(approve及wuid)=1466,突破1000關卡。為證實猜想,刻意將aspnet:MaxJsonDeserializerMembers調為1465以及1466,果真得到1465失敗,1466成功的結果,驗證前面的Member個數定義正確。有了這個基礎,我們就能精準估算MaxJsonDeserializerMembers該設多大。

咦?為什麼不設一個宇宙無敵大的上限就好,要傷這種腦筋?依老骨頭的直覺,預設值偏低必有玄機,恣意加大絕非上策。微軟的這篇KB說明始末:MaxJsonDeserializerMembers是在MS11-100安全更新時刻意調小的,目的在防止有人藉由傳送超大JSON癱瘓ASP.NET網站達到DoS攻擊的目的。也許是說,MaxJsonDeserializerMembers被調得愈大,網站被攻擊癱瘓的風險也愈高,修改設定宜謹慎拿捏。


Comments

# by kkman021

曾經也遇到這個問題,最後選擇在client端先把物件轉成json的字串,action接到字串後再反序列成物件。 就不用改設定了!但,不知道安全性如何。

# by Jeffrey

to kkman021, 利用巨型JSON展開DoS攻擊的原理在於ASP.NET還沒識別敵我之前就耗費大量資源解析JSON,轉成字串後有個好處,可以執行反序列化之前先判斷是正常請求還是攻擊,若發現是DoS攻擊就直接拒絕不要花時間處理,如此就能降低風險。

Post a comment