小技巧 - ASP.NET MVC 限定 POST 但開放本機 GET

分享 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); 
        } 
    } 
}
歡迎推文分享:
Published 06 July 2018 11:59 PM 由 Jeffrey
Filed under:
Views: 1,420



意見

# Tim said on 08 July, 2018 08:59 PM

很有幫助的技巧,謝啦~

你的看法呢?

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

5 + 3 =

搜尋

Go

<July 2018>
SunMonTueWedThuFriSat
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

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

文章典藏
其他功能

這個部落格


Syndication