以JSON傳送大量物件引發ASP.NET MVC反序列化錯誤

某專案使用[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被調得愈大,網站被攻擊癱瘓的風險也愈高,修改設定宜謹慎拿捏。

歡迎推文分享:
Published 29 October 2015 10:16 PM 由 Jeffrey
Filed under: ,
Views: 5,627



意見

# kkman021 said on 29 October, 2015 10:42 AM

曾經也遇到這個問題,最後選擇在client端先把物件轉成json的字串,action接到字串後再反序列成物件。

就不用改設定了!但,不知道安全性如何。

# Jeffrey said on 29 October, 2015 08:32 PM

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

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<October 2015>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication