6

詭異茶包 - 同網站下 WebForm 正常但 MVC 傳回 HTTP 503

 3 years ago
source link: https://blog.darkthread.net/blog/weird-http-503/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
同網站下 WebForm 正常但 MVC 傳回 HTTP 503-黑暗執行緒

今天遇上這幾年來數一數二有趣的詭異茶包,所幸憑藉老司機的經驗與直覺順利過關斬將,找出真相。

同事報案:某網站行為異常,起初頁面正常,執行到某幾個動作會卡住,久久沒反應。初步偵察,該環境僅有 IE 可用,且有 SSL 憑證未被信任的問題,即使一開始 IE 有忽略 SSL 憑證無效繼續的選項,但之後還是常陷入不尋常的無回應狀態。

決定放棄難以使喚的 IE,自己寫 PowerShell + C# 跑 WebClient.DownloadString() 拿第一手資訊,不要 IE 加進來攪局。但此時跳出兩隻小雜魚怪物攔路,分別是 SSL 憑證無效跟 TLS 1.0/1.1 停用。不打緊,這我有經驗:

加上 ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 及 ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; 之後,已能成功用 WebClient.DownloadString() 取得 ASP.NET 網站回應。觀察到一個現象,網站同時有 WebForm 及 MVC,.aspx 是好的,MVC 卻會傳回 HTTP 503,模擬如下:

如上圖所示,WebForm.aspx 是好的,兩個 MVC 動作則傳回 503,接著馬上試 WebForm.aspx 卻是好的。這裡有兩點很吊詭:

  1. 依我的理解,HTTP 503 多因 IIS AppPool 崩潰或停止造成,潰崩原因可能是伺服器太忙或網頁程式出現嚴重錯誤,被 IIS 也有保護機制判定連續出錯關閉。但是,如果真是 AppPool 崩壞,怎麼會有同一 AppPool 裡 WebForm.aspx 是好的,只有 MVC 吐回 503 的詭異結果。
  2. AppPool 崩潰時,Windows 事件檢視器裡多半要留下「伺服應用程式集區 'XXX' 的處理序與 World Wide Web Publishing 服務通訊時發生嚴重錯誤。」之類的訊息,但問題主機的事件檢視器很乾淨,沒有半則相關錯誤,這跟過往的經驗大不相同。

由於該功能很長一段時間沒用了,再想了幾種可能,主要集中在 MVC 與 WebForm 的差異上:.NET Framework 版本不對?某個 Windows Update 把 MVC 搞壞?但這些猜測很難解釋上述兩項疑點。

靈光一現,我有個大膽的想法...

會不會,這個 HTTP 503 回應並非源於 AppPool 崩壞,而是 MVC 程式自己捏造的?

請同事用 503 關鍵字搜尋程式碼卻無所獲,有點失望,心想猜錯了;隔了幾分鐘,不死心再請同事找 Unavailable 關鍵字,啊哈! BINGO!

找到 MVC 程式中有一段存取權限檢查,在檢核失敗時傳回 System.Net.HttpStatusCode.ServiceUnavailable!

附上我前面模擬 HTTP 503 的 MVC 程式當成示範:

using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Filters;

namespace DemoWeb.Controllers
{
    public class MvcActionsController : Controller
    {
        [MyAuthFilter]
        public ActionResult Index()
        {
            return Content("OK");
        }
        [MyAuthFilter]
        public ActionResult Query()
        {
            return Content("OK");
        }
    }

    public class MyAuthFilterAttribute : ActionFilterAttribute, IAuthenticationFilter
    {
        bool CheckAuthToken(HttpRequestBase request) => false;
        public void OnAuthentication(AuthenticationContext filterContext)
        {
            if (!CheckAuthToken(filterContext.HttpContext.Request))
            {
                filterContext.Result = new HttpStatusCodeResult(
                    System.Net.HttpStatusCode.ServiceUnavailable);
            }
        }

        public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
        {
            
        }
    }
}

存取被拒回傳 HTTP 503 這招的欺敵效果十足,說起來也具有防駭功用,只是當偵錯時遇上,就變成坑了,哈。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK