分享 ASP.NET MVC 私房小技巧一則。

AJAX 呼叫 ASP.NET MVC 時,基於安全考量應限定 POST 方法。(參考:隱含殺機的GET式AJAX資料更新 - 黑暗執行緒)
不過在開放測試階段,開放 GET 可在瀏覽器網址列輸入 URL 測試較方便,有沒有兩全其美的方法?

於是我寫了一個 Action Attribute,實現「從 localhost 呼叫可用 GET,從正常 IP 存取只能 POST」的效果,像是這樣:

HomeController 的 Test Action(),瀏覽器透過 localhost 可讀取,改用實際 IP 則傳回 HTTP 404。應用方法很簡單,在 Action 加上 [LocalHttpGetOrHttpPost] 即可,基於安全考量,預設只開放 POST,必須在 appSetting 加入 <add key="EnableRestrictedGet" value="true"/> 才開放本機使用 GET :

[LocalHttpGetOrHttpPost] 
public ActionResult Test() 
{ 
    return Content("Test OK"); 
} 

為求彈性起見,再多支援透過 appSetting 指定開放 GET 存取的 IP 清單。例如:

[RestrictedHttpGetOrHttpPost("TestIps")] 
public ActionResult Test() 
{ 
    return Content("Test OK"); 
}

設定檔需加入對應的 IP 清單:

  <appSettings> 
    <add key="EnableRestrictedGet" value="true"/> 
    <add key="AllowGetIPs" value="::1,127.0.0.1,172.28.1.1"/> 
  </appSettings>

完整程式範例如下,有需要的同學請自取:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Web; 
using System.Web.Mvc;

namespace System.Web.Mvc 
{ 
    public class LocalHttpGetOrHttpPostAttribute : RestrictedHttpGetOrHttpPostAttribute 
    { 
        public LocalHttpGetOrHttpPostAttribute() : base("localhost") 
        { 
        } 
    }

    public class RestrictedHttpGetOrHttpPostAttribute : ActionMethodSelectorAttribute 
    { 
        //REF: https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Mvc/HttpGetAttribute.cs 
        private static readonly AcceptVerbsAttribute getCheck = new AcceptVerbsAttribute(HttpVerbs.Get); 
        private static readonly AcceptVerbsAttribute postCheck = new AcceptVerbsAttribute(HttpVerbs.Post);

        private static readonly bool EnableRestrictedGet = 
            System.Configuration.ConfigurationManager.AppSettings["EnableRestrictedGet"] == "true";

        private readonly string[] AllowedGetIPAddresses = "::1,127.0.0.1".Split(',');

        public RestrictedHttpGetOrHttpPostAttribute(string allowedIpSettingName) 
        { 
            if (allowedIpSettingName != "localhost") 
            { 
                var allowedIps = System.Configuration.ConfigurationManager.AppSettings[allowedIpSettingName]; 
                if (string.IsNullOrEmpty(allowedIps)) 
                    throw new ArgumentException($"appSetting '{allowedIpSettingName}' not found!"); 
                AllowedGetIPAddresses = allowedIps.Split(',', ';'); 
            } 
        } 
        public override bool IsValidForRequest(ControllerContext controllerContext, 
            MethodInfo methodInfo) 
        { 
            return 
                postCheck.IsValidForRequest(controllerContext, methodInfo) || 
                EnableRestrictedGet && 
                getCheck.IsValidForRequest(controllerContext, methodInfo) && 
                AllowedGetIPAddresses.Contains(controllerContext.HttpContext.Request.UserHostAddress); 
        } 
    } 
}

Comments

# by Tim

很有幫助的技巧,謝啦~

Post a comment


94 - 72 =